Skip to content

Commit

Permalink
[clang-format] Add TemplateNames option to help parse C++ angles (llv…
Browse files Browse the repository at this point in the history
  • Loading branch information
owenca authored Oct 3, 2024
1 parent 98281da commit 688bc95
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 17 deletions.
9 changes: 9 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6554,6 +6554,15 @@ the configuration (without a prefix: ``Auto``).
let DAGArgOtherID = (other i32:$other1, i32:$other2);
let DAGArgBang = (!cast<SomeType>("Some") i32:$src1, i32:$src2)

.. _TemplateNames:

**TemplateNames** (``List of Strings``) :versionbadge:`clang-format 20` :ref:`<TemplateNames>`
A vector of non-keyword identifiers that should be interpreted as
template names.

A ``<`` after a template name is annotated as a template opener instead of
a binary operator.

.. _TypeNames:

**TypeNames** (``List of Strings``) :versionbadge:`clang-format 17` :ref:`<TypeNames>`
Expand Down
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ clang-format
------------

- Adds ``BreakBinaryOperations`` option.
- Adds ``TemplateNames`` option.

libclang
--------
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -4974,6 +4974,15 @@ struct FormatStyle {
/// \version 3.7
unsigned TabWidth;

/// A vector of non-keyword identifiers that should be interpreted as
/// template names.
///
/// A ``<`` after a template name is annotated as a template opener instead of
/// a binary operator.
///
/// \version 20
std::vector<std::string> TemplateNames;

/// A vector of non-keyword identifiers that should be interpreted as type
/// names.
///
Expand Down Expand Up @@ -5230,6 +5239,7 @@ struct FormatStyle {
TableGenBreakingDAGArgOperators ==
R.TableGenBreakingDAGArgOperators &&
TableGenBreakInsideDAGArg == R.TableGenBreakInsideDAGArg &&
TabWidth == R.TabWidth && TemplateNames == R.TemplateNames &&
TabWidth == R.TabWidth && TypeNames == R.TypeNames &&
TypenameMacros == R.TypenameMacros && UseTab == R.UseTab &&
VerilogBreakBetweenInstancePorts ==
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("TableGenBreakInsideDAGArg",
Style.TableGenBreakInsideDAGArg);
IO.mapOptional("TabWidth", Style.TabWidth);
IO.mapOptional("TemplateNames", Style.TemplateNames);
IO.mapOptional("TypeNames", Style.TypeNames);
IO.mapOptional("TypenameMacros", Style.TypenameMacros);
IO.mapOptional("UseTab", Style.UseTab);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ namespace format {
TYPE(TrailingReturnArrow) \
TYPE(TrailingUnaryOperator) \
TYPE(TypeDeclarationParen) \
TYPE(TemplateName) \
TYPE(TypeName) \
TYPE(TypenameMacro) \
TYPE(UnaryOperator) \
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Format/FormatTokenLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ FormatTokenLexer::FormatTokenLexer(
Macros.insert({Identifier, TT_StatementAttributeLikeMacro});
}

for (const auto &TemplateName : Style.TemplateNames)
TemplateNames.insert(&IdentTable.get(TemplateName));
for (const auto &TypeName : Style.TypeNames)
TypeNames.insert(&IdentTable.get(TypeName));
}
Expand Down Expand Up @@ -1368,6 +1370,8 @@ FormatToken *FormatTokenLexer::getNextToken() {
FormatTok->setType(TT_MacroBlockBegin);
else if (MacroBlockEndRegex.match(Text))
FormatTok->setType(TT_MacroBlockEnd);
else if (TemplateNames.contains(Identifier))
FormatTok->setFinalizedType(TT_TemplateName);
else if (TypeNames.contains(Identifier))
FormatTok->setFinalizedType(TT_TypeName);
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/FormatTokenLexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class FormatTokenLexer {

llvm::SmallMapVector<IdentifierInfo *, TokenType, 8> Macros;

llvm::SmallPtrSet<IdentifierInfo *, 8> TypeNames;
llvm::SmallPtrSet<IdentifierInfo *, 8> TemplateNames, TypeNames;

bool FormattingDisabled;

Expand Down
36 changes: 20 additions & 16 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,36 +149,36 @@ class AnnotatingParser {
}

bool parseAngle() {
if (!CurrentToken || !CurrentToken->Previous)
if (!CurrentToken)
return false;

auto *Left = CurrentToken->Previous; // The '<'.
if (!Left)
return false;
if (NonTemplateLess.count(CurrentToken->Previous) > 0)

if (NonTemplateLess.count(Left) > 0)
return false;

if (const auto &Previous = *CurrentToken->Previous; // The '<'.
Previous.Previous) {
if (Previous.Previous->Tok.isLiteral())
const auto *BeforeLess = Left->Previous;

if (BeforeLess) {
if (BeforeLess->Tok.isLiteral())
return false;
if (Previous.Previous->is(tok::r_brace))
if (BeforeLess->is(tok::r_brace))
return false;
if (Previous.Previous->is(tok::r_paren) && Contexts.size() > 1 &&
(!Previous.Previous->MatchingParen ||
Previous.Previous->MatchingParen->isNot(
TT_OverloadedOperatorLParen))) {
if (BeforeLess->is(tok::r_paren) && Contexts.size() > 1 &&
!(BeforeLess->MatchingParen &&
BeforeLess->MatchingParen->is(TT_OverloadedOperatorLParen))) {
return false;
}
if (Previous.Previous->is(tok::kw_operator) &&
CurrentToken->is(tok::l_paren)) {
if (BeforeLess->is(tok::kw_operator) && CurrentToken->is(tok::l_paren))
return false;
}
}

FormatToken *Left = CurrentToken->Previous;
Left->ParentBracket = Contexts.back().ContextKind;
ScopedContextCreator ContextCreator(*this, tok::less, 12);
Contexts.back().IsExpression = false;

const auto *BeforeLess = Left->Previous;

// If there's a template keyword before the opening angle bracket, this is a
// template parameter, not an argument.
if (BeforeLess && BeforeLess->isNot(tok::kw_template))
Expand Down Expand Up @@ -229,6 +229,10 @@ class AnnotatingParser {
next();
return true;
}
if (BeforeLess && BeforeLess->is(TT_TemplateName)) {
next();
continue;
}
if (CurrentToken->is(tok::question) &&
Style.Language == FormatStyle::LK_Java) {
next();
Expand Down
17 changes: 17 additions & 0 deletions clang/unittests/Format/TokenAnnotatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3516,6 +3516,23 @@ TEST_F(TokenAnnotatorTest, SplitPenalty) {
EXPECT_SPLIT_PENALTY(Tokens[7], 23u);
}

TEST_F(TokenAnnotatorTest, TemplateName) {
constexpr StringRef Code{"return Foo < A || B > (C ^ D);"};

auto Tokens = annotate(Code);
ASSERT_EQ(Tokens.size(), 14u) << Tokens;
EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator);
EXPECT_TOKEN(Tokens[6], tok::greater, TT_BinaryOperator);

auto Style = getLLVMStyle();
Style.TemplateNames.push_back("Foo");

Tokens = annotate(Code, Style);
EXPECT_TOKEN(Tokens[1], tok::identifier, TT_TemplateName);
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[6], tok::greater, TT_TemplateCloser);
}

} // namespace
} // namespace format
} // namespace clang

0 comments on commit 688bc95

Please sign in to comment.