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 @@ -1526,8 +1526,36 @@ 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)) { + assert(Current.Previous); + if (Current.Previous->is(tok::kw_operator)) { + // `template ... operator=` cannot be an expression. + return false; + } + + // `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 @@ -25657,6 +25657,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 @@ -131,6 +131,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; @@ -164,17 +189,17 @@ EXPECT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[4], tok::amp, TT_PointerOrReference); - Tokens = annotate("void operator=() &&;"); - EXPECT_EQ(Tokens.size(), 8u) << Tokens; - EXPECT_TOKEN(Tokens[5], tok::ampamp, TT_PointerOrReference); + Tokens = annotate("void operator=(T) &&;"); + EXPECT_EQ(Tokens.size(), 9u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::ampamp, TT_PointerOrReference); Tokens = annotate("template void f() &;"); EXPECT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::amp, TT_PointerOrReference); - Tokens = annotate("template void operator=() &;"); - EXPECT_EQ(Tokens.size(), 13u) << Tokens; - EXPECT_TOKEN(Tokens[10], tok::amp, TT_PointerOrReference); + Tokens = annotate("template void operator=(T) &;"); + EXPECT_EQ(Tokens.size(), 14u) << Tokens; + EXPECT_TOKEN(Tokens[11], tok::amp, TT_PointerOrReference); } TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {