[clang] 96824ab - [clang-format] Detect pointer qualifiers in cast expressions
Alex Richardson via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 28 03:32:13 PDT 2020
Author: Alex Richardson
Date: 2020-08-28T11:31:47+01:00
New Revision: 96824abe7d80fc499032dab598968436132fcfb5
URL: https://github.com/llvm/llvm-project/commit/96824abe7d80fc499032dab598968436132fcfb5
DIFF: https://github.com/llvm/llvm-project/commit/96824abe7d80fc499032dab598968436132fcfb5.diff
LOG: [clang-format] Detect pointer qualifiers in cast expressions
When guessing whether a closing paren is then end of a cast expression also
skip over pointer qualifiers while looking for TT_PointerOrReference.
This prevents some address-of and dereference operators from being parsed
as a binary operator.
Before:
x = (foo *const) * v;
x = (foo *const volatile restrict __attribute__((foo)) _Nonnull _Null_unspecified _Nonnull) & v;
After:
x = (foo *const)*v;
x = (foo *const volatile restrict __attribute__((foo)) _Nonnull _Null_unspecified _Nonnull)&v;
Reviewed By: MyDeveloperDay
Differential Revision: https://reviews.llvm.org/D86716
Added:
Modified:
clang/lib/Format/FormatToken.h
clang/lib/Format/TokenAnnotator.cpp
clang/unittests/Format/FormatTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index ece1bf4b97f7..a54600a478a4 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -439,6 +439,12 @@ struct FormatToken {
(!ColonRequired || (Next && Next->is(tok::colon)));
}
+ bool canBePointerOrReferenceQualifier() const {
+ return isOneOf(tok::kw_const, tok::kw_restrict, tok::kw_volatile,
+ tok::kw___attribute, tok::kw__Nonnull, tok::kw__Nullable,
+ tok::kw__Null_unspecified);
+ }
+
/// Determine whether the token is a simple-type-specifier.
bool isSimpleTypeSpecifier() const;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 3e25ef6b9774..a9077500e041 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1827,10 +1827,30 @@ class AnnotatingParser {
return true;
// Heuristically try to determine whether the parentheses contain a type.
- bool ParensAreType =
- !Tok.Previous ||
- Tok.Previous->isOneOf(TT_PointerOrReference, TT_TemplateCloser) ||
- Tok.Previous->isSimpleTypeSpecifier();
+ auto IsQualifiedPointerOrReference = [](FormatToken *T) {
+ // This is used to handle cases such as x = (foo *const)&y;
+ assert(!T->isSimpleTypeSpecifier() && "Should have already been checked");
+ // Strip trailing qualifiers such as const or volatile when checking
+ // whether the parens could be a cast to a pointer/reference type.
+ while (T) {
+ if (T->is(TT_AttributeParen)) {
+ // Handle `x = (foo *__attribute__((foo)))&v;`:
+ if (T->MatchingParen && T->MatchingParen->Previous &&
+ T->MatchingParen->Previous->is(tok::kw___attribute)) {
+ T = T->MatchingParen->Previous->Previous;
+ continue;
+ }
+ } else if (T->canBePointerOrReferenceQualifier()) {
+ T = T->Previous;
+ continue;
+ }
+ break;
+ }
+ return T && T->is(TT_PointerOrReference);
+ };
+ bool ParensAreType = !Tok.Previous || Tok.Previous->is(TT_TemplateCloser) ||
+ Tok.Previous->isSimpleTypeSpecifier() ||
+ IsQualifiedPointerOrReference(Tok.Previous);
bool ParensCouldEndDecl =
Tok.Next->isOneOf(tok::equal, tok::semi, tok::l_brace, tok::greater);
if (ParensAreType && !ParensCouldEndDecl)
@@ -1890,10 +1910,8 @@ class AnnotatingParser {
const FormatToken *NextToken = Tok.getNextNonComment();
if (!NextToken ||
- NextToken->isOneOf(
- tok::arrow, tok::equal, tok::kw_const, tok::kw_restrict,
- tok::kw_volatile, tok::kw___attribute, tok::kw__Nonnull,
- tok::kw__Nullable, tok::kw__Null_unspecified, tok::kw_noexcept) ||
+ NextToken->isOneOf(tok::arrow, tok::equal, tok::kw_noexcept) ||
+ NextToken->canBePointerOrReferenceQualifier() ||
(NextToken->is(tok::l_brace) && !NextToken->getNextNonComment()))
return TT_PointerOrReference;
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 7e505c2401e9..f2978cdbed8d 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -8127,6 +8127,33 @@ TEST_F(FormatTest, UnderstandsAttributes) {
AfterType);
}
+TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) {
+ // Check that qualifiers on pointers don't break parsing of casts.
+ verifyFormat("x = (foo *const)*v;");
+ verifyFormat("x = (foo *volatile)*v;");
+ verifyFormat("x = (foo *restrict)*v;");
+ verifyFormat("x = (foo *__attribute__((foo)))*v;");
+ verifyFormat("x = (foo *_Nonnull)*v;");
+ verifyFormat("x = (foo *_Nullable)*v;");
+ verifyFormat("x = (foo *_Null_unspecified)*v;");
+ verifyFormat("x = (foo *_Nonnull)*v;");
+
+ // Check that we handle multiple trailing qualifiers and skip them all to
+ // determine that the expression is a cast to a pointer type.
+ FormatStyle LongPointerRight = getLLVMStyleWithColumns(999);
+ FormatStyle LongPointerLeft = getLLVMStyleWithColumns(999);
+ LongPointerLeft.PointerAlignment = FormatStyle::PAS_Left;
+ StringRef AllQualifiers = "const volatile restrict __attribute__((foo)) "
+ "_Nonnull _Null_unspecified _Nonnull";
+ verifyFormat(("x = (foo *" + AllQualifiers + ")*v;").str(), LongPointerRight);
+ verifyFormat(("x = (foo* " + AllQualifiers + ")*v;").str(), LongPointerLeft);
+
+ // Also check that address-of is not parsed as a binary bitwise-and:
+ verifyFormat("x = (foo *const)&v;");
+ verifyFormat(("x = (foo *" + AllQualifiers + ")&v;").str(), LongPointerRight);
+ verifyFormat(("x = (foo* " + AllQualifiers + ")&v;").str(), LongPointerLeft);
+}
+
TEST_F(FormatTest, UnderstandsSquareAttributes) {
verifyFormat("SomeType s [[unused]] (InitValue);");
verifyFormat("SomeType s [[gnu::unused]] (InitValue);");
More information about the cfe-commits
mailing list