[clang-format] Fix a crash (assertion) in qualifier alignment when matching template...
authormydeveloperday <mydeveloperday@gmail.com>
Thu, 6 Jan 2022 19:39:24 +0000 (19:39 +0000)
committermydeveloperday <mydeveloperday@gmail.com>
Thu, 6 Jan 2022 19:40:39 +0000 (19:40 +0000)
https://github.com/llvm/llvm-project/issues/53008

```
template <class Id> using A = quantity /**/<kind<Id>, 1>;
```

the presence of the comment between identifier and template opener seems to be causing the qualifier alignment to fail

Reviewed By: curdeius

Fixes: #53008

Differential Revision: https://reviews.llvm.org/D116726

clang/lib/Format/QualifierAlignmentFixer.cpp
clang/lib/Format/QualifierAlignmentFixer.h
clang/unittests/Format/QualifierFixerTest.cpp

index 5a89225..ec19a38 100644 (file)
@@ -204,9 +204,9 @@ static void rotateTokens(const SourceManager &SourceMgr,
   replaceToken(SourceMgr, Fixes, Range, NewText);
 }
 
-FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
+const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
     const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
-    tooling::Replacements &Fixes, FormatToken *Tok,
+    tooling::Replacements &Fixes, const FormatToken *Tok,
     const std::string &Qualifier, tok::TokenKind QualifierType) {
   // We only need to think about streams that begin with a qualifier.
   if (!Tok->is(QualifierType))
@@ -281,16 +281,16 @@ FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
   return Tok;
 }
 
-FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
+const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
     const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
-    tooling::Replacements &Fixes, FormatToken *Tok,
+    tooling::Replacements &Fixes, const FormatToken *Tok,
     const std::string &Qualifier, tok::TokenKind QualifierType) {
   // if Tok is an identifier and possibly a macro then don't convert.
   if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok))
     return Tok;
 
-  FormatToken *Qual = Tok;
-  FormatToken *LastQual = Qual;
+  const FormatToken *Qual = Tok;
+  const FormatToken *LastQual = Qual;
   while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
     LastQual = Qual;
     Qual = Qual->Next;
@@ -326,7 +326,7 @@ FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
         Tok->Previous->isOneOf(tok::star, tok::ampamp, tok::amp)) {
       return Tok;
     }
-    FormatToken *Next = Tok->Next;
+    const FormatToken *Next = Tok->Next;
     // The case  `std::Foo<T> const` -> `const std::Foo<T> &&`
     while (Next && Next->isOneOf(tok::identifier, tok::coloncolon))
       Next = Next->Next;
@@ -334,6 +334,8 @@ FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
         Next->Previous->startsSequence(tok::identifier, TT_TemplateOpener)) {
       // Read from to the end of the TemplateOpener to
       // TemplateCloser const ArrayRef<int> a; const ArrayRef<int> &a;
+      if (Next->is(tok::comment) && Next->getNextNonComment())
+        Next = Next->getNextNonComment();
       assert(Next->MatchingParen && "Missing template closer");
       Next = Next->MatchingParen->Next;
 
@@ -398,7 +400,8 @@ LeftRightQualifierAlignmentFixer::analyze(
     FormatToken *First = AnnotatedLines[I]->First;
     const auto *Last = AnnotatedLines[I]->Last;
 
-    for (auto *Tok = First; Tok && Tok != Last && Tok->Next; Tok = Tok->Next) {
+    for (const auto *Tok = First; Tok && Tok != Last && Tok->Next;
+         Tok = Tok->Next) {
       if (Tok->is(tok::comment))
         continue;
       if (RightAlign)
index 7abd256..30ef96b 100644 (file)
@@ -72,17 +72,19 @@ public:
 
   static tok::TokenKind getTokenFromQualifier(const std::string &Qualifier);
 
-  FormatToken *analyzeRight(const SourceManager &SourceMgr,
-                            const AdditionalKeywords &Keywords,
-                            tooling::Replacements &Fixes, FormatToken *Tok,
-                            const std::string &Qualifier,
-                            tok::TokenKind QualifierType);
-
-  FormatToken *analyzeLeft(const SourceManager &SourceMgr,
-                           const AdditionalKeywords &Keywords,
-                           tooling::Replacements &Fixes, FormatToken *Tok,
-                           const std::string &Qualifier,
-                           tok::TokenKind QualifierType);
+  const FormatToken *analyzeRight(const SourceManager &SourceMgr,
+                                  const AdditionalKeywords &Keywords,
+                                  tooling::Replacements &Fixes,
+                                  const FormatToken *Tok,
+                                  const std::string &Qualifier,
+                                  tok::TokenKind QualifierType);
+
+  const FormatToken *analyzeLeft(const SourceManager &SourceMgr,
+                                 const AdditionalKeywords &Keywords,
+                                 tooling::Replacements &Fixes,
+                                 const FormatToken *Tok,
+                                 const std::string &Qualifier,
+                                 tok::TokenKind QualifierType);
 
   // is the Token a simple or qualifier type
   static bool isQualifierOrType(const FormatToken *Tok,
index e3d0a9a..0517de2 100755 (executable)
@@ -818,5 +818,45 @@ TEST_F(QualifierFixerTest, NoOpQualifierReplacements) {
   EXPECT_EQ(ReplacementCount, 0);
 }
 
+TEST_F(QualifierFixerTest, QualifierTemplates) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Custom;
+  Style.QualifierOrder = {"static", "const", "type"};
+
+  ReplacementCount = 0;
+  EXPECT_EQ(ReplacementCount, 0);
+  verifyFormat("using A = B<>;", Style);
+  verifyFormat("using A = B /**/<>;", Style);
+  verifyFormat("template <class C> using A = B<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /**/<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /* */<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /*foo*/<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /**/ /**/<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B<Foo</**/ C>, 1>;", Style);
+  verifyFormat("template <class C> using A = /**/ B<Foo<C>, 1>;", Style);
+  EXPECT_EQ(ReplacementCount, 0);
+  verifyFormat("template <class C>\n"
+               "using A = B // foo\n"
+               "    <Foo<C>, 1>;",
+               Style);
+
+  ReplacementCount = 0;
+  Style.QualifierOrder = {"type", "static", "const"};
+  verifyFormat("using A = B<>;", Style);
+  verifyFormat("using A = B /**/<>;", Style);
+  verifyFormat("template <class C> using A = B<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /**/<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /* */<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /*foo*/<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B /**/ /**/<Foo<C>, 1>;", Style);
+  verifyFormat("template <class C> using A = B<Foo</**/ C>, 1>;", Style);
+  verifyFormat("template <class C> using A = /**/ B<Foo<C>, 1>;", Style);
+  EXPECT_EQ(ReplacementCount, 0);
+  verifyFormat("template <class C>\n"
+               "using A = B // foo\n"
+               "    <Foo<C>, 1>;",
+               Style);
+}
+
 } // namespace format
 } // namespace clang