Skip to content

Commit 60a58ca

Browse files
temyurchenkolravenclaw
authored andcommitted
[clang][AST] fix ast-print of extern <lang> with >=2 declarators
Fixes llvm#93913
1 parent 0fb1a0f commit 60a58ca

File tree

7 files changed

+98
-24
lines changed

7 files changed

+98
-24
lines changed

clang/lib/AST/Decl.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -576,13 +576,16 @@ template <typename T> static bool isFirstInExternCContext(T *D) {
576576
return First->isInExternCContext();
577577
}
578578

579-
static bool isSingleLineLanguageLinkage(const Decl &D) {
580-
if (const auto *SD = dyn_cast<LinkageSpecDecl>(D.getDeclContext()))
581-
if (!SD->hasBraces())
582-
return true;
579+
static bool isUnbracedLanguageLinkage(const DeclContext *DC) {
580+
if (const auto *SD = dyn_cast_if_present<LinkageSpecDecl>(DC))
581+
return !SD->hasBraces();
583582
return false;
584583
}
585584

585+
static bool hasUnbracedLanguageLinkage(const Decl &D) {
586+
return isUnbracedLanguageLinkage(D.getDeclContext());
587+
}
588+
586589
static bool isDeclaredInModuleInterfaceOrPartition(const NamedDecl *D) {
587590
if (auto *M = D->getOwningModule())
588591
return M->isInterfaceOrPartition();
@@ -644,7 +647,7 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
644647

645648
if (Var->getStorageClass() != SC_Extern &&
646649
Var->getStorageClass() != SC_PrivateExtern &&
647-
!isSingleLineLanguageLinkage(*Var))
650+
!hasUnbracedLanguageLinkage(*Var))
648651
return LinkageInfo::internal();
649652
}
650653

@@ -2118,6 +2121,12 @@ VarDecl::VarDecl(Kind DK, ASTContext &C, DeclContext *DC,
21182121
"ParmVarDeclBitfields too large!");
21192122
static_assert(sizeof(NonParmVarDeclBitfields) <= sizeof(unsigned),
21202123
"NonParmVarDeclBitfields too large!");
2124+
2125+
// The unbraced `extern "C"` invariant is that the storage class
2126+
// specifier is omitted in the source code, i.e. SC_None (but is,
2127+
// implicitly, `extern`).
2128+
assert(!isUnbracedLanguageLinkage(DC) || SC == SC_None);
2129+
21212130
AllBits = 0;
21222131
VarDeclBits.SClass = SC;
21232132
// Everything else is implicitly initialized to false.
@@ -2301,7 +2310,7 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
23012310
// A declaration directly contained in a linkage-specification is treated
23022311
// as if it contains the extern specifier for the purpose of determining
23032312
// the linkage of the declared name and whether it is a definition.
2304-
if (isSingleLineLanguageLinkage(*this))
2313+
if (hasUnbracedLanguageLinkage(*this))
23052314
return DeclarationOnly;
23062315

23072316
// C99 6.9.2p2:
@@ -3027,6 +3036,12 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
30273036
DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0),
30283037
EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {
30293038
assert(T.isNull() || T->isFunctionType());
3039+
3040+
// The unbraced `extern "C"` invariant is that the storage class
3041+
// specifier is omitted in the source code, i.e. SC_None (but is,
3042+
// implicitly, `extern`).
3043+
assert(!isUnbracedLanguageLinkage(DC) || S == SC_None);
3044+
30303045
FunctionDeclBits.SClass = S;
30313046
FunctionDeclBits.IsInline = isInlineSpecified;
30323047
FunctionDeclBits.IsInlineSpecified = isInlineSpecified;

clang/lib/AST/DeclPrinter.cpp

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ static void printExplicitSpecifier(ExplicitSpecifier ES, llvm::raw_ostream &Out,
633633
Out << Proto;
634634
}
635635

636-
static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
636+
static void maybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
637637
QualType T,
638638
llvm::raw_ostream &Out) {
639639
StringRef prefix = T->isClassType() ? "class "
@@ -643,6 +643,22 @@ static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
643643
Out << prefix;
644644
}
645645

646+
/// Return the language of the linkage spec of `D`, if applicable.
647+
///
648+
/// \Return - "C" if `D` has been declared with unbraced `extern "C"`
649+
/// - "C++" if `D` has been declared with unbraced `extern "C++"`
650+
/// - nullptr in any other case
651+
static const char *tryGetUnbracedLinkageLanguage(const Decl *D) {
652+
const auto *SD = dyn_cast<LinkageSpecDecl>(D->getDeclContext());
653+
if (!SD || SD->hasBraces())
654+
return nullptr;
655+
if (SD->getLanguage() == LinkageSpecLanguageIDs::C)
656+
return "C";
657+
assert(SD->getLanguage() == LinkageSpecLanguageIDs::CXX &&
658+
"unknown language in linkage specification");
659+
return "C++";
660+
}
661+
646662
void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
647663
if (!D->getDescribedFunctionTemplate() &&
648664
!D->isFunctionTemplateSpecialization()) {
@@ -662,6 +678,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
662678
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
663679
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
664680
if (!Policy.SuppressSpecifiers) {
681+
if (const char *Lang = tryGetUnbracedLinkageLanguage(D)) {
682+
// the "extern" specifier is implicit
683+
assert(D->getStorageClass() == SC_None);
684+
Out << "extern \"" << Lang << "\" ";
685+
}
665686
switch (D->getStorageClass()) {
666687
case SC_None: break;
667688
case SC_Extern: Out << "extern "; break;
@@ -807,7 +828,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
807828
}
808829
if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
809830
!Policy.SuppressUnwrittenScope)
810-
MaybePrintTagKeywordIfSupressingScopes(Policy, AFT->getReturnType(),
831+
maybePrintTagKeywordIfSupressingScopes(Policy, AFT->getReturnType(),
811832
Out);
812833
AFT->getReturnType().print(Out, Policy, Proto);
813834
Proto.clear();
@@ -932,6 +953,11 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
932953
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
933954

934955
if (!Policy.SuppressSpecifiers) {
956+
if (const char *Lang = tryGetUnbracedLinkageLanguage(D)) {
957+
// the "extern" specifier is implicit
958+
assert(D->getStorageClass() == SC_None);
959+
Out << "extern \"" << Lang << "\" ";
960+
}
935961
StorageClass SC = D->getStorageClass();
936962
if (SC != SC_None)
937963
Out << VarDecl::getStorageClassSpecifierString(SC) << " ";
@@ -961,7 +987,7 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
961987

962988
if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
963989
!Policy.SuppressUnwrittenScope)
964-
MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
990+
maybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
965991

966992
printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
967993
D->getIdentifier())
@@ -1064,6 +1090,8 @@ void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) {
10641090

10651091
void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) {
10661092
prettyPrintAttributes(D);
1093+
if (const char *Lang = tryGetUnbracedLinkageLanguage(D))
1094+
Out << "extern \"" << Lang << "\";";
10671095
}
10681096

10691097
void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
@@ -1136,22 +1164,21 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
11361164
}
11371165

11381166
void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
1139-
const char *l;
1167+
if (!D->hasBraces()) {
1168+
VisitDeclContext(D);
1169+
return;
1170+
}
1171+
const char *L;
11401172
if (D->getLanguage() == LinkageSpecLanguageIDs::C)
1141-
l = "C";
1173+
L = "C";
11421174
else {
11431175
assert(D->getLanguage() == LinkageSpecLanguageIDs::CXX &&
11441176
"unknown language in linkage specification");
1145-
l = "C++";
1177+
L = "C++";
11461178
}
1147-
1148-
Out << "extern \"" << l << "\" ";
1149-
if (D->hasBraces()) {
1150-
Out << "{\n";
1151-
VisitDeclContext(D);
1152-
Indent() << "}";
1153-
} else
1154-
Visit(*D->decls_begin());
1179+
Out << "extern \"" << L << "\" {\n";
1180+
VisitDeclContext(D);
1181+
Indent() << "}";
11551182
}
11561183

11571184
void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,

clang/lib/Sema/SemaDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2379,7 +2379,7 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type,
23792379
}
23802380

23812381
FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type,
2382-
/*TInfo=*/nullptr, SC_Extern,
2382+
/*TInfo=*/nullptr, SC_None,
23832383
getCurFPFeatures().isFPConstrained(),
23842384
false, Type->isFunctionProtoType());
23852385
New->setImplicit();

clang/lib/Sema/SemaExpr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6288,7 +6288,7 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context,
62886288
FunctionDecl *OverloadDecl = FunctionDecl::Create(
62896289
Context, Parent, FDecl->getLocation(), FDecl->getLocation(),
62906290
FDecl->getIdentifier(), OverloadTy,
6291-
/*TInfo=*/nullptr, SC_Extern, Sema->getCurFPFeatures().isFPConstrained(),
6291+
/*TInfo=*/nullptr, SC_None, Sema->getCurFPFeatures().isFPConstrained(),
62926292
false,
62936293
/*hasPrototype=*/true);
62946294
SmallVector<ParmVarDecl*, 16> Params;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -ast-print %s -o - | FileCheck %s
2+
3+
// CHECK: extern "C" int printf(const char *, ...);
4+
extern "C" int printf(const char *...);
5+
6+
// CHECK: extern "C++" int f(int);
7+
// CHECK-NEXT: extern "C++" int g(int);
8+
extern "C++" int f(int), g(int);
9+
10+
// CHECK: extern "C" char a;
11+
// CHECK-NEXT: extern "C" char b;
12+
extern "C" char a, b;
13+
14+
// CHECK: extern "C" {
15+
// CHECK-NEXT: void foo();
16+
// CHECK-NEXT: int x;
17+
// CHECK-NEXT: int y;
18+
// CHECK-NEXT: extern short z;
19+
// CHECK-NEXT: }
20+
extern "C" {
21+
void foo(void);
22+
int x, y;
23+
extern short z;
24+
}
25+
26+
// CHECK: extern "C" {
27+
// CHECK-NEXT: }
28+
extern "C" {}
29+
30+
// CHECK: extern "C++";
31+
extern "C++";

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ class CodeComplete : public CodeCompleteConsumer {
881881
else
882882
ToInsert += "(";
883883
raw_string_ostream OS(Description);
884-
F->print(OS, m_desc_policy, false);
884+
F->print(OS, m_desc_policy);
885885
OS.flush();
886886
} else if (const VarDecl *V = dyn_cast<VarDecl>(D)) {
887887
Description = V->getType().getAsString(m_desc_policy);

lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ clang::NamedDecl *NameSearchContext::AddFunDecl(const CompilerType &type,
7777

7878
clang::FunctionDecl *func_decl = FunctionDecl::Create(
7979
ast, context, SourceLocation(), SourceLocation(), decl_name, qual_type,
80-
nullptr, SC_Extern, /*UsesFPIntrin=*/false, isInlineSpecified, hasWrittenPrototype,
80+
nullptr, SC_None, /*UsesFPIntrin=*/false, isInlineSpecified,
81+
hasWrittenPrototype,
8182
isConstexprSpecified ? ConstexprSpecKind::Constexpr
8283
: ConstexprSpecKind::Unspecified);
8384

0 commit comments

Comments
 (0)