[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From fac45476736cd04f044f046fd0df9e59450a926b Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH 1/2] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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->hasBrace
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From fac45476736cd04f044f046fd0df9e59450a926b Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH 1/2] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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->hasBrace
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
temyurchenko wrote: This is a relanding of #93131. The first commit is the same, the second commit presents and fixes the issue from the linked discussion. cc @erichkeane, @AaronBallman, @gulfemsavrun. (I can't set the reviewers myself) https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
temyurchenko wrote: > I see the 2nds commit doesn't add any tests! Please make it do so I've tried and I'm not quite sure how to do it. The issue is we need to test AST printing of «implicitly declared» functions, such as builtins and functions instrumented by lldb, such as `log` for logging; which seems contradictory. Do you have any suggestions? Furthermore, the second commit adds an assertion of an invariant and then fixes the places in the code where the invariant breaks. Is it fair to say that the codebase itself is the test for the invariant? If any codepath would break the invariant, the assertion would be triggered. Not on a release build though, I imagine. https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From a54036c0fdc1a875f40a274d502dd96651a35ae3 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Thu, 6 Jun 2024 20:20:19 -0400 Subject: [PATCH] enforce the unbraced `extern ` invariant Quoting 9.11.8: > A declaration directly contained in a linkage-specification is > treated as if it contains the extern specifier (9.2.2) for the > purpose of determining the linkage of the declared name and whether > it is a definition. Such a declaration shall not specify a storage > class. So, the invariant is that function and variable declarations within an unbraced language linkage specification have `StorageClass` set to `SC_None`. Problem: in several places the invariant was broken. Solution: remove the explicit `SC_Extern` specification. Note, that my changes result in the `extern` being implicit in some functions that are not contained in a language linkage specification. --- clang/lib/AST/Decl.cpp| 26 --- clang/lib/Sema/SemaDecl.cpp | 2 +- .../Clang/ClangExpressionParser.cpp | 2 +- .../Clang/NameSearchContext.cpp | 3 ++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 1f19dadafa44e..ebaeed0e818e1 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -576,13 +576,19 @@ template static bool isFirstInExternCContext(T *D) { return First->isInExternCContext(); } -static bool isSingleLineLanguageLinkage(const Decl &D) { - if (const auto *SD = dyn_cast(D.getDeclContext())) +static bool isUnbracedLanguageLinkage(const DeclContext *DC) { + if (!DC) +return false; + if (const auto *SD = dyn_cast(DC)) if (!SD->hasBraces()) return true; return false; } +static bool hasUnbracedLanguageLinkage(const Decl &D) { + return isUnbracedLanguageLinkage(D.getDeclContext()); +} + static bool isDeclaredInModuleInterfaceOrPartition(const NamedDecl *D) { if (auto *M = D->getOwningModule()) return M->isInterfaceOrPartition(); @@ -644,7 +650,7 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, if (Var->getStorageClass() != SC_Extern && Var->getStorageClass() != SC_PrivateExtern && - !isSingleLineLanguageLinkage(*Var)) + !hasUnbracedLanguageLinkage(*Var)) return LinkageInfo::internal(); } @@ -2133,6 +2139,12 @@ VarDecl::VarDecl(Kind DK, ASTContext &C, DeclContext *DC, "ParmVarDeclBitfields too large!"); static_assert(sizeof(NonParmVarDeclBitfields) <= sizeof(unsigned), "NonParmVarDeclBitfields too large!"); + + // The unbraced `extern "C"` invariant is that the storage class + // specifier is omitted in the source code, i.e. SC_None (but is, + // implicitly, `extern`). + assert(!isUnbracedLanguageLinkage(DC) || SC == SC_None); + AllBits = 0; VarDeclBits.SClass = SC; // Everything else is implicitly initialized to false. @@ -2316,7 +2328,7 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const { // A declaration directly contained in a linkage-specification is treated // as if it contains the extern specifier for the purpose of determining // the linkage of the declared name and whether it is a definition. - if (isSingleLineLanguageLinkage(*this)) + if (hasUnbracedLanguageLinkage(*this)) return DeclarationOnly; // C99 6.9.2p2: @@ -3041,6 +3053,12 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) { assert(T.isNull() || T->isFunctionType()); + + // The unbraced `extern "C"` invariant is that the storage class + // specifier is omitted in the source code, i.e. SC_None (but is, + // implicitly, `extern`). + assert(!isUnbracedLanguageLinkage(DC) || S == SC_None); + FunctionDeclBits.SClass = S; FunctionDeclBits.IsInline = isInlineSpecified; FunctionDeclBits.IsInlineSpecified = isInlineSpecified; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4b9b735f1cfb4..4e2f02ba7007a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2380,7 +2380,7 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, } FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type, - /*TInfo=*/nullptr, SC_Extern, + /*TInfo=*/nullptr, SC_None, getCurFPFeatures().isFPConstrained(), false, Type->isFunctionProtoType()); New->setImplicit(); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpression
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
temyurchenko wrote: > Hmm... I don't have a great idea. Is using it not enough to get it to be > emitted? I see here we don't seem to cause them to be emitted: > https://godbolt.org/z/nYzMca7Te Yes, I get the same result. I also tried adding a test in `DeclPrinterTest`, but it doesn't lookup builtins. https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From 9b18a5c4132cf093fc9b32f6b4dcfe406d485e3d Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Thu, 6 Jun 2024 20:20:19 -0400 Subject: [PATCH] enforce the unbraced `extern ` invariant Quoting 9.11.8: > A declaration directly contained in a linkage-specification is > treated as if it contains the extern specifier (9.2.2) for the > purpose of determining the linkage of the declared name and whether > it is a definition. Such a declaration shall not specify a storage > class. So, the invariant is that function and variable declarations within an unbraced language linkage specification have `StorageClass` set to `SC_None`. Problem: in several places the invariant was broken. Solution: remove the explicit `SC_Extern` specification. Note, that my changes result in the `extern` being implicit in some functions that are not contained in a language linkage specification. --- clang/lib/AST/Decl.cpp| 26 --- clang/lib/Sema/SemaDecl.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 2 +- .../Clang/ClangExpressionParser.cpp | 2 +- .../Clang/NameSearchContext.cpp | 3 ++- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 1f19dadafa44e..ebaeed0e818e1 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -576,13 +576,19 @@ template static bool isFirstInExternCContext(T *D) { return First->isInExternCContext(); } -static bool isSingleLineLanguageLinkage(const Decl &D) { - if (const auto *SD = dyn_cast(D.getDeclContext())) +static bool isUnbracedLanguageLinkage(const DeclContext *DC) { + if (!DC) +return false; + if (const auto *SD = dyn_cast(DC)) if (!SD->hasBraces()) return true; return false; } +static bool hasUnbracedLanguageLinkage(const Decl &D) { + return isUnbracedLanguageLinkage(D.getDeclContext()); +} + static bool isDeclaredInModuleInterfaceOrPartition(const NamedDecl *D) { if (auto *M = D->getOwningModule()) return M->isInterfaceOrPartition(); @@ -644,7 +650,7 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, if (Var->getStorageClass() != SC_Extern && Var->getStorageClass() != SC_PrivateExtern && - !isSingleLineLanguageLinkage(*Var)) + !hasUnbracedLanguageLinkage(*Var)) return LinkageInfo::internal(); } @@ -2133,6 +2139,12 @@ VarDecl::VarDecl(Kind DK, ASTContext &C, DeclContext *DC, "ParmVarDeclBitfields too large!"); static_assert(sizeof(NonParmVarDeclBitfields) <= sizeof(unsigned), "NonParmVarDeclBitfields too large!"); + + // The unbraced `extern "C"` invariant is that the storage class + // specifier is omitted in the source code, i.e. SC_None (but is, + // implicitly, `extern`). + assert(!isUnbracedLanguageLinkage(DC) || SC == SC_None); + AllBits = 0; VarDeclBits.SClass = SC; // Everything else is implicitly initialized to false. @@ -2316,7 +2328,7 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const { // A declaration directly contained in a linkage-specification is treated // as if it contains the extern specifier for the purpose of determining // the linkage of the declared name and whether it is a definition. - if (isSingleLineLanguageLinkage(*this)) + if (hasUnbracedLanguageLinkage(*this)) return DeclarationOnly; // C99 6.9.2p2: @@ -3041,6 +3053,12 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) { assert(T.isNull() || T->isFunctionType()); + + // The unbraced `extern "C"` invariant is that the storage class + // specifier is omitted in the source code, i.e. SC_None (but is, + // implicitly, `extern`). + assert(!isUnbracedLanguageLinkage(DC) || S == SC_None); + FunctionDeclBits.SClass = S; FunctionDeclBits.IsInline = isInlineSpecified; FunctionDeclBits.IsInlineSpecified = isInlineSpecified; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4b9b735f1cfb4..4e2f02ba7007a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2380,7 +2380,7 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, } FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type, - /*TInfo=*/nullptr, SC_Extern, + /*TInfo=*/nullptr, SC_None, getCurFPFeatures().isFPConstrained(), false, Type->isFunctionProtoType()); New->setImplicit(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 76145f291887
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From f1951f3f2dd322123a1c49221c4252f4ea932242 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko <44875844+temyurche...@users.noreply.github.com> Date: Thu, 30 May 2024 16:18:47 -0400 Subject: [PATCH 1/2] [clang][AST] fix ast-print of `extern ` 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 ` 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. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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(CXXReco
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From 410c7ba9fb7667dabdfbc48fdbda427401ca8df0 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko <44875844+temyurche...@users.noreply.github.com> Date: Thu, 30 May 2024 16:18:47 -0400 Subject: [PATCH 1/2] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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(Li
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
@@ -2380,7 +2380,7 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, } FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type, - /*TInfo=*/nullptr, SC_Extern, + /*TInfo=*/nullptr, SC_None, temyurchenko wrote: You are right, I also paid attention to that issue. However, when a storage class specifier is omitted for a function, it's storage class is implicitly supposed to be `extern`. Thus, it's fine. Furthermore, the meaning of this field according to the documentation comments is not for the actual storage duration or linkage, but for the storage-class specifier _as present in the source code_. Since builtins aren't really present in the source code, it seems further reasonable to omit a specifier. https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
@@ -576,13 +576,19 @@ template static bool isFirstInExternCContext(T *D) { return First->isInExternCContext(); } -static bool isSingleLineLanguageLinkage(const Decl &D) { - if (const auto *SD = dyn_cast(D.getDeclContext())) +static bool isUnbracedLanguageLinkage(const DeclContext *DC) { + if (!DC) +return false; + if (const auto *SD = dyn_cast(DC)) if (!SD->hasBraces()) return true; temyurchenko wrote: Will do. https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From 410c7ba9fb7667dabdfbc48fdbda427401ca8df0 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko <44875844+temyurche...@users.noreply.github.com> Date: Thu, 30 May 2024 16:18:47 -0400 Subject: [PATCH 1/3] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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(Li
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
@@ -2380,7 +2380,7 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, } FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type, - /*TInfo=*/nullptr, SC_Extern, + /*TInfo=*/nullptr, SC_None, temyurchenko wrote: > That's not actually correct -- the declaration of a function at block scope > should definitely _not_ be extern, it should have no linkage: > https://godbolt.org/z/81fMaPaTq I may be wrong to rely on cppreference, but the following page tells that indeed a function at a block scope has implicit `extern`, which could also be made explicit: https://en.cppreference.com/w/c/language/storage_duration. I also checked your example locally by compiling (via clang) `main.c` with an additional `defs.c`: main.c: ```c int main(void) { void f(); extern void g(); f(); g(); return 0; } ``` defs.c: ```c #include void f() { printf("f\n"); } void g() { printf("g\n"); } ``` The executable works as expected. I will also take a look at the C99 standard draft. https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
@@ -2380,7 +2380,7 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, } FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type, - /*TInfo=*/nullptr, SC_Extern, + /*TInfo=*/nullptr, SC_None, temyurchenko wrote: > > That's not actually correct -- the declaration of a function at block scope > > should definitely _not_ be extern, it should have no linkage: > > https://godbolt.org/z/81fMaPaTq > > I may be wrong to rely on cppreference, but the following page tells that > indeed a function at a block scope has implicit `extern`, which could also be > made explicit: https://en.cppreference.com/w/c/language/storage_duration. > > I also checked your example locally by compiling (via clang) `main.c` with an > additional `defs.c`: main.c: > > ```c > int main(void) { > void f(); > extern void g(); > > f(); > g(); > > return 0; > } > ``` > > defs.c: > > ```c > #include > > void f() { > printf("f\n"); > } > > void g() { > printf("g\n"); > } > ``` > > The executable works as expected. > > I will also take a look at the C99 standard draft. Paragraph 6.2.2.5 says: > If the declaration of an identifier for a function has no storage-class > specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external. Thus, seemingly confirming that a function declared at the block scope is declared with an implicit `extern`. https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko edited https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From 410c7ba9fb7667dabdfbc48fdbda427401ca8df0 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko <44875844+temyurche...@users.noreply.github.com> Date: Thu, 30 May 2024 16:18:47 -0400 Subject: [PATCH 1/3] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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(Li
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93913 >From 410c7ba9fb7667dabdfbc48fdbda427401ca8df0 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko <44875844+temyurche...@users.noreply.github.com> Date: Thu, 30 May 2024 16:18:47 -0400 Subject: [PATCH 1/2] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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(Li
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
@@ -576,13 +576,18 @@ template static bool isFirstInExternCContext(T *D) { return First->isInExternCContext(); } -static bool isSingleLineLanguageLinkage(const Decl &D) { - if (const auto *SD = dyn_cast(D.getDeclContext())) -if (!SD->hasBraces()) - return true; +static bool isUnbracedLanguageLinkage(const DeclContext *DC) { + if (!DC) +return false; + if (const auto *SD = dyn_cast(DC)) +return !SD->hasBraces(); return false; } temyurchenko wrote: Done! https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [lldb] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
temyurchenko wrote: > Do you need someone to land these changes on your behalf? Yeah, I don't have the rights :( https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -0,0 +1,27 @@ +// 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++" { temyurchenko wrote: > Oofda, there are differences between the braced and non-braced versions, so > ideally, we want to reproduce exactly what was in the user's source to avoid > accidentally changing the meaning of code You are completely right, my bad. I'll work on fixing this. > z, y, and z are external, a, b, and c are not: https://godbolt.org/z/evG9KPKMM I believe, they all have external linkage. However, x, y and z are also definitions, while a, b, and c are just declarations. So, for example, you can reference `x` from another translation unit and the value will be `0`. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko edited https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -0,0 +1,27 @@ +// 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++" { temyurchenko wrote: > ideally, we want to reproduce exactly what was in the user's source to avoid > accidentally changing the meaning of code Do you know why we split declarators from the same declaration into several declarations, whenever possible? Judging by the comments in `DeclPrinter::visitDeclContext` it seems intentional and desired. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko edited https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93131 >From 3e19f7566f8dc96116c463e7c4fae0c094d3640c Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH] [clang][AST] fix ast-print of `extern ` 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 ` 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 | 38 --- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 63 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..9aac3f7034b8b 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(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()) { @@ -661,7 +677,12 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXConstructorDecl *CDecl = dyn_cast(D); CXXConversionDecl *ConversionDecl = dyn_cast(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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 +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,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 +986,7 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { if (!Policy.SuppressTagKeyword && Policy.SuppressScope && !Policy.SuppressUnwrittenScope) -MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out); +maybePrintTagKeywordIfSupressingScopes(Policy, T, Out); printDeclType(T, (isa(D) && Policy.CleanUglifiedParameters && D->getIdentifier()) @@ -1064,6 +1089,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 +1172,12 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } - Out << "extern \"" << l << "\" "; if
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -0,0 +1,27 @@ +// 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++" { temyurchenko wrote: Hopefully, the new implementation has fixed the issue. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93131 >From b67bd3dc6a47aad978b699ae5451b5a595479546 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH] [clang][AST] fix ast-print of `extern ` 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 ` 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 | 39 --- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 64 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..4cfd359628467 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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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) { @@ -1145,13 +1173,12 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } - Out << "extern \"" << l << "\" "; if (D->hasBraces()) { -Out << "{\n"; +Out << "e
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93131 >From 0fd91ab5ba81c42594551d2662534d8a76c60d77 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 39 --- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 64 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..4cfd359628467 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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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) { @@ -1145,13 +1173,12 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } - Out << "extern \"" << l << "\" "; if (D->hasBraces()) { -Out << "{\
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93131 >From a8f15038fa0fbe3ba55d136eb459f511d73b0b34 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 49 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 2 files changed, 69 insertions(+), 11 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..9e7ce96d1b41e 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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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,6 +1164,10 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { } void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { + if (!D->hasBraces()) { +VisitDeclC
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93131 >From b1490a354b4ddcbcf0bfd7790c9f4fd8bd799751 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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())
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -662,6 +678,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXConversionDecl *ConversionDecl = dyn_cast(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(D); if (!Policy.SuppressSpecifiers) { +if (const char *lang = tryGetUnbracedLinkageLanguage(D)) { + // the "extern" specifier is implicit + assert(D->getStorageClass() == SC_None); + Out << "extern \"" << lang << "\" "; +} temyurchenko wrote: fixed https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93131 >From fac45476736cd04f044f046fd0df9e59450a926b Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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())
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
temyurchenko wrote: What should be the next step following the approvals? https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
temyurchenko wrote: > > What should be the next step following the approvals? > > If you do not have 'squash and merge' rights here, one of us can do it for > you once testing completes. I don't have them, would be glad if you could do that! https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
temyurchenko wrote: > We started seeing an lldb test failure, and I bisected to this commit: I see the source of the bug, I'm thinking how to address it best. If it's urgent, I can produce an urgent fix. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of extern with >=2 declarators, retry (PR #93913)
https://github.com/temyurchenko created https://github.com/llvm/llvm-project/pull/93913 None >From fac45476736cd04f044f046fd0df9e59450a926b Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH 1/2] [clang][AST] fix ast-print of `extern ` 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: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately. --- clang/lib/AST/DeclPrinter.cpp | 55 ++- clang/test/AST/ast-print-language-linkage.cpp | 31 +++ 2 files changed, 72 insertions(+), 14 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..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(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(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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(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->ha
[clang] [clang][AST] fix ast-print of extern with >=2 declarators, fixed (PR #93913)
https://github.com/temyurchenko edited https://github.com/llvm/llvm-project/pull/93913 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
temyurchenko wrote: > No worries, and I reverted it for you. When you have the fix ready, I'm happy > to verify it on our builders before you reland. Can you also reopen this PR, please? For documentation/history reasons, it's probably better to provide a fix within the scope of this PR. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko created https://github.com/llvm/llvm-project/pull/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 ` 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. >From 10670795596129c33ae021a3970411b630637b80 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH] [clang][AST] fix ast-print of `extern ` 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 ` 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 | 6 +++-- clang/test/AST/ast-print-language-linkage.cpp | 27 +++ 2 files changed, 31 insertions(+), 2 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..369dfffe58667 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1145,13 +1145,15 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } + bool HasMoreThanOneDecl = + *D->decls_begin() && D->decls_begin()->getNextDeclInContext(); Out << "extern \"" << l << "\" "; - if (D->hasBraces()) { + if (D->hasBraces() || HasMoreThanOneDecl) { Out << "{\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 0..4998a62f280b5 --- /dev/null +++ b/clang/test/AST/ast-print-language-linkage.cpp @@ -0,0 +1,27 @@ +// 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++" { +// CHECK-NEXT: int f(int); +// CHECK-NEXT: int g(int); +// CHECK-NEXT: } +extern "C++" int f(int), g(int); + +// CHECK: extern "C" { +// CHECK-NEXT: void foo(); +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: } +extern "C" { + void foo(void); + int x, y; +} + +// 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
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
temyurchenko wrote: Fixes #93085 https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -0,0 +1,27 @@ +// 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++" { temyurchenko wrote: Unfortunately, I'm not aware of the design goals of `DeclPrinter`. How accurately is it trying to represent the original code? Because AST's are naturally a lossy projection of CST's, so there is always going to be a difference to the original. How much of that difference is admissible? I can try to work around that by querying `VarDecl::isInExternCContext`/`isInExternCXXContext` inside of `VarDecl`, but my initial naive implementation didn't have the desired result. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -1145,13 +1145,15 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } + bool HasMoreThanOneDecl = + *D->decls_begin() && D->decls_begin()->getNextDeclInContext(); temyurchenko wrote: Sure! For my own education though, I'd like to know the reasoning, just to be able to make the right decision in future situations. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -0,0 +1,27 @@ +// 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++" { +// CHECK-NEXT: int f(int); +// CHECK-NEXT: int g(int); +// CHECK-NEXT: } +extern "C++" int f(int), g(int); + +// CHECK: extern "C" { +// CHECK-NEXT: void foo(); +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: } +extern "C" { + void foo(void); + int x, y; temyurchenko wrote: If you mean the splitting of `int x, y` into `int x; int y`, that's actually the «default». The declarators are split into different declarations whenever it's possible. So, the splitting doesn't happen only when there is a definition of a tagged type within the declaration, I believe. So, for example: `union {} a, b;` → `union {\n} a, b;` but `int a, b;` → `int a; int b;` That fact, that this very splitting already occurs in DeclPrinter and leads to the deviation from the original source, was the reason why I felt more comfortable to go forward with adding braces. https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
@@ -1145,13 +1145,15 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } + bool HasMoreThanOneDecl = + *D->decls_begin() && D->decls_begin()->getNextDeclInContext(); temyurchenko wrote: Got it! https://github.com/llvm/llvm-project/pull/93131 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][AST] fix ast-print of `extern ` with >=2 declarators (PR #93131)
https://github.com/temyurchenko updated https://github.com/llvm/llvm-project/pull/93131 >From 10670795596129c33ae021a3970411b630637b80 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 22 May 2024 23:41:35 -0400 Subject: [PATCH 1/2] [clang][AST] fix ast-print of `extern ` 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 ` 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 | 6 +++-- clang/test/AST/ast-print-language-linkage.cpp | 27 +++ 2 files changed, 31 insertions(+), 2 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..369dfffe58667 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1145,13 +1145,15 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } + bool HasMoreThanOneDecl = + *D->decls_begin() && D->decls_begin()->getNextDeclInContext(); Out << "extern \"" << l << "\" "; - if (D->hasBraces()) { + if (D->hasBraces() || HasMoreThanOneDecl) { Out << "{\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 0..4998a62f280b5 --- /dev/null +++ b/clang/test/AST/ast-print-language-linkage.cpp @@ -0,0 +1,27 @@ +// 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++" { +// CHECK-NEXT: int f(int); +// CHECK-NEXT: int g(int); +// CHECK-NEXT: } +extern "C++" int f(int), g(int); + +// CHECK: extern "C" { +// CHECK-NEXT: void foo(); +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: } +extern "C" { + void foo(void); + int x, y; +} + +// CHECK: extern "C" { +// CHECK-NEXT: } +extern "C" {} + +// CHECK: extern "C++" ; +extern "C++"; >From b4204dff09bd5fc47edf1f136d512db7570e4ad7 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Thu, 23 May 2024 14:15:31 -0400 Subject: [PATCH 2/2] fixup! [clang][AST] fix ast-print of `extern ` with >=2 declarators --- clang/lib/AST/DeclPrinter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 369dfffe58667..131d2ac37bb7e 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1145,8 +1145,7 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { l = "C++"; } - bool HasMoreThanOneDecl = - *D->decls_begin() && D->decls_begin()->getNextDeclInContext(); + bool HasMoreThanOneDecl = std::distance(D->decls_begin(), D->decls_end()) > 1; Out << "extern \"" << l << "\" "; if (D->hasBraces() || HasMoreThanOneDecl) { Out << "{\n"; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits