Skip to content

Commit 3e19f75

Browse files
committed
[clang][AST] fix ast-print of extern <lang> with >=2 declarators
Problem: the printer used to ignore all but the first declarator for unbraced language linkage declarators. Furthemore, that one would be printed without the final semicolon. Solution: when there is more than one declarator, we print them in a braced `extern <lang>` block. If the original declaration was unbraced and there is one or less declarator, we omit the braces, but add the semicolon.
1 parent 2ba0838 commit 3e19f75

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed

clang/lib/AST/DeclPrinter.cpp

Lines changed: 32 additions & 6 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) return nullptr;
654+
if (SD->hasBraces()) 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()) {
@@ -661,7 +677,12 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
661677
CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D);
662678
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
663679
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
680+
664681
if (!Policy.SuppressSpecifiers) {
682+
if (const char *lang = tryGetUnbracedLinkageLanguage(D)) {
683+
assert(D->getStorageClass() == SC_None); // the "extern" specifier is implicit
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,10 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
932953
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
933954

934955
if (!Policy.SuppressSpecifiers) {
956+
if (const char *lang = tryGetUnbracedLinkageLanguage(D)) {
957+
assert(D->getStorageClass() == SC_None); // the "extern" specifier is implicit
958+
Out << "extern \"" << lang << "\" ";
959+
}
935960
StorageClass SC = D->getStorageClass();
936961
if (SC != SC_None)
937962
Out << VarDecl::getStorageClassSpecifierString(SC) << " ";
@@ -961,7 +986,7 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
961986

962987
if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
963988
!Policy.SuppressUnwrittenScope)
964-
MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
989+
maybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
965990

966991
printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
967992
D->getIdentifier())
@@ -1064,6 +1089,8 @@ void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) {
10641089

10651090
void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) {
10661091
prettyPrintAttributes(D);
1092+
if (const char *lang = tryGetUnbracedLinkageLanguage(D))
1093+
Out << "extern \"" << lang << "\";";
10671094
}
10681095

10691096
void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
@@ -1145,13 +1172,12 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
11451172
l = "C++";
11461173
}
11471174

1148-
Out << "extern \"" << l << "\" ";
11491175
if (D->hasBraces()) {
1150-
Out << "{\n";
1176+
Out << "extern \"" << l << "\" {\n";
11511177
VisitDeclContext(D);
11521178
Indent() << "}";
11531179
} else
1154-
Visit(*D->decls_begin());
1180+
VisitDeclContext(D);
11551181
}
11561182

11571183
void DeclPrinter::printTemplateParameters(const TemplateParameterList *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++";

0 commit comments

Comments
 (0)