-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix the double space and double attribute printing of the final keyword. #88600
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-hlsl Author: Vassil Vassilev (vgvassilev) ChangesFixes #56517. cc: @kimgr Full diff: https://github.com/llvm/llvm-project/pull/88600.diff 4 Files Affected:
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 6afdb6cfccb142..c758f792b04a59 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -119,7 +119,7 @@ namespace {
void printTemplateArguments(llvm::ArrayRef<TemplateArgumentLoc> Args,
const TemplateParameterList *Params);
enum class AttrPosAsWritten { Default = 0, Left, Right };
- void
+ bool
prettyPrintAttributes(const Decl *D,
AttrPosAsWritten Pos = AttrPosAsWritten::Default);
void prettyPrintPragmas(Decl *D);
@@ -252,10 +252,12 @@ static DeclPrinter::AttrPosAsWritten getPosAsWritten(const Attr *A,
return DeclPrinter::AttrPosAsWritten::Right;
}
-void DeclPrinter::prettyPrintAttributes(const Decl *D,
+// returns true if an attribute was printed.
+bool DeclPrinter::prettyPrintAttributes(const Decl *D,
AttrPosAsWritten Pos /*=Default*/) {
+ bool hasPrinted = false;
if (Policy.PolishForDeclaration)
- return;
+ return hasPrinted;
if (D->hasAttrs()) {
const AttrVec &Attrs = D->getAttrs();
@@ -275,6 +277,7 @@ void DeclPrinter::prettyPrintAttributes(const Decl *D,
if (Pos != AttrPosAsWritten::Left)
Out << ' ';
A->printPretty(Out, Policy);
+ hasPrinted = true;
if (Pos == AttrPosAsWritten::Left)
Out << ' ';
}
@@ -282,6 +285,7 @@ void DeclPrinter::prettyPrintAttributes(const Decl *D,
}
}
}
+ return hasPrinted;
}
void DeclPrinter::prettyPrintPragmas(Decl *D) {
@@ -1060,12 +1064,14 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
// FIXME: add printing of pragma attributes if required.
if (!Policy.SuppressSpecifiers && D->isModulePrivate())
Out << "__module_private__ ";
- Out << D->getKindName();
- prettyPrintAttributes(D);
+ Out << D->getKindName() << ' ';
+
+ // FIXME: Move before printing the decl kind to match the behavior of the
+ // attribute printing for variables and function where they are printed first.
+ prettyPrintAttributes(D, AttrPosAsWritten::Left);
if (D->getIdentifier()) {
- Out << ' ';
if (auto *NNS = D->getQualifier())
NNS->print(Out, Policy);
Out << *D;
@@ -1082,11 +1088,8 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
}
}
- if (D->hasDefinition()) {
- if (D->hasAttr<FinalAttr>()) {
- Out << " final";
- }
- }
+ if (prettyPrintAttributes(D, AttrPosAsWritten::Right))
+ Out << ' ';
if (D->isCompleteDefinition()) {
// Print the base classes
@@ -1114,10 +1117,12 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
// Print the class definition
// FIXME: Doesn't print access specifiers, e.g., "public:"
+ //if (!D->getIdentifier())
+ Out << ' ';
if (Policy.TerseOutput) {
- Out << " {}";
+ Out << "{}";
} else {
- Out << " {\n";
+ Out << "{\n";
VisitDeclContext(D);
Indent() << "}";
}
diff --git a/clang/test/SemaCXX/cxx11-attr-print.cpp b/clang/test/SemaCXX/cxx11-attr-print.cpp
index a169d1b4409b4d..2b084018bc0662 100644
--- a/clang/test/SemaCXX/cxx11-attr-print.cpp
+++ b/clang/test/SemaCXX/cxx11-attr-print.cpp
@@ -87,3 +87,8 @@ template struct S<int>;
// CHECK: using Small2 {{\[}}[gnu::mode(byte)]] = int;
using Small2 [[gnu::mode(byte)]] = int;
+
+class FinalNonTemplate final {};
+// CHECK: class FinalNonTemplate final {
+template <typename T> class FinalTemplate final {};
+// CHECK: template <typename T> class FinalTemplate final {
diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
index 7e79ae3bf005fc..b1a15c43191829 100644
--- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
@@ -6,11 +6,11 @@ typedef vector<float, 3> float3;
RWBuffer<float3> Buffer;
// expected-error@+2 {{class template 'RWBuffer' requires template arguments}}
-// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer final}}
+// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer}}
RWBuffer BufferErr1;
// expected-error@+2 {{too few template arguments for class template 'RWBuffer'}}
-// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer final}}
+// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer}}
RWBuffer<> BufferErr2;
[numthreads(1,1,1)]
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 6c56f99f503df4..765cbbf3b04bcf 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -1608,7 +1608,7 @@ writePrettyPrintFunction(const Record &R,
Prefix = "[";
Suffix = "]";
} else if (Variety == "Keyword") {
- Prefix = " ";
+ Prefix = "";
Suffix = "";
} else if (Variety == "Pragma") {
Prefix = "#pragma ";
|
fe26228
to
b43c52e
Compare
@@ -6,11 +6,11 @@ typedef vector<float, 3> float3; | |||
RWBuffer<float3> Buffer; | |||
|
|||
// expected-error@+2 {{class template 'RWBuffer' requires template arguments}} | |||
// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer final}} | |||
// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We remove the final
keyword here as it is modeled as an attribute and HLSL calls FinalAttr::CreateImplicit
to constrain the users from inheriting from the builtin classes. We do not print implicit attributes. This used to work because we double printed final
once as an attribute and once as part of the regular printing of CXXRecordDecls...
Thanks! Could you add this DeclPrinterTest unittest for regression?
This is my expectation for correct output, but the current branch seems to disagree -- now there's two spaces between EDIT: Hmm, I see your
|
This is actually a regression in this PR, and it breaks the clangd test added here: 9f57b65 (the patch that originally led to double EDIT: It looks like |
b43c52e
to
bcd04db
Compare
I've added a fix for your example. Can you provide a test case for the clangd use-case? |
Thank you. I believe this should cover both cases, added some attempt at rationale in comments: diff --git a/clang/unittests/AST/DeclPrinterTest.cpp b/clang/unittests/AST/DeclPrinterTest.cpp
index f2b027a25621..8691a6c38f16 100644
--- a/clang/unittests/AST/DeclPrinterTest.cpp
+++ b/clang/unittests/AST/DeclPrinterTest.cpp
@@ -86,16 +86,13 @@ PrintedDeclCXX98Matches(StringRef Code, const DeclarationMatcher &NodeMatch,
ExpectedPrinted, "input.cc");
}
-::testing::AssertionResult PrintedDeclCXX11Matches(
- StringRef Code,
- const DeclarationMatcher &NodeMatch,
- StringRef ExpectedPrinted) {
+::testing::AssertionResult
+PrintedDeclCXX11Matches(StringRef Code, const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted,
+ PrintingPolicyAdjuster PolicyModifier = nullptr) {
std::vector<std::string> Args(1, "-std=c++11");
- return PrintedDeclMatches(Code,
- Args,
- NodeMatch,
- ExpectedPrinted,
- "input.cc");
+ return PrintedDeclMatches(Code, Args, NodeMatch, ExpectedPrinted, "input.cc",
+ PolicyModifier);
}
::testing::AssertionResult PrintedDeclCXX11nonMSCMatches(
@@ -1556,3 +1553,25 @@ TEST(DeclPrinter, VarDeclWithInitializer) {
PrintedDeclCXX17Matches("void foo() {int arr[42]; for(int a : arr);}",
namedDecl(hasName("a")).bind("id"), "int a"));
}
+
+TEST(DeclPrinter, TestTemplateFinal) {
+ // By default we should print 'final' keyword whether class is implicitly or
+ // explicitly marked final.
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename T>\n"
+ "class FinalTemplate final {};",
+ classTemplateDecl(hasName("FinalTemplate")).bind("id"),
+ "template <typename T> class FinalTemplate final {}"));
+}
+
+TEST(DeclPrinter, TestTemplateFinalWithPolishForDecl) {
+ // clangd relies on the 'final' keyword being printed when
+ // PolishForDeclaration is enabled, so make sure it is even if implicit attrs
+ // are disabled.
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename T>\n"
+ "class FinalTemplate final {};",
+ classTemplateDecl(hasName("FinalTemplate")).bind("id"),
+ "template <typename T> class FinalTemplate final {}",
+ [](PrintingPolicy &Policy) { Policy.PolishForDeclaration = true; }));
+} |
IIUC, the PolishForDeclaration option is supposed to |
bcd04db
to
c68344d
Compare
I've put a fix that fixes the cases that you mentioned... |
c68344d
to
cb3da95
Compare
cb3da95
to
9b2bb90
Compare
I don't believe that's the intent of
Sorry for stirring confusion into this, my primary focus is IWYU, but I remember that the original double-final came from a change intended for Clangd. Hopefully this helps clarify. |
|
||
if (D->hasAttrs()) { | ||
const AttrVec &Attrs = D->getAttrs(); | ||
for (auto *A : Attrs) { | ||
if (A->isInherited() || A->isImplicit()) | ||
continue; | ||
// Don't strip out the keyword attributes, they aren't regular attributes. | ||
if (Policy.PolishForDeclaration && !A->isKeywordAttribute()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I was about to suggest something like this, but didn't know isKeywordAttribute
existed.
The ROOT usecase of the Cling interpreter has similar infrastructure and it works at scale couple of million lines of scientific codes: https://github.com/root-project/root/blob/master/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp. There are some tests that one can look at: https://github.com/root-project/root/tree/master/interpreter/cling/test/Autoloading I am wondering if that'd be interesting and if so, maybe we can share it between both projects via upstreaming to Clang... |
Current PR passes all my tests, both Clang ( |
That sounds fantastic, but mostly because I don't have anything to offer, and everything to benefit :) I was just thinking about adding a I suspect your Let's maybe continue this discussion somewhere else? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 nits, else LGTM. Once you and @kimgr are in agreement with what to do next (and have looked into my nits), feel free to commit.
clang/lib/AST/DeclPrinter.cpp
Outdated
|
||
if (D->hasAttrs()) { | ||
const AttrVec &Attrs = D->getAttrs(); | ||
for (auto *A : Attrs) { | ||
if (A->isInherited() || A->isImplicit()) | ||
continue; | ||
// Don't strip out the keyword attributes, they aren't regular attributes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Don't strip out the keyword attributes, they aren't regular attributes. | |
// Don't print out the keyword attributes, they aren't regular attributes. |
??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should word it as Print out the keyword attributes, they aren't regular attributes.
, I think.
if (Pos == AttrPosAsWritten::Left) | ||
Out << ' '; | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
return hasPrinted; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see Out->tell()
here, it is perhaps less error-prone here (that is, store the uint64_t
value above, and see if it changed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking to use something like that but I believe the boolean flag is more readable. I'd like to keep it the way it is.
@vgvassilev Thank you! |
Fixes #56517.
cc: @kimgr