-
Notifications
You must be signed in to change notification settings - Fork 11.7k
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
[clang-format] Make some binary operations imply requires clause #110942
base: main
Are you sure you want to change the base?
Conversation
This patch adjusts the requires clause/expression lookahead heuristic to assume that some binary operation mean that the requires keyword refers to a clause. This partially reverts the removed case clauses from 9b68c09, with an additional check that we're not in a type parameter. This is quite a band-aid fix, it may be worth it to rewrite that whole heuristic to track more state in the future, instead of just blindly marching forward across multiple unrelated definitions, since right now, the definition following the one with the requires clause can influence whether the heuristic chooses clause or expression. Fixes llvm#110485
@llvm/pr-subscribers-clang-format Author: Emilia Kond (rymiel) ChangesThis patch adjusts the requires clause/expression lookahead heuristic to assume that some binary operation mean that the requires keyword refers to a clause. This partially reverts the removed case clauses from 9b68c09, with an additional check that we're not in a type parameter. This is quite a band-aid fix, it may be worth it to rewrite that whole heuristic to track more state in the future, instead of just blindly marching forward across multiple unrelated definitions, since right now, the definition following the one with the requires clause can influence whether the heuristic chooses clause or expression. Fixes #110485 Full diff: https://github.com/llvm/llvm-project/pull/110942.diff 2 Files Affected:
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 40f77266fabdca..98078fb50dcfaa 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -3528,6 +3528,17 @@ bool UnwrappedLineParser::parseRequires() {
return false;
}
break;
+ case tok::equalequal:
+ case tok::greaterequal:
+ case tok::lessequal:
+ case tok::r_paren:
+ case tok::pipepipe:
+ if (OpenAngles == 0) {
+ FormatTok = Tokens->setPosition(StoredPosition);
+ parseRequiresClause(RequiresToken);
+ return true;
+ }
+ break;
case tok::eof:
// Break out of the loop.
Lookahead = 50;
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 1884d41a5f23f5..6f1b1df0bda655 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -1296,6 +1296,21 @@ TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {
Tokens = annotate("bool x = t && requires(Foo<C1 || C2> x) { x.foo(); };");
ASSERT_EQ(Tokens.size(), 25u) << Tokens;
EXPECT_TOKEN(Tokens[5], tok::kw_requires, TT_RequiresExpression);
+
+ // Second function definition is required due to lookahead
+ Tokens = annotate("void f() & requires(n == 1) {}\nvoid g();");
+ ASSERT_EQ(Tokens.size(), 19u) << Tokens;
+ EXPECT_TOKEN(Tokens[4], tok::amp, TT_PointerOrReference);
+ EXPECT_TOKEN(Tokens[5], tok::kw_requires, TT_RequiresClause);
+
+ Tokens = annotate("void f() & requires(n || h) {}\nvoid g();");
+ ASSERT_EQ(Tokens.size(), 19u) << Tokens;
+ EXPECT_TOKEN(Tokens[4], tok::amp, TT_PointerOrReference);
+ EXPECT_TOKEN(Tokens[5], tok::kw_requires, TT_RequiresClause);
+
+ Tokens = annotate("bool x = t && requires(F<n == 1> x) { x.foo(); };");
+ ASSERT_EQ(Tokens.size(), 25u) << Tokens;
+ EXPECT_TOKEN(Tokens[5], tok::kw_requires, TT_RequiresExpression);
}
TEST_F(TokenAnnotatorTest, UnderstandsRequiresExpressions) {
|
case tok::equalequal: | ||
case tok::greaterequal: | ||
case tok::lessequal: | ||
case tok::r_paren: | ||
case tok::pipepipe: | ||
if (OpenAngles == 0) { | ||
FormatTok = Tokens->setPosition(StoredPosition); | ||
parseRequiresClause(RequiresToken); | ||
return true; | ||
} | ||
break; |
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.
Instead, how about just moving case tok::amp:
up?
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -3474,10 +3474,10 @@ bool UnwrappedLineParser::parseRequires() {
case tok::r_paren:
case tok::kw_noexcept:
case tok::kw_const:
+ case tok::amp:
// This is a requires clause.
parseRequiresClause(RequiresToken);
return true;
- case tok::amp:
case tok::ampamp: {
// This can be either:
// if (... && requires (T t) ...)
The rationale is that it doesn't make sense to bitwise-and another expression with a requires
expression.
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.
Then wouldn't r-value reference methods still be broken? Those still exist even if they're rare. I mean, we can do both
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.
My suggested change (in place of yours) passes all the existing tests and the new tests you add here. Can you give a counterexample?
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.
What about the original issue but with an r-value ref-qualifier instead of an l-value one:
template <int n> struct S
{
void f() && requires(n == 1) {}
// some comment
void g() {}
};
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.
Nvm. This would fail:
template <int n> struct S
{
void f() &&
requires(n == 1)
{
}
// some comment
void g() {}
};
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.
What about <
and >
?
template <int n> struct S {
void f() &
requires(n < 1)
{}
// some comment
void g() {}
};
This patch adjusts the requires clause/expression lookahead heuristic to assume that some binary operation mean that the requires keyword refers to a clause.
This partially reverts the removed case clauses from 9b68c09, with an additional check that we're not in a type parameter.
This is quite a band-aid fix, it may be worth it to rewrite that whole heuristic to track more state in the future, instead of just blindly marching forward across multiple unrelated definitions, since right now, the definition following the one with the requires clause can influence whether the heuristic chooses clause or expression.
Fixes #110485