Skip to content

[clang-format] Add TemplateNames option to help parse C++ angles #109916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -532,6 +532,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 @@ -3505,6 +3505,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
Loading