[clang] [clang-format] Improve annotating star and amp (PR #194190)

Björn Schäpers via cfe-commits cfe-commits at lists.llvm.org
Sat Apr 25 15:43:34 PDT 2026


https://github.com/HazardyKnusperkeks created https://github.com/llvm/llvm-project/pull/194190

Use the context better.

Fixes #193255

>From 478479a5d6642bb2f74f603441fb0f92765fa807 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= <bjoern at hazardy.de>
Date: Sun, 26 Apr 2026 00:40:24 +0200
Subject: [PATCH] [clang-format] Improve annotating star and amp

Use the context better.

Fixes #193255
---
 clang/lib/Format/TokenAnnotator.cpp           | 80 +++++++++++--------
 clang/unittests/Format/TokenAnnotatorTest.cpp |  5 ++
 2 files changed, 50 insertions(+), 35 deletions(-)

diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 898759cb8ea1b..f5db7ee23af34 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -315,9 +315,13 @@ class AnnotatingParser {
     FormatToken *PrevNonComment = OpeningParen.getPreviousNonComment();
     OpeningParen.ParentBracket = Contexts.back().ContextKind;
     ScopedContextCreator ContextCreator(*this, tok::l_paren, 1);
+    auto &Context = Contexts.back();
+
+    if (OpeningParen.is(TT_FunctionDeclarationLParen))
+      Context.ContextType = Context::ParameterList;
 
     // FIXME: This is a bit of a hack. Do better.
-    Contexts.back().ColonIsForRangeExpr =
+    Context.ColonIsForRangeExpr =
         Contexts.size() == 2 && Contexts[0].ColonIsForRangeExpr;
 
     if (OpeningParen.Previous &&
@@ -350,17 +354,17 @@ class AnnotatingParser {
       // then the argument must be an expression.
       bool OperatorCalledAsMemberFunction =
           Prev->Previous && Prev->Previous->isOneOf(tok::period, tok::arrow);
-      Contexts.back().IsExpression = OperatorCalledAsMemberFunction;
+      Context.IsExpression = OperatorCalledAsMemberFunction;
     } else if (OpeningParen.is(TT_VerilogInstancePortLParen)) {
-      Contexts.back().IsExpression = true;
-      Contexts.back().ContextType = Context::VerilogInstancePortList;
+      Context.IsExpression = true;
+      Context.ContextType = Context::VerilogInstancePortList;
     } else if (Style.isJavaScript() &&
                (Line.startsWith(Keywords.kw_type, tok::identifier) ||
                 Line.startsWith(tok::kw_export, Keywords.kw_type,
                                 tok::identifier))) {
       // type X = (...);
       // export type X = (...);
-      Contexts.back().IsExpression = false;
+      Context.IsExpression = false;
     } else if (OpeningParen.Previous &&
                (OpeningParen.Previous->isOneOf(
                     tok::kw_noexcept, tok::kw_explicit, tok::kw_while,
@@ -368,68 +372,69 @@ class AnnotatingParser {
                     TT_BinaryOperator) ||
                 OpeningParen.Previous->isIf())) {
       // if and while usually contain expressions.
-      Contexts.back().IsExpression = true;
+      Context.IsExpression = true;
     } else if (Style.isJavaScript() && OpeningParen.Previous &&
                (OpeningParen.Previous->is(Keywords.kw_function) ||
                 (OpeningParen.Previous->endsSequence(tok::identifier,
                                                      Keywords.kw_function)))) {
       // function(...) or function f(...)
-      Contexts.back().IsExpression = false;
+      Context.IsExpression = false;
     } else if (Style.isJavaScript() && OpeningParen.Previous &&
                OpeningParen.Previous->is(TT_JsTypeColon)) {
       // let x: (SomeType);
-      Contexts.back().IsExpression = false;
+      Context.IsExpression = false;
     } else if (isLambdaParameterList(&OpeningParen)) {
       // This is a parameter list of a lambda expression.
       OpeningParen.setType(TT_LambdaDefinitionLParen);
-      Contexts.back().IsExpression = false;
+      Context.ContextType = Context::ParameterList;
+      Context.IsExpression = false;
     } else if (OpeningParen.is(TT_RequiresExpressionLParen)) {
-      Contexts.back().IsExpression = false;
+      Context.IsExpression = false;
     } else if (OpeningParen.Previous &&
                OpeningParen.Previous->is(tok::kw__Generic)) {
-      Contexts.back().ContextType = Context::C11GenericSelection;
-      Contexts.back().IsExpression = true;
+      Context.ContextType = Context::C11GenericSelection;
+      Context.IsExpression = true;
     } else if (OpeningParen.Previous &&
                OpeningParen.Previous->TokenText == "Q_PROPERTY") {
-      Contexts.back().ContextType = Context::QtProperty;
-      Contexts.back().IsExpression = false;
+      Context.ContextType = Context::QtProperty;
+      Context.IsExpression = false;
     } else if (Line.InPPDirective &&
                (!OpeningParen.Previous ||
                 OpeningParen.Previous->isNot(tok::identifier))) {
-      Contexts.back().IsExpression = true;
+      Context.IsExpression = true;
     } else if (Contexts[Contexts.size() - 2].CaretFound) {
       // This is the parameter list of an ObjC block.
-      Contexts.back().IsExpression = false;
+      Context.IsExpression = false;
     } else if (OpeningParen.Previous &&
                OpeningParen.Previous->is(TT_ForEachMacro)) {
       // The first argument to a foreach macro is a declaration.
-      Contexts.back().ContextType = Context::ForEachMacro;
-      Contexts.back().IsExpression = false;
+      Context.ContextType = Context::ForEachMacro;
+      Context.IsExpression = false;
     } else if (OpeningParen.Previous && OpeningParen.Previous->MatchingParen &&
                OpeningParen.Previous->MatchingParen->isOneOf(
                    TT_ObjCBlockLParen, TT_FunctionTypeLParen)) {
-      Contexts.back().IsExpression = false;
+      Context.IsExpression = false;
     } else if (!Line.MustBeDeclaration &&
                (!Line.InPPDirective || (Line.InMacroBody && !Scopes.empty()))) {
       bool IsForOrCatch =
           OpeningParen.Previous &&
           OpeningParen.Previous->isOneOf(tok::kw_for, tok::kw_catch);
-      Contexts.back().IsExpression = !IsForOrCatch;
+      Context.IsExpression = !IsForOrCatch;
     }
 
     if (Style.isTableGen()) {
       if (FormatToken *Prev = OpeningParen.Previous) {
         if (Prev->is(TT_TableGenCondOperator)) {
-          Contexts.back().IsTableGenCondOpe = true;
-          Contexts.back().IsExpression = true;
+          Context.IsTableGenCondOpe = true;
+          Context.IsExpression = true;
         } else if (Contexts.size() > 1 &&
                    Contexts[Contexts.size() - 2].IsTableGenBangOpe) {
           // Hack to handle bang operators. The parent context's flag
           // was set by parseTableGenSimpleValue().
           // We have to specify the context outside because the prev of "(" may
           // be ">", not the bang operator in this case.
-          Contexts.back().IsTableGenBangOpe = true;
-          Contexts.back().IsExpression = true;
+          Context.IsTableGenBangOpe = true;
+          Context.IsExpression = true;
         } else {
           // Otherwise, this paren seems DAGArg.
           if (!parseTableGenDAGArg())
@@ -452,7 +457,7 @@ class AnnotatingParser {
         OpeningParen.setType(TT_TypeDeclarationParen);
         // decltype() and typeof() usually contain expressions.
         if (PrevNonComment->isOneOf(tok::kw_decltype, tok::kw_typeof))
-          Contexts.back().IsExpression = true;
+          Context.IsExpression = true;
       }
     }
 
@@ -462,7 +467,7 @@ class AnnotatingParser {
     const bool IsStaticAssert =
         PrevNonComment && PrevNonComment->is(tok::kw_static_assert);
     if (IsStaticAssert)
-      Contexts.back().InStaticAssertFirstArgument = true;
+      Context.InStaticAssertFirstArgument = true;
 
     // MightBeFunctionType and ProbablyFunctionType are used for
     // function pointer and reference types as well as Objective-C
@@ -490,7 +495,7 @@ class AnnotatingParser {
       if (CurrentToken->is(tok::comma))
         MightBeFunctionType = false;
       if (Prev.is(TT_BinaryOperator))
-        Contexts.back().IsExpression = true;
+        Context.IsExpression = true;
       if (CurrentToken->is(tok::r_paren)) {
         if (Prev.is(TT_PointerOrReference) &&
             (PrevPrev == &OpeningParen || PrevPrev->is(tok::coloncolon))) {
@@ -523,9 +528,9 @@ class AnnotatingParser {
 
         if (StartsObjCSelector) {
           CurrentToken->setType(TT_ObjCSelector);
-          if (Contexts.back().FirstObjCSelectorName) {
-            Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName =
-                Contexts.back().LongestObjCSelectorName;
+          if (Context.FirstObjCSelectorName) {
+            Context.FirstObjCSelectorName->LongestObjCSelectorName =
+                Context.LongestObjCSelectorName;
           }
         }
 
@@ -569,7 +574,7 @@ class AnnotatingParser {
            Prev.isTypeName(LangOpts)) &&
           !(CurrentToken->is(tok::l_brace) ||
             (CurrentToken->is(tok::l_paren) && !ProbablyFunctionTypeLParen))) {
-        Contexts.back().IsExpression = false;
+        Context.IsExpression = false;
       }
       if (CurrentToken->isOneOf(tok::semi, tok::colon)) {
         MightBeObjCForRangeLoop = false;
@@ -595,18 +600,18 @@ class AnnotatingParser {
       // parse the type correctly. Reset that after a comma.
       if (CurrentToken->is(tok::comma)) {
         if (IsStaticAssert)
-          Contexts.back().InStaticAssertFirstArgument = false;
+          Context.InStaticAssertFirstArgument = false;
         else
-          Contexts.back().CanBeExpression = true;
+          Context.CanBeExpression = true;
       }
 
       if (Style.isTableGen()) {
         if (CurrentToken->is(tok::comma)) {
-          if (Contexts.back().IsTableGenCondOpe)
+          if (Context.IsTableGenCondOpe)
             CurrentToken->setType(TT_TableGenCondOperatorComma);
           next();
         } else if (CurrentToken->is(tok::colon)) {
-          if (Contexts.back().IsTableGenCondOpe)
+          if (Context.IsTableGenCondOpe)
             CurrentToken->setType(TT_TableGenCondOperatorColon);
           next();
         }
@@ -2196,6 +2201,8 @@ class AnnotatingParser {
       QtProperty,
       // Like in the outer parentheses in `ffnand ff1(.q());`.
       VerilogInstancePortList,
+      // Inside a parameter list.
+      ParameterList,
     } ContextType = Unknown;
   };
 
@@ -3038,6 +3045,9 @@ class AnnotatingParser {
                                                 : TT_BinaryOperator;
     }
 
+    if (Contexts.back().ContextType == Context::ParameterList)
+      return TT_PointerOrReference;
+
     const FormatToken *PrevToken = Tok.getPreviousNonComment();
     if (!PrevToken)
       return TT_UnaryOperator;
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index cf2bd9f8bd76a..215804f4df02c 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -433,6 +433,11 @@ TEST_F(TokenAnnotatorTest, UnderstandsUsesOfStarAndAmp) {
   EXPECT_TOKEN(Tokens[7], tok::r_paren, TT_Unknown); // Not TT_CastRParen
   EXPECT_TOKEN(Tokens[10], tok::amp, TT_PointerOrReference);
   EXPECT_TOKEN(Tokens[11], tok::identifier, TT_StartOfName);
+
+  Tokens = annotate("template<typename T>\n"
+                    "concept C = requires(T t) { [](auto &c) {}(t); };");
+  ASSERT_EQ(Tokens.size(), 30u) << Tokens;
+  EXPECT_TOKEN(Tokens[18], tok::amp, TT_PointerOrReference);
 }
 
 TEST_F(TokenAnnotatorTest, UnderstandsUsesOfPlusAndMinus) {



More information about the cfe-commits mailing list