forked from OSchip/llvm-project
[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
This commit is contained in:
parent
d304360dec
commit
96824abe7d
|
@ -439,6 +439,12 @@ public:
|
|||
(!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;
|
||||
|
||||
|
|
|
@ -1827,10 +1827,30 @@ private:
|
|||
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 @@ private:
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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);");
|
||||
|
|
Loading…
Reference in New Issue