Author: Artem Yurchenko Date: 2024-05-30T13:18:47-07:00 New Revision: 7b8048939024841e07f8d89ddfaa4311f9dd7e9c
URL: https://github.com/llvm/llvm-project/commit/7b8048939024841e07f8d89ddfaa4311f9dd7e9c DIFF: https://github.com/llvm/llvm-project/commit/7b8048939024841e07f8d89ddfaa4311f9dd7e9c.diff LOG: [clang][AST] fix ast-print of `extern <lang>` with >=2 declarators (#93131) 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. **N.B.** We are printing braces which were, in some cases, absent from the original CST. If that's an issue, I'll work on it. See the tests for the examples. Added: clang/test/AST/ast-print-language-linkage.cpp Modified: clang/lib/AST/DeclPrinter.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 0cf4e64f83b8d..9250a7f6eceb2 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -633,7 +633,7 @@ static void printExplicitSpecifier(ExplicitSpecifier ES, llvm::raw_ostream &Out, Out << Proto; } -static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy, +static void maybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy, QualType T, llvm::raw_ostream &Out) { StringRef prefix = T->isClassType() ? "class " @@ -643,6 +643,22 @@ static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy, Out << prefix; } +/// Return the language of the linkage spec of `D`, if applicable. +/// +/// \Return - "C" if `D` has been declared with unbraced `extern "C"` +/// - "C++" if `D` has been declared with unbraced `extern "C++"` +/// - nullptr in any other case +static const char *tryGetUnbracedLinkageLanguage(const Decl *D) { + const auto *SD = dyn_cast<LinkageSpecDecl>(D->getDeclContext()); + if (!SD || SD->hasBraces()) + return nullptr; + if (SD->getLanguage() == LinkageSpecLanguageIDs::C) + return "C"; + assert(SD->getLanguage() == LinkageSpecLanguageIDs::CXX && + "unknown language in linkage specification"); + return "C++"; +} + void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (!D->getDescribedFunctionTemplate() && !D->isFunctionTemplateSpecialization()) { @@ -662,6 +678,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D); if (!Policy.SuppressSpecifiers) { + if (const char *Lang = tryGetUnbracedLinkageLanguage(D)) { + // the "extern" specifier is implicit + assert(D->getStorageClass() == SC_None); + Out << "extern \"" << Lang << "\" "; + } switch (D->getStorageClass()) { case SC_None: break; case SC_Extern: Out << "extern "; break; @@ -807,7 +828,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { } if (!Policy.SuppressTagKeyword && Policy.SuppressScope && !Policy.SuppressUnwrittenScope) - MaybePrintTagKeywordIfSupressingScopes(Policy, AFT->getReturnType(), + maybePrintTagKeywordIfSupressingScopes(Policy, AFT->getReturnType(), Out); AFT->getReturnType().print(Out, Policy, Proto); Proto.clear(); @@ -932,6 +953,11 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { : D->getASTContext().getUnqualifiedObjCPointerType(D->getType()); if (!Policy.SuppressSpecifiers) { + if (const char *Lang = tryGetUnbracedLinkageLanguage(D)) { + // the "extern" specifier is implicit + assert(D->getStorageClass() == SC_None); + Out << "extern \"" << Lang << "\" "; + } StorageClass SC = D->getStorageClass(); if (SC != SC_None) Out << VarDecl::getStorageClassSpecifierString(SC) << " "; @@ -961,7 +987,7 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { if (!Policy.SuppressTagKeyword && Policy.SuppressScope && !Policy.SuppressUnwrittenScope) - MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out); + maybePrintTagKeywordIfSupressingScopes(Policy, T, Out); printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters && D->getIdentifier()) @@ -1064,6 +1090,8 @@ void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) { prettyPrintAttributes(D); + if (const char *Lang = tryGetUnbracedLinkageLanguage(D)) + Out << "extern \"" << Lang << "\";"; } void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { @@ -1136,22 +1164,21 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { } void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { - const char *l; + if (!D->hasBraces()) { + VisitDeclContext(D); + return; + } + const char *L; if (D->getLanguage() == LinkageSpecLanguageIDs::C) - l = "C"; + L = "C"; else { assert(D->getLanguage() == LinkageSpecLanguageIDs::CXX && "unknown language in linkage specification"); - l = "C++"; + L = "C++"; } - - Out << "extern \"" << l << "\" "; - if (D->hasBraces()) { - Out << "{\n"; - VisitDeclContext(D); - Indent() << "}"; - } else - Visit(*D->decls_begin()); + Out << "extern \"" << L << "\" {\n"; + VisitDeclContext(D); + Indent() << "}"; } void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params, diff --git a/clang/test/AST/ast-print-language-linkage.cpp b/clang/test/AST/ast-print-language-linkage.cpp new file mode 100644 index 0000000000000..7e4dc3f25f062 --- /dev/null +++ b/clang/test/AST/ast-print-language-linkage.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -ast-print %s -o - | FileCheck %s + +// CHECK: extern "C" int printf(const char *, ...); +extern "C" int printf(const char *...); + +// CHECK: extern "C++" int f(int); +// CHECK-NEXT: extern "C++" int g(int); +extern "C++" int f(int), g(int); + +// CHECK: extern "C" char a; +// CHECK-NEXT: extern "C" char b; +extern "C" char a, b; + +// CHECK: extern "C" { +// CHECK-NEXT: void foo(); +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: extern short z; +// CHECK-NEXT: } +extern "C" { + void foo(void); + int x, y; + extern short z; +} + +// CHECK: extern "C" { +// CHECK-NEXT: } +extern "C" {} + +// CHECK: extern "C++"; +extern "C++"; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits