[clang] [clang-format] Add basic support for C++/CLI (#27126) (PR #112689)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 17 03:50:07 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Calum Robinson (calumr)

<details>
<summary>Changes</summary>

* This only looks for ^ as a pointer/reference token, which is the main issue when trying to format C++/CLI.

Obviously there's more to C++/CLI than this minor change, but it's extremely useful to be able to clang-format our whole codebase now without a ^ mangling the output.

---
Full diff: https://github.com/llvm/llvm-project/pull/112689.diff


7 Files Affected:

- (modified) clang/include/clang/Basic/LangOptions.def (+1) 
- (modified) clang/lib/Format/Format.cpp (+1) 
- (modified) clang/lib/Format/FormatToken.h (+3-1) 
- (modified) clang/lib/Format/QualifierAlignmentFixer.cpp (+1-1) 
- (modified) clang/lib/Format/TokenAnnotator.cpp (+18-16) 
- (modified) clang/lib/Format/WhitespaceManager.cpp (+13-11) 
- (modified) clang/lib/Format/WhitespaceManager.h (+3-1) 


``````````diff
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 68db400c22e6c1..1285ce06d845f3 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -101,6 +101,7 @@ LANGOPT(CPlusPlus17       , 1, 0, "C++17")
 LANGOPT(CPlusPlus20       , 1, 0, "C++20")
 LANGOPT(CPlusPlus23       , 1, 0, "C++23")
 LANGOPT(CPlusPlus26       , 1, 0, "C++26")
+LANGOPT(CPlusPlusCLI      , 1, 0, "C++/CLI")
 LANGOPT(ObjC              , 1, 0, "Objective-C")
 BENIGN_LANGOPT(ObjCDefaultSynthProperties , 1, 0,
                "Objective-C auto-synthesized properties")
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 148270795c562f..4344b5b2ad4c26 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3914,6 +3914,7 @@ LangOptions getFormattingLangOpts(const FormatStyle &Style) {
   LangOpts.Bool = 1;
   LangOpts.ObjC = 1;
   LangOpts.MicrosoftExt = 1;    // To get kw___try, kw___finally.
+  LangOpts.CPlusPlusCLI = 1;
   LangOpts.DeclSpecKeyword = 1; // To get __declspec.
   LangOpts.C99 = 1; // To get kw_restrict for non-underscore-prefixed restrict.
   return LangOpts;
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 7d342a7dcca01d..cdf613774fa6bc 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -731,7 +731,9 @@ struct FormatToken {
                     TT_LambdaArrow, TT_LeadingJavaAnnotation);
   }
 
-  bool isPointerOrReference() const {
+  bool isPointerOrReference(const LangOptions &LangOpts) const {
+    if (LangOpts.CPlusPlusCLI && is(tok::caret))
+      return true;
     return isOneOf(tok::star, tok::amp, tok::ampamp);
   }
 
diff --git a/clang/lib/Format/QualifierAlignmentFixer.cpp b/clang/lib/Format/QualifierAlignmentFixer.cpp
index 593f8efff25aa9..f07c8913b907ac 100644
--- a/clang/lib/Format/QualifierAlignmentFixer.cpp
+++ b/clang/lib/Format/QualifierAlignmentFixer.cpp
@@ -385,7 +385,7 @@ const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
 
   // For left qualifiers preceeded by nothing, a template declaration, or *,&,&&
   // we only perform sorting.
-  if (!TypeToken || TypeToken->isPointerOrReference() ||
+  if (!TypeToken || TypeToken->isPointerOrReference(LangOpts) ||
       TypeToken->ClosesRequiresClause || TypeToken->ClosesTemplateDeclaration) {
 
     // Don't sort past a non-configured qualifier token.
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index fcefaa7bb298ea..5a33c1c00c8b24 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -468,8 +468,8 @@ class AnnotatingParser {
     // void (&&FunctionReference)(void);
     // void (^ObjCBlock)(void);
     bool MightBeFunctionType = !Contexts[Contexts.size() - 2].IsExpression;
-    bool ProbablyFunctionType =
-        CurrentToken->isPointerOrReference() || CurrentToken->is(tok::caret);
+    bool ProbablyFunctionType = CurrentToken->isPointerOrReference(LangOpts) ||
+                                CurrentToken->is(tok::caret);
     bool HasMultipleLines = false;
     bool HasMultipleParametersOnALine = false;
     bool MightBeObjCForRangeLoop =
@@ -507,7 +507,8 @@ class AnnotatingParser {
           //   auto my_lambda = MACRO((Type *type, int i) { .. body .. });
           for (FormatToken *Tok = &OpeningParen; Tok != CurrentToken;
                Tok = Tok->Next) {
-            if (Tok->is(TT_BinaryOperator) && Tok->isPointerOrReference())
+            if (Tok->is(TT_BinaryOperator) &&
+                Tok->isPointerOrReference(LangOpts))
               Tok->setType(TT_PointerOrReference);
           }
         }
@@ -578,7 +579,7 @@ class AnnotatingParser {
              Tok != CurrentToken &&
              !Tok->isOneOf(tok::equal, tok::l_paren, tok::l_brace);
              Tok = Tok->Next) {
-          if (Tok->isPointerOrReference())
+          if (Tok->isPointerOrReference(LangOpts))
             Tok->setFinalizedType(TT_PointerOrReference);
         }
       }
@@ -1411,7 +1412,7 @@ class AnnotatingParser {
         for (auto *Prev = Tok->Previous;
              Prev && !Prev->isOneOf(tok::semi, tok::l_paren);
              Prev = Prev->Previous) {
-          if (Prev->isPointerOrReference())
+          if (Prev->isPointerOrReference(LangOpts))
             Prev->setFinalizedType(TT_PointerOrReference);
         }
       } else if (Contexts.back().ContextType == Context::C11GenericSelection) {
@@ -2244,7 +2245,7 @@ class AnnotatingParser {
           if (Previous->opensScope())
             break;
           if (Previous->isOneOf(TT_BinaryOperator, TT_UnaryOperator) &&
-              Previous->isPointerOrReference() && Previous->Previous &&
+              Previous->isPointerOrReference(LangOpts) && Previous->Previous &&
               Previous->Previous->isNot(tok::equal)) {
             Previous->setType(TT_PointerOrReference);
           }
@@ -2410,7 +2411,7 @@ class AnnotatingParser {
     } else if (isDeductionGuide(Current)) {
       // Deduction guides trailing arrow " A(...) -> A<T>;".
       Current.setType(TT_TrailingReturnArrow);
-    } else if (Current.isPointerOrReference()) {
+    } else if (Current.isPointerOrReference(LangOpts)) {
       Current.setType(determineStarAmpUsage(
           Current,
           Contexts.back().CanBeExpression && Contexts.back().IsExpression,
@@ -2580,7 +2581,7 @@ class AnnotatingParser {
     if (const auto *NextNonComment = Tok.getNextNonComment();
         (!NextNonComment && !Line.InMacroBody) ||
         (NextNonComment &&
-         (NextNonComment->isPointerOrReference() ||
+         (NextNonComment->isPointerOrReference(LangOpts) ||
           NextNonComment->is(tok::string_literal) ||
           (Line.InPragmaDirective && NextNonComment->is(tok::identifier))))) {
       return false;
@@ -3767,7 +3768,7 @@ static bool isFunctionDeclarationName(const LangOptions &LangOpts,
         continue;
       }
       if ((Next->isTypeName(LangOpts) || Next->is(tok::identifier)) &&
-          Next->Next && Next->Next->isPointerOrReference()) {
+          Next->Next && Next->Next->isPointerOrReference(LangOpts)) {
         // For operator void*(), operator char*(), operator Foo*().
         Next = Next->Next;
         continue;
@@ -3797,7 +3798,8 @@ static bool isFunctionDeclarationName(const LangOptions &LangOpts,
       assert(Previous.MatchingParen->is(TT_TypeDeclarationParen));
       return true;
     }
-    if (!Previous.isPointerOrReference() && Previous.isNot(TT_TemplateCloser))
+    if (!Previous.isPointerOrReference(LangOpts) &&
+        Previous.isNot(TT_TemplateCloser))
       return false;
     Next = skipOperatorName(Next);
   } else {
@@ -3983,7 +3985,7 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
             continue;
           auto *Next = Tok->Next;
           const bool NextIsBinaryOperator =
-              Next && Next->isPointerOrReference() && Next->Next &&
+              Next && Next->isPointerOrReference(LangOpts) && Next->Next &&
               Next->Next->is(tok::identifier);
           if (!NextIsBinaryOperator)
             continue;
@@ -4632,15 +4634,15 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
   }
   // Ensure right pointer alignment with ellipsis e.g. int *...P
   if (Left.is(tok::ellipsis) && BeforeLeft &&
-      BeforeLeft->isPointerOrReference()) {
+      BeforeLeft->isPointerOrReference(LangOpts)) {
     return Style.PointerAlignment != FormatStyle::PAS_Right;
   }
 
   if (Right.is(tok::star) && Left.is(tok::l_paren))
     return false;
-  if (Left.is(tok::star) && Right.isPointerOrReference())
+  if (Left.is(tok::star) && Right.isPointerOrReference(LangOpts))
     return false;
-  if (Right.isPointerOrReference()) {
+  if (Right.isPointerOrReference(LangOpts)) {
     const FormatToken *Previous = &Left;
     while (Previous && Previous->isNot(tok::kw_operator)) {
       if (Previous->is(tok::identifier) || Previous->isTypeName(LangOpts)) {
@@ -5877,7 +5879,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
   }
 
   if (Style.BraceWrapping.BeforeLambdaBody && Right.is(TT_LambdaLBrace) &&
-      (Left.isPointerOrReference() || Left.is(TT_TemplateCloser))) {
+      (Left.isPointerOrReference(LangOpts) || Left.is(TT_TemplateCloser))) {
     return true;
   }
 
@@ -6431,7 +6433,7 @@ TokenAnnotator::getTokenPointerOrReferenceAlignment(
       return FormatStyle::PAS_Middle;
     }
   }
-  assert(PointerOrReference.is(tok::star));
+  assert(LangOpts.CPlusPlusCLI ? PointerOrReference.isOneOf(tok::star, tok::caret) : PointerOrReference.is(tok::star));
   return Style.PointerAlignment;
 }
 
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index b6b24672f6a39d..ae2284528f9fc0 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -284,8 +284,9 @@ void WhitespaceManager::calculateLineBreakInformation() {
 // moved to that column.
 template <typename F>
 static void
-AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
-                   unsigned Column, bool RightJustify, F &&Matches,
+AlignTokenSequence(const FormatStyle &Style, const LangOptions &LangOpts,
+                   unsigned Start, unsigned End, unsigned Column,
+                   bool RightJustify, F &&Matches,
                    SmallVector<WhitespaceManager::Change, 16> &Changes) {
   bool FoundMatchOnLine = false;
   int Shift = 0;
@@ -478,7 +479,7 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
       for (int Previous = i - 1;
            Previous >= 0 && Changes[Previous].Tok->is(TT_PointerOrReference);
            --Previous) {
-        assert(Changes[Previous].Tok->isPointerOrReference());
+        assert(Changes[Previous].Tok->isPointerOrReference(LangOpts));
         if (Changes[Previous].Tok->isNot(tok::star)) {
           if (ReferenceNotRightAligned)
             continue;
@@ -525,7 +526,8 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
 // When RightJustify and ACS.PadOperators are true, operators in each block to
 // be aligned will be padded on the left to the same length before aligning.
 template <typename F>
-static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
+static unsigned AlignTokens(const FormatStyle &Style,
+                            const LangOptions &LangOpts, F &&Matches,
                             SmallVector<WhitespaceManager::Change, 16> &Changes,
                             unsigned StartAt,
                             const FormatStyle::AlignConsecutiveStyle &ACS = {},
@@ -577,7 +579,7 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
   // containing any matching token to be aligned and located after such token.
   auto AlignCurrentSequence = [&] {
     if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {
-      AlignTokenSequence(Style, StartOfSequence, EndOfSequence,
+      AlignTokenSequence(Style, LangOpts, StartOfSequence, EndOfSequence,
                          WidthLeft + WidthAnchor, RightJustify, Matches,
                          Changes);
     }
@@ -627,7 +629,7 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
     } else if (CurrentChange.indentAndNestingLevel() > IndentAndNestingLevel) {
       // Call AlignTokens recursively, skipping over this scope block.
       unsigned StoppedAt =
-          AlignTokens(Style, Matches, Changes, i, ACS, RightJustify);
+          AlignTokens(Style, LangOpts, Matches, Changes, i, ACS, RightJustify);
       i = StoppedAt - 1;
       continue;
     }
@@ -828,7 +830,7 @@ void WhitespaceManager::alignConsecutiveAssignments() {
     return;
 
   AlignTokens(
-      Style,
+      Style, LangOpts,
       [&](const Change &C) {
         // Do not align on equal signs that are first on a line.
         if (C.NewlinesBefore > 0)
@@ -866,7 +868,7 @@ void WhitespaceManager::alignConsecutiveColons(
     return;
 
   AlignTokens(
-      Style,
+      Style, LangOpts,
       [&](Change const &C) {
         // Do not align on ':' that is first on a line.
         if (C.NewlinesBefore > 0)
@@ -1010,7 +1012,7 @@ void WhitespaceManager::alignConsecutiveDeclarations() {
     return;
 
   AlignTokens(
-      Style,
+      Style, LangOpts,
       [&](Change const &C) {
         if (Style.AlignConsecutiveDeclarations.AlignFunctionPointers) {
           for (const auto *Prev = C.Tok->Previous; Prev; Prev = Prev->Previous)
@@ -1047,7 +1049,7 @@ void WhitespaceManager::alignConsecutiveDeclarations() {
 void WhitespaceManager::alignChainedConditionals() {
   if (Style.BreakBeforeTernaryOperators) {
     AlignTokens(
-        Style,
+        Style, LangOpts,
         [](Change const &C) {
           // Align question operators and last colon
           return C.Tok->is(TT_ConditionalExpr) &&
@@ -1072,7 +1074,7 @@ void WhitespaceManager::alignChainedConditionals() {
       if (AlignWrappedOperand(C))
         C.StartOfTokenColumn -= 2;
     AlignTokens(
-        Style,
+        Style, LangOpts,
         [this](Change const &C) {
           // Align question operators if next operand is not wrapped, as
           // well as wrapped operands after question operator or last
diff --git a/clang/lib/Format/WhitespaceManager.h b/clang/lib/Format/WhitespaceManager.h
index 7b91d8bf4db72b..b2bf2690230a98 100644
--- a/clang/lib/Format/WhitespaceManager.h
+++ b/clang/lib/Format/WhitespaceManager.h
@@ -36,7 +36,8 @@ class WhitespaceManager {
 public:
   WhitespaceManager(const SourceManager &SourceMgr, const FormatStyle &Style,
                     bool UseCRLF)
-      : SourceMgr(SourceMgr), Style(Style), UseCRLF(UseCRLF) {}
+      : SourceMgr(SourceMgr), Style(Style),
+        LangOpts(getFormattingLangOpts(Style)), UseCRLF(UseCRLF) {}
 
   bool useCRLF() const { return UseCRLF; }
 
@@ -363,6 +364,7 @@ class WhitespaceManager {
   const SourceManager &SourceMgr;
   tooling::Replacements Replaces;
   const FormatStyle &Style;
+  const LangOptions LangOpts;
   bool UseCRLF;
 };
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/112689


More information about the cfe-commits mailing list