diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1510,8 +1510,30 @@ if (Current.getPrecedence() != prec::Assignment) return false; - if (Line.First->isOneOf(tok::kw_template, tok::kw_using, tok::kw_return)) + if (Line.First->isOneOf(tok::kw_using, tok::kw_return)) return false; + if (Line.First->is(tok::kw_template)) { + // `template` keyword can start a variable template. + const FormatToken *Tok = Line.First->getNextNonComment(); + assert(Tok); // Current token is on the same line. + if (Tok->isNot(TT_TemplateOpener)) { + // Explicit template instantiations do not have `<>`. + return false; + } + + Tok = Tok->MatchingParen; + if (!Tok) + return false; + Tok = Tok->getNextNonComment(); + if (!Tok) + return false; + + if (Tok->isOneOf(tok::kw_class, tok::kw_enum, tok::kw_concept, + tok::kw_struct, tok::kw_using)) + return false; + + return true; + } // Type aliases use `type X = ...;` in TypeScript and can be exported // using `export type ...`. diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -25525,6 +25525,12 @@ Style); } +TEST_F(FormatTest, FormatsVariableTemplates) { + verifyFormat("inline bool var = is_integral_v && is_signed_v;"); + verifyFormat("template " + "inline bool var = is_integral_v && is_signed_v;"); +} + } // namespace } // namespace format } // namespace clang diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -117,6 +117,31 @@ EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_EnumLBrace); } +TEST_F(TokenAnnotatorTest, UnderstandsDefaultedAndDeletedFunctions) { + auto Tokens = annotate("auto operator<=>(const T &) const & = default;"); + EXPECT_EQ(Tokens.size(), 14u) << Tokens; + EXPECT_TOKEN(Tokens[9], tok::amp, TT_PointerOrReference); + + Tokens = annotate("template void F(T) && = delete;"); + EXPECT_EQ(Tokens.size(), 15u) << Tokens; + EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_PointerOrReference); +} + +TEST_F(TokenAnnotatorTest, UnderstandsVariables) { + auto Tokens = + annotate("inline bool var = is_integral_v && is_signed_v;"); + EXPECT_EQ(Tokens.size(), 15u) << Tokens; + EXPECT_TOKEN(Tokens[8], tok::ampamp, TT_BinaryOperator); +} + +TEST_F(TokenAnnotatorTest, UnderstandsVariableTemplates) { + auto Tokens = + annotate("template " + "inline bool var = is_integral_v && is_signed_v;"); + EXPECT_EQ(Tokens.size(), 20u) << Tokens; + EXPECT_TOKEN(Tokens[13], tok::ampamp, TT_BinaryOperator); +} + TEST_F(TokenAnnotatorTest, UnderstandsLBracesInMacroDefinition) { auto Tokens = annotate("#define BEGIN NS {"); EXPECT_EQ(Tokens.size(), 6u) << Tokens;