https://github.com/dzbarsky created 
https://github.com/llvm/llvm-project/pull/202628

CollectExtraHighlightings derives from RecursiveASTVisitor, so
SemanticHighlighting.cpp emits a complete CRTP traversal implementation
specialized for one visitor.

Derive it from DynamicRecursiveASTVisitor instead. The visitor keeps the same
Visit* and TraverseTemplateArgumentLoc behavior while reusing traversal
functions that clangd already links. Mark each virtual callback override so
signature drift remains a compile-time error.

A fully stripped Release arm64 clangd decreases from 60,921,480 to 60,888,296
bytes, saving 33,184 bytes (0.054%). At this point in the cumulative patch 
stack, the stripped all-tools multicall
binary decreases from 150,586,136 to 150,569,536 bytes, saving 16,600 bytes
(0.011%). Measured independently, this change saves 49,120 bytes in the
multicall binary.

All seven SemanticHighlighting tests pass before and after the change. LLVM has
no semantic-highlighting benchmark. Seven focused test runs measured 1.432
seconds baseline versus 1.368 seconds patched mean user CPU time; wall time was
dominated by test scheduling noise.

Work towards #202616

>From 4495106d4584fa7a4d760186d1a25f6df0ba8fe3 Mon Sep 17 00:00:00 2001
From: David Zbarsky <[email protected]>
Date: Mon, 8 Jun 2026 13:09:12 -0400
Subject: [PATCH] [clangd] Share semantic highlighting AST traversal code

CollectExtraHighlightings derives from RecursiveASTVisitor, so
SemanticHighlighting.cpp emits a complete CRTP traversal implementation
specialized for one visitor.

Derive it from DynamicRecursiveASTVisitor instead. The visitor keeps the same
Visit* and TraverseTemplateArgumentLoc behavior while reusing traversal
functions that clangd already links. Mark each virtual callback override so
signature drift remains a compile-time error.

A fully stripped Release arm64 clangd decreases from 60,921,480 to 60,888,296
bytes, saving 33,184 bytes (0.054%). At this point in the cumulative patch 
stack, the stripped all-tools multicall
binary decreases from 150,586,136 to 150,569,536 bytes, saving 16,600 bytes
(0.011%). Measured independently, this change saves 49,120 bytes in the
multicall binary.

All seven SemanticHighlighting tests pass before and after the change. LLVM has
no semantic-highlighting benchmark. Seven focused test runs measured 1.432
seconds baseline versus 1.368 seconds patched mean user CPU time; wall time was
dominated by test scheduling noise.
---
 .../clangd/SemanticHighlighting.cpp           | 84 ++++++++++---------
 1 file changed, 43 insertions(+), 41 deletions(-)

diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp 
b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index 856904bc810d1..f85c8d2d7f2be 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -21,8 +21,8 @@
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/ExprCXX.h"
-#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
@@ -535,34 +535,33 @@ std::optional<HighlightingModifier> scopeModifier(const 
Type *T) {
 
 /// Produces highlightings, which are not captured by findExplicitReferences,
 /// e.g. highlights dependent names and 'auto' as the underlying type.
-class CollectExtraHighlightings
-    : public RecursiveASTVisitor<CollectExtraHighlightings> {
-  using Base = RecursiveASTVisitor<CollectExtraHighlightings>;
+class CollectExtraHighlightings : public DynamicRecursiveASTVisitor {
+  using Base = DynamicRecursiveASTVisitor;
 
 public:
   CollectExtraHighlightings(HighlightingsBuilder &H) : H(H) {}
 
-  bool VisitCXXConstructExpr(CXXConstructExpr *E) {
+  bool VisitCXXConstructExpr(CXXConstructExpr *E) override {
     highlightMutableReferenceArguments(E->getConstructor(),
                                        {E->getArgs(), E->getNumArgs()});
 
     return true;
   }
 
-  bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
+  bool TraverseConstructorInitializer(CXXCtorInitializer *Init) override {
     if (Init->isMemberInitializer())
       if (auto *Member = Init->getMember())
         highlightMutableReferenceArgument(Member->getType(), Init->getInit());
     return Base::TraverseConstructorInitializer(Init);
   }
 
-  bool TraverseTypeConstraint(const TypeConstraint *C) {
+  bool TraverseTypeConstraint(const TypeConstraint *C) override {
     if (auto *Args = C->getTemplateArgsAsWritten())
       H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
     return Base::TraverseTypeConstraint(C);
   }
 
-  bool VisitPredefinedExpr(PredefinedExpr *E) {
+  bool VisitPredefinedExpr(PredefinedExpr *E) override {
     H.addToken(E->getLocation(), HighlightingKind::LocalVariable)
         .addModifier(HighlightingModifier::Static)
         .addModifier(HighlightingModifier::Readonly)
@@ -570,26 +569,26 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
+  bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) override {
     if (auto *Args = E->getTemplateArgsAsWritten())
       H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
     return true;
   }
 
-  bool VisitTemplateDecl(TemplateDecl *D) {
+  bool VisitTemplateDecl(TemplateDecl *D) override {
     if (auto *TPL = D->getTemplateParameters())
       H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
     return true;
   }
 
-  bool VisitTagDecl(TagDecl *D) {
+  bool VisitTagDecl(TagDecl *D) override {
     for (TemplateParameterList *TPL : D->getTemplateParameterLists())
       H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
     return true;
   }
 
-  bool
-  VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D) {
+  bool VisitClassTemplateSpecializationDecl(
+      ClassTemplateSpecializationDecl *D) override {
     if (const auto *Info = D->getExplicitInstantiationInfo()) {
       H.addAngleBracketTokens(Info->TemplateArgsAsWritten->getLAngleLoc(),
                               Info->TemplateArgsAsWritten->getRAngleLoc());
@@ -602,7 +601,8 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *D) {
+  bool VisitVarTemplateSpecializationDecl(
+      VarTemplateSpecializationDecl *D) override {
     if (const auto *Info = D->getExplicitInstantiationInfo()) {
       H.addAngleBracketTokens(Info->TemplateArgsAsWritten->getLAngleLoc(),
                               Info->TemplateArgsAsWritten->getRAngleLoc());
@@ -615,16 +615,16 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitDeclRefExpr(DeclRefExpr *E) {
+  bool VisitDeclRefExpr(DeclRefExpr *E) override {
     H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
     return true;
   }
-  bool VisitMemberExpr(MemberExpr *E) {
+  bool VisitMemberExpr(MemberExpr *E) override {
     H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
     return true;
   }
 
-  bool VisitFunctionDecl(FunctionDecl *D) {
+  bool VisitFunctionDecl(FunctionDecl *D) override {
     if (const TemplateParameterList *TPL =
             D->getTemplateSpecializationParameters())
       H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
@@ -646,7 +646,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
+  bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) override {
     const auto AddOpToken = [&](SourceLocation Loc) {
       H.addToken(Loc, HighlightingKind::Operator)
           .addModifier(HighlightingModifier::UserDefined);
@@ -660,47 +660,47 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitUnaryOperator(UnaryOperator *Op) {
+  bool VisitUnaryOperator(UnaryOperator *Op) override {
     auto &Token = H.addToken(Op->getOperatorLoc(), HighlightingKind::Operator);
     if (Op->getSubExpr()->isTypeDependent())
       Token.addModifier(HighlightingModifier::UserDefined);
     return true;
   }
 
-  bool VisitBinaryOperator(BinaryOperator *Op) {
+  bool VisitBinaryOperator(BinaryOperator *Op) override {
     auto &Token = H.addToken(Op->getOperatorLoc(), HighlightingKind::Operator);
     if (Op->getLHS()->isTypeDependent() || Op->getRHS()->isTypeDependent())
       Token.addModifier(HighlightingModifier::UserDefined);
     return true;
   }
 
-  bool VisitConditionalOperator(ConditionalOperator *Op) {
+  bool VisitConditionalOperator(ConditionalOperator *Op) override {
     H.addToken(Op->getQuestionLoc(), HighlightingKind::Operator);
     H.addToken(Op->getColonLoc(), HighlightingKind::Operator);
     return true;
   }
 
-  bool VisitCXXNewExpr(CXXNewExpr *E) {
+  bool VisitCXXNewExpr(CXXNewExpr *E) override {
     auto &Token = H.addToken(E->getBeginLoc(), HighlightingKind::Operator);
     if (isa_and_present<CXXMethodDecl>(E->getOperatorNew()))
       Token.addModifier(HighlightingModifier::UserDefined);
     return true;
   }
 
-  bool VisitCXXDeleteExpr(CXXDeleteExpr *E) {
+  bool VisitCXXDeleteExpr(CXXDeleteExpr *E) override {
     auto &Token = H.addToken(E->getBeginLoc(), HighlightingKind::Operator);
     if (isa_and_present<CXXMethodDecl>(E->getOperatorDelete()))
       Token.addModifier(HighlightingModifier::UserDefined);
     return true;
   }
 
-  bool VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
+  bool VisitCXXNamedCastExpr(CXXNamedCastExpr *E) override {
     const auto &B = E->getAngleBrackets();
     H.addAngleBracketTokens(B.getBegin(), B.getEnd());
     return true;
   }
 
-  bool VisitCallExpr(CallExpr *E) {
+  bool VisitCallExpr(CallExpr *E) override {
     // Highlighting parameters passed by non-const reference does not really
     // make sense for literals...
     if (isa<UserDefinedLiteral>(E))
@@ -779,7 +779,7 @@ class CollectExtraHighlightings
     }
   }
 
-  bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) {
+  bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) override {
     if (auto K = kindForType(L.getTypePtr(), H.getResolver())) {
       auto &Tok = H.addToken(L.getBeginLoc(), *K)
                       .addModifier(HighlightingModifier::Deduced);
@@ -791,7 +791,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitCXXDestructorDecl(CXXDestructorDecl *D) {
+  bool VisitCXXDestructorDecl(CXXDestructorDecl *D) override {
     SourceLocation Loc =
         D->getNameInfo().getNamedTypeInfo()->getTypeLoc().getBeginLoc();
     H.addExtraModifier(Loc, HighlightingModifier::ConstructorOrDestructor);
@@ -801,7 +801,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitCXXMemberCallExpr(CXXMemberCallExpr *CE) {
+  bool VisitCXXMemberCallExpr(CXXMemberCallExpr *CE) override {
     // getMethodDecl can return nullptr with member pointers, e.g.
     // `(foo.*pointer_to_member_fun)(arg);`
     if (auto *D = CE->getMethodDecl()) {
@@ -823,7 +823,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitDeclaratorDecl(DeclaratorDecl *D) {
+  bool VisitDeclaratorDecl(DeclaratorDecl *D) override {
     for (TemplateParameterList *TPL : D->getTemplateParameterLists())
       H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
     auto *AT = D->getType()->getContainedAutoType();
@@ -875,7 +875,7 @@ class CollectExtraHighlightings
     }
   }
 
-  bool VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
+  bool VisitObjCMethodDecl(ObjCMethodDecl *OMD) override {
     llvm::SmallVector<SourceLocation> Locs;
     OMD->getSelectorLocs(Locs);
     highlightObjCSelector(Locs, /*Decl=*/true,
@@ -884,7 +884,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitObjCMessageExpr(ObjCMessageExpr *OME) {
+  bool VisitObjCMessageExpr(ObjCMessageExpr *OME) override {
     llvm::SmallVector<SourceLocation> Locs;
     OME->getSelectorLocs(Locs);
     bool DefaultLibrary = false;
@@ -909,7 +909,7 @@ class CollectExtraHighlightings
       Tok.addModifier(HighlightingModifier::DefaultLibrary);
   }
 
-  bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *OPRE) {
+  bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *OPRE) override {
     // We need to handle implicit properties here since they will appear to
     // reference `ObjCMethodDecl` via an implicit `ObjCMessageExpr`, so normal
     // highlighting will not work.
@@ -931,7 +931,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitOverloadExpr(OverloadExpr *E) {
+  bool VisitOverloadExpr(OverloadExpr *E) override {
     H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
     if (!E->decls().empty())
       return true; // handled by findExplicitReferences.
@@ -943,7 +943,8 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
+  bool
+  VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) override {
     H.addToken(E->getMemberNameInfo().getLoc(), HighlightingKind::Unknown)
         .addModifier(HighlightingModifier::DependentName)
         .addModifier(HighlightingModifier::ClassScope);
@@ -951,7 +952,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
+  bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) override {
     H.addToken(E->getNameInfo().getLoc(), HighlightingKind::Unknown)
         .addModifier(HighlightingModifier::DependentName)
         .addModifier(HighlightingModifier::ClassScope);
@@ -959,7 +960,7 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitAttr(Attr *A) {
+  bool VisitAttr(Attr *A) override {
     switch (A->getKind()) {
     case attr::Override:
     case attr::Final:
@@ -971,14 +972,15 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool VisitDependentNameTypeLoc(DependentNameTypeLoc L) {
+  bool VisitDependentNameTypeLoc(DependentNameTypeLoc L) override {
     H.addToken(L.getNameLoc(), HighlightingKind::Type)
         .addModifier(HighlightingModifier::DependentName)
         .addModifier(HighlightingModifier::ClassScope);
     return true;
   }
 
-  bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) {
+  bool
+  VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) override 
{
     if (!L.getTypePtr()->getTemplateName().getAsTemplateDecl(
             /*IgnoreDeduced=*/true))
       H.addToken(L.getTemplateNameLoc(), HighlightingKind::Type)
@@ -988,12 +990,12 @@ class CollectExtraHighlightings
     return true;
   }
 
-  bool TraverseTemplateArgumentLoc(TemplateArgumentLoc L) {
+  bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &L) override {
     // Handle template template arguments only (other arguments are handled by
     // their Expr, TypeLoc etc values).
     if (L.getArgument().getKind() != TemplateArgument::Template &&
         L.getArgument().getKind() != TemplateArgument::TemplateExpansion)
-      return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L);
+      return Base::TraverseTemplateArgumentLoc(L);
 
     TemplateName N = L.getArgument().getAsTemplateOrTemplatePattern();
     switch (N.getKind()) {
@@ -1016,7 +1018,7 @@ class CollectExtraHighlightings
       // Names that could be resolved to a TemplateDecl are handled elsewhere.
       break;
     }
-    return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L);
+    return Base::TraverseTemplateArgumentLoc(L);
   }
 
 private:

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to