benhamilton created this revision.
benhamilton added reviewers: krasimir, jolesiak.
Herald added a subscriber: cfe-commits.
Previously, clang-format would detect C++11 and C++17 attribute
specifiers like the following as Objective-C method invocations:
[[noreturn]];
[[clang::fallthrough]];
[[noreturn, deprecated("so sorry")]];
[[using gsl: suppress("type")]];
To fix this, I ported part of the logic from
tools/clang/lib/Parse/ParseTentative.cpp into TokenAnnotator.cpp so we
can explicitly parse and identify C++11 attribute specifiers.
This allows the guessLanguage() and getStyle() APIs to correctly
guess files containing the C++11 attribute specifiers as C++,
not Objective-C.
Test Plan: New tests added. Ran tests with:
make -j12 FormatTests && ./tools/clang/unittests/Format/FormatTests
Repository:
rC Clang
https://reviews.llvm.org/D43902
Files:
lib/Format/FormatToken.h
lib/Format/TokenAnnotator.cpp
unittests/Format/FormatTest.cpp
Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -11969,6 +11969,36 @@
EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo", "@interface Foo\n@end\n"));
}
+TEST_F(FormatTest, GuessLanguageWithCpp11AttributeSpecifiers) {
+ EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[noreturn]];"));
+ EXPECT_EQ(FormatStyle::LK_ObjC,
+ guessLanguage("foo.h", "array[[calculator getIndex]];"));
+ EXPECT_EQ(FormatStyle::LK_Cpp,
+ guessLanguage("foo.h", "[[noreturn, deprecated(\"so sorry\")]];"));
+ EXPECT_EQ(
+ FormatStyle::LK_Cpp,
+ guessLanguage("foo.h", "[[noreturn, deprecated(\"gone, sorry\")]];"));
+ EXPECT_EQ(FormatStyle::LK_ObjC,
+ guessLanguage("foo.h", "[[noreturn foo] bar];"));
+ EXPECT_EQ(FormatStyle::LK_Cpp,
+ guessLanguage("foo.h", "[[clang::fallthrough]];"));
+ EXPECT_EQ(FormatStyle::LK_ObjC,
+ guessLanguage("foo.h", "[[clang:fallthrough] foo];"));
+ EXPECT_EQ(FormatStyle::LK_Cpp,
+ guessLanguage("foo.h", "[[gsl::suppress(\"type\")]];"));
+ EXPECT_EQ(FormatStyle::LK_Cpp,
+ guessLanguage("foo.h", "[[using clang: fallthrough]];"));
+ EXPECT_EQ(FormatStyle::LK_ObjC,
+ guessLanguage("foo.h", "[[abusing clang:fallthrough] bar];"));
+ EXPECT_EQ(FormatStyle::LK_Cpp,
+ guessLanguage("foo.h", "[[using gsl: suppress(\"type\")]];"));
+ EXPECT_EQ(
+ FormatStyle::LK_Cpp,
+ guessLanguage("foo.h",
+ "[[clang::callable_when(\"unconsumed\", \"unknown\")]]"));
+ EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[foo::bar, ...]]"));
+}
+
} // end namespace
} // end namespace format
} // end namespace clang
Index: lib/Format/TokenAnnotator.cpp
===================================================================
--- lib/Format/TokenAnnotator.cpp
+++ lib/Format/TokenAnnotator.cpp
@@ -320,13 +320,65 @@
return false;
}
+ const FormatToken *parseCpp11Attribute(const FormatToken *Tok,
+ bool NamespaceAllowed) {
+ if (!Tok || !Tok->isOneOf(tok::identifier, tok::ellipsis)) return Tok;
+ Tok = Tok->Next;
+ if (!Tok) return nullptr;
+ if (NamespaceAllowed &&
+ Tok->startsSequence(tok::coloncolon, tok::identifier)) {
+ Tok = Tok->Next->Next;
+ }
+ if (!Tok) return nullptr;
+ if (Tok->is(tok::l_paren)) {
+ const FormatToken *ParamToken = Tok->Next;
+ while (ParamToken && ParamToken->isNot(tok::r_paren))
+ ParamToken = ParamToken->Next;
+ if (!ParamToken || ParamToken->isNot(tok::r_paren)) return nullptr;
+ Tok = ParamToken->Next;
+ }
+ return Tok;
+ }
+
+ // Look for [[ ... ]] which is a valid C++11 attribute specifier but
+ // never a valid Objective-C or Objective-C++ method invocation.
+ bool parseCpp11AttributeSpecifier(FormatToken *Tok) {
+ if (!Style.isCpp()) return false;
+ if (!Tok || !Tok->startsSequence(tok::l_square, tok::l_square))
+ return false;
+ const FormatToken *AttributeToken = Tok->Next->Next;
+ if (!AttributeToken) return false;
+ // C++17 '[[using namespace: foo, bar(baz, blech)]]'
+ if (AttributeToken->startsSequence(tok::kw_using, tok::identifier,
+ tok::colon)) {
+ AttributeToken = AttributeToken->Next->Next->Next;
+ while (AttributeToken) {
+ AttributeToken =
+ parseCpp11Attribute(AttributeToken, /*NamespaceAllowed=*/false);
+ if (!AttributeToken || AttributeToken->isNot(tok::comma)) break;
+ AttributeToken = AttributeToken->Next;
+ }
+ } else {
+ // C++11 '[[namespace::foo, namespace::bar(baz, blech)]]'
+ while (AttributeToken) {
+ AttributeToken =
+ parseCpp11Attribute(AttributeToken, /*NamespaceAllowed=*/true);
+ if (!AttributeToken || AttributeToken->isNot(tok::comma)) break;
+ AttributeToken = AttributeToken->Next;
+ }
+ }
+ if (!AttributeToken) return false;
+ return AttributeToken->startsSequence(tok::r_square, tok::r_square);
+ }
+
bool parseSquare() {
if (!CurrentToken)
return false;
// A '[' could be an index subscript (after an identifier or after
// ')' or ']'), it could be the start of an Objective-C method
- // expression, or it could the start of an Objective-C array literal.
+ // expression, it could the start of an Objective-C array literal,
+ // or it could be a C++ attribute specifier [[foo::bar]].
FormatToken *Left = CurrentToken->Previous;
Left->ParentBracket = Contexts.back().ContextKind;
FormatToken *Parent = Left->getPreviousNonComment();
@@ -339,6 +391,12 @@
(Contexts.back().CanBeExpression || Contexts.back().IsExpression ||
Contexts.back().InTemplateArgument);
+ if (parseCpp11AttributeSpecifier(Left)) {
+ CurrentToken->Type = TT_AttributeSpecifier;
+ next();
+ return true;
+ }
+
bool StartsObjCMethodExpr =
!CppArrayTemplates && Style.isCpp() &&
Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) &&
Index: lib/Format/FormatToken.h
===================================================================
--- lib/Format/FormatToken.h
+++ lib/Format/FormatToken.h
@@ -30,6 +30,7 @@
TYPE(ArrayInitializerLSquare) \
TYPE(ArraySubscriptLSquare) \
TYPE(AttributeParen) \
+ TYPE(AttributeSpecifier) \
TYPE(BinaryOperator) \
TYPE(BitFieldColon) \
TYPE(BlockComment) \
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits