(!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;
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)
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;
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);");