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
