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);"); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits