[clang] [clang][AST] fix ast-print of `extern <lang>` with >=2 declarators (PR #93131)
via cfe-commits
cfe-commits at lists.llvm.org
Tue May 28 17:34:05 PDT 2024
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/93131
>From ae5225aa51a0a8f79ed134d9d5013d5774d2c04d Mon Sep 17 00:00:00 2001
From: Artem Yurchenko <artemyurchenko at zoho.com>
Date: Wed, 22 May 2024 23:41:35 -0400
Subject: [PATCH] [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.
---
clang/lib/AST/DeclPrinter.cpp | 37 ++++++++++++++++---
clang/test/AST/ast-print-language-linkage.cpp | 31 ++++++++++++++++
2 files changed, 62 insertions(+), 6 deletions(-)
create mode 100644 clang/test/AST/ast-print-language-linkage.cpp
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 0cf4e64f83b8d..60d5074e69151 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) return nullptr;
+ if (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,10 @@ 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)) {
+ assert(D->getStorageClass() == SC_None); // the "extern" specifier is implicit
+ Out << "extern \"" << lang << "\" ";
+ }
switch (D->getStorageClass()) {
case SC_None: break;
case SC_Extern: Out << "extern "; break;
@@ -807,7 +827,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 +952,10 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
if (!Policy.SuppressSpecifiers) {
+ if (const char *lang = tryGetUnbracedLinkageLanguage(D)) {
+ assert(D->getStorageClass() == SC_None); // the "extern" specifier is implicit
+ Out << "extern \"" << lang << "\" ";
+ }
StorageClass SC = D->getStorageClass();
if (SC != SC_None)
Out << VarDecl::getStorageClassSpecifierString(SC) << " ";
@@ -961,7 +985,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 +1088,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) {
@@ -1145,13 +1171,12 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
l = "C++";
}
- Out << "extern \"" << l << "\" ";
if (D->hasBraces()) {
- Out << "{\n";
+ Out << "extern \"" << l << "\" {\n";
VisitDeclContext(D);
Indent() << "}";
} else
- Visit(*D->decls_begin());
+ VisitDeclContext(D);
}
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