[clang] 7b80489 - [clang][AST] fix ast-print of `extern <lang>` with >=2 declarators (#93131)

via cfe-commits cfe-commits at lists.llvm.org
Thu May 30 13:18:51 PDT 2024


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++";


        


More information about the cfe-commits mailing list