forked from OSchip/llvm-project
[clang-format] Allow configuring list of function-like macros that resolve to a type
Summary: Adds a `TypenameMacros` configuration option that causes certain identifiers to be handled in a way similar to `typeof()`. This is enough to: - Prevent misinterpreting declarations of pointers to such types as expressions (`STACK_OF(int) * foo` -> `STACK_OF(int) *foo`), - Avoid surprising line breaks in variable/struct field declarations (`STACK_OF(int)\nfoo;` -> `STACK_OF(int) foo;`, see https://bugs.llvm.org/show_bug.cgi?id=30353). Reviewers: Typz, krasimir, djasper Reviewed By: Typz Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D57184 llvm-svn: 361986
This commit is contained in:
parent
308b7139b1
commit
c5227a1f53
|
@ -1367,6 +1367,24 @@ the configuration (without a prefix: ``Auto``).
|
|||
|
||||
For example: BOOST_FOREACH.
|
||||
|
||||
**TypenameMacros** (``std::vector<std::string>``)
|
||||
A vector of macros that should be interpreted as type declarations
|
||||
instead of as function calls.
|
||||
|
||||
These are expected to be macros of the form:
|
||||
|
||||
.. code-block: c++
|
||||
|
||||
STACK_OF(...)
|
||||
|
||||
In the .clang-format configuration file, this can be configured like:
|
||||
|
||||
.. code-block: yaml
|
||||
|
||||
TypenameMacros: ['STACK_OF', 'LIST']
|
||||
|
||||
For example: OpenSSL STACK_OF, BSD LIST_ENTRY.
|
||||
|
||||
**IncludeBlocks** (``IncludeBlocksStyle``)
|
||||
Dependent on the value, multiple ``#include`` blocks can be sorted
|
||||
as one and divided based on category.
|
||||
|
|
|
@ -1160,6 +1160,22 @@ struct FormatStyle {
|
|||
/// For example: BOOST_FOREACH.
|
||||
std::vector<std::string> ForEachMacros;
|
||||
|
||||
/// \brief A vector of macros that should be interpreted as type declarations
|
||||
/// instead of as function calls.
|
||||
///
|
||||
/// These are expected to be macros of the form:
|
||||
/// \code
|
||||
/// STACK_OF(...)
|
||||
/// \endcode
|
||||
///
|
||||
/// In the .clang-format configuration file, this can be configured like:
|
||||
/// \code{.yaml}
|
||||
/// TypenameMacros: ['STACK_OF', 'LIST']
|
||||
/// \endcode
|
||||
///
|
||||
/// For example: OpenSSL STACK_OF, BSD LIST_ENTRY.
|
||||
std::vector<std::string> TypenameMacros;
|
||||
|
||||
/// A vector of macros that should be interpreted as complete
|
||||
/// statements.
|
||||
///
|
||||
|
@ -1952,7 +1968,8 @@ struct FormatStyle {
|
|||
SpacesInParentheses == R.SpacesInParentheses &&
|
||||
SpacesInSquareBrackets == R.SpacesInSquareBrackets &&
|
||||
Standard == R.Standard && TabWidth == R.TabWidth &&
|
||||
StatementMacros == R.StatementMacros && UseTab == R.UseTab;
|
||||
StatementMacros == R.StatementMacros && UseTab == R.UseTab &&
|
||||
TypenameMacros == R.TypenameMacros;
|
||||
}
|
||||
|
||||
llvm::Optional<FormatStyle> GetLanguageStyle(LanguageKind Language) const;
|
||||
|
|
|
@ -505,6 +505,7 @@ template <> struct MappingTraits<FormatStyle> {
|
|||
IO.mapOptional("Standard", Style.Standard);
|
||||
IO.mapOptional("StatementMacros", Style.StatementMacros);
|
||||
IO.mapOptional("TabWidth", Style.TabWidth);
|
||||
IO.mapOptional("TypenameMacros", Style.TypenameMacros);
|
||||
IO.mapOptional("UseTab", Style.UseTab);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -96,6 +96,7 @@ namespace format {
|
|||
TYPE(TrailingAnnotation) \
|
||||
TYPE(TrailingReturnArrow) \
|
||||
TYPE(TrailingUnaryOperator) \
|
||||
TYPE(TypenameMacro) \
|
||||
TYPE(UnaryOperator) \
|
||||
TYPE(CSharpStringLiteral) \
|
||||
TYPE(CSharpNullCoalescing) \
|
||||
|
|
|
@ -39,6 +39,8 @@ FormatTokenLexer::FormatTokenLexer(const SourceManager &SourceMgr, FileID ID,
|
|||
Macros.insert({&IdentTable.get(ForEachMacro), TT_ForEachMacro});
|
||||
for (const std::string &StatementMacro : Style.StatementMacros)
|
||||
Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro});
|
||||
for (const std::string &TypenameMacro : Style.TypenameMacros)
|
||||
Macros.insert({&IdentTable.get(TypenameMacro), TT_TypenameMacro});
|
||||
}
|
||||
|
||||
ArrayRef<FormatToken *> FormatTokenLexer::lex() {
|
||||
|
|
|
@ -1194,11 +1194,12 @@ private:
|
|||
|
||||
// Reset token type in case we have already looked at it and then
|
||||
// recovered from an error (e.g. failure to find the matching >).
|
||||
if (!CurrentToken->isOneOf(
|
||||
TT_LambdaLSquare, TT_LambdaLBrace, TT_ForEachMacro,
|
||||
TT_FunctionLBrace, TT_ImplicitStringLiteral, TT_InlineASMBrace,
|
||||
TT_JsFatArrow, TT_LambdaArrow, TT_OverloadedOperator,
|
||||
TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral))
|
||||
if (!CurrentToken->isOneOf(TT_LambdaLSquare, TT_LambdaLBrace,
|
||||
TT_ForEachMacro, TT_TypenameMacro,
|
||||
TT_FunctionLBrace, TT_ImplicitStringLiteral,
|
||||
TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow,
|
||||
TT_OverloadedOperator, TT_RegexLiteral,
|
||||
TT_TemplateString, TT_ObjCStringLiteral))
|
||||
CurrentToken->Type = TT_Unknown;
|
||||
CurrentToken->Role.reset();
|
||||
CurrentToken->MatchingParen = nullptr;
|
||||
|
@ -1416,6 +1417,7 @@ private:
|
|||
if (AfterParen->Tok.isNot(tok::caret)) {
|
||||
if (FormatToken *BeforeParen = Current.MatchingParen->Previous)
|
||||
if (BeforeParen->is(tok::identifier) &&
|
||||
!BeforeParen->is(TT_TypenameMacro) &&
|
||||
BeforeParen->TokenText == BeforeParen->TokenText.upper() &&
|
||||
(!BeforeParen->Previous ||
|
||||
BeforeParen->Previous->ClosesTemplateDeclaration))
|
||||
|
@ -1667,7 +1669,8 @@ private:
|
|||
FormatToken *TokenBeforeMatchingParen =
|
||||
PrevToken->MatchingParen->getPreviousNonComment();
|
||||
if (TokenBeforeMatchingParen &&
|
||||
TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype))
|
||||
TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype,
|
||||
TT_TypenameMacro))
|
||||
return TT_PointerOrReference;
|
||||
}
|
||||
|
||||
|
@ -2527,7 +2530,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
|
|||
FormatToken *TokenBeforeMatchingParen =
|
||||
Left.MatchingParen->getPreviousNonComment();
|
||||
if (!TokenBeforeMatchingParen ||
|
||||
!TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype))
|
||||
!TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype,
|
||||
TT_TypenameMacro))
|
||||
return true;
|
||||
}
|
||||
return (Left.Tok.isLiteral() ||
|
||||
|
|
|
@ -13545,6 +13545,35 @@ TEST_F(FormatTest, GuessLanguageWithChildLines) {
|
|||
guessLanguage("foo.h", "#define FOO ({ foo(); ({ NSString *s; }) })"));
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, TypenameMacros) {
|
||||
std::vector<std::string> TypenameMacros = {"STACK_OF", "LIST", "TAILQ_ENTRY"};
|
||||
|
||||
// Test case reported in https://bugs.llvm.org/show_bug.cgi?id=30353
|
||||
FormatStyle Google = getGoogleStyleWithColumns(0);
|
||||
Google.TypenameMacros = TypenameMacros;
|
||||
verifyFormat("struct foo {\n"
|
||||
" int bar;\n"
|
||||
" TAILQ_ENTRY(a) bleh;\n"
|
||||
"};", Google);
|
||||
|
||||
FormatStyle Macros = getLLVMStyle();
|
||||
Macros.TypenameMacros = TypenameMacros;
|
||||
|
||||
verifyFormat("STACK_OF(int) a;", Macros);
|
||||
verifyFormat("STACK_OF(int) *a;", Macros);
|
||||
verifyFormat("STACK_OF(int const *) *a;", Macros);
|
||||
verifyFormat("STACK_OF(int *const) *a;", Macros);
|
||||
verifyFormat("STACK_OF(int, string) a;", Macros);
|
||||
verifyFormat("STACK_OF(LIST(int)) a;", Macros);
|
||||
verifyFormat("STACK_OF(LIST(int)) a, b;", Macros);
|
||||
verifyFormat("for (LIST(int) *a = NULL; a;) {\n}", Macros);
|
||||
verifyFormat("STACK_OF(int) f(LIST(int) *arg);", Macros);
|
||||
|
||||
Macros.PointerAlignment = FormatStyle::PAS_Left;
|
||||
verifyFormat("STACK_OF(int)* a;", Macros);
|
||||
verifyFormat("STACK_OF(int*)* a;", Macros);
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
} // end namespace format
|
||||
} // end namespace clang
|
||||
|
|
Loading…
Reference in New Issue