diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -14,12 +14,83 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" #include namespace clang { namespace clangd { namespace { +/// Gets an underlying type in a type hierarchy. Should be used to find the type +/// in a hieracrchy when the HighlightingTokenCollector's RecursiveASTVisitor +/// can not recurse to it by default and the addType function is used outside a +/// Visit*TypeLoc. +class UnderlyingTypeVisitor + : public RecursiveASTVisitor { + const Type *Underlying; + +public: + /// Gets an underlying type in the \p T type hierarchy. + /// Gets the first type ptr with a Tag decl if one exists. + /// Else gets the first Leaf type ptr. + /// If neither are found returns the type ptr. + const Type *getUnderlyingType(QualType T) { + Underlying = &(*T); + TraverseType(T); + return Underlying; + } + + // Check if T has a tag decl and if so stop traversing the type hierachy as we + // want to return this. + bool VisitType(Type *T) { + if (T->getAsTagDecl()) { + Underlying = T; + return false; + } + return true; + } + + // The default behaviour for RecursiveASTVisitor is to traverse the class type + // as well. This makes member pointers become highlighted as classes which is + // incorrect. + bool TraverseMemberPointerType(MemberPointerType *T) { + return RecursiveASTVisitor::TraverseType( + T->getPointeeType()); + } + + // RecursiveASTVisitor does not traverse the underlying type of a decltype. + bool TraverseDecltypeType(DecltypeType *T) { + return RecursiveASTVisitor::TraverseType( + T->getUnderlyingType()); + } + + // RecursiveASTVisitor also traverses parameter and exception types. Only want + // return type for this. + bool TraverseFunctionProtoType(FunctionProtoType *T) { + return RecursiveASTVisitor::TraverseType( + T->getReturnType()); + } + + // RecursiveASTVisitor does not traverse a typedef's underlying type. + bool TraverseTypedefType(TypedefType *T) { + return RecursiveASTVisitor::TraverseType( + T->getDecl()->getUnderlyingType()); + } + +// Create Visit functions for all leaf types that set the Leaf type. Don't want +// to create any functions for non leaf Types. +#define TYPE(X, Y) +#define LEAF_TYPE(CLASS) \ + bool Visit##CLASS##Type(CLASS##Type *T) { \ + Underlying = T; \ + return true; \ + } +#include "clang/AST/TypeNodes.def" +#undef TYPE +#undef LEAF_TYPE +}; + // Collects all semantic tokens in an ASTContext. class HighlightingTokenCollector : public RecursiveASTVisitor { @@ -119,13 +190,8 @@ bool VisitTypedefNameDecl(TypedefNameDecl *TD) { if (const auto *TSI = TD->getTypeSourceInfo()) - addType(TD->getLocation(), TSI->getTypeLoc().getTypePtr()); - return true; - } - - bool VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc &TL) { - // TemplateTypeParmTypeLoc does not have a TagDecl in its type ptr. - addToken(TL.getBeginLoc(), TL.getDecl()); + addType(TD->getLocation(), UnderlyingTypeVisitor().getUnderlyingType( + TSI->getTypeLoc().getType())); return true; } @@ -182,6 +248,9 @@ if (TP->isBuiltinType()) // Builtins must be special cased as they do not have a TagDecl. addToken(Loc, HighlightingKind::Primitive); + if (const TemplateTypeParmType *TD = dyn_cast(TP)) + // TemplateTypeParmType also do not have a TagDecl. + addToken(Loc, TD->getDecl()); if (const TagDecl *TD = TP->getAsTagDecl()) addToken(Loc, TD); } diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -16,6 +16,10 @@ namespace clang { namespace clangd { +void PrintTo(const HighlightingToken &T, ::std::ostream *OS) { + *OS << "(" << T.R.start.line << ", " << T.R.start.character << ") -> (" << T.R.end.line << ", " << T.R.end.character << "): " << (int)T.Kind; +} + namespace { MATCHER_P(LineNumber, L, "") { return arg.Line == L; } @@ -225,7 +229,9 @@ )cpp", R"cpp( namespace $Namespace[[a]] { - struct $Class[[A]] {}; + struct $Class[[A]] { + $Primitive[[void]] $Method[[foo]]($Class[[A]]*); + }; typedef $Primitive[[char]] $Primitive[[C]]; } typedef $Namespace[[a]]::$Class[[A]] $Class[[B]]; @@ -238,6 +244,12 @@ $Enum[[CD]] $Function[[f]]($Class[[BB]]); typedef $Namespace[[a]]::$Primitive[[C]] $Primitive[[PC]]; typedef $Primitive[[float]] $Primitive[[F]]; + using $Primitive[[Member]] = + $Primitive[[void]] (B::*)($Namespace[[a]]::$Class[[A]]*); + $Primitive[[void]] $Function[[foo]]($Primitive[[int]], $Class[[B]]); + typedef decltype($Function[[foo]]) $Primitive[[fooo]]; + typedef $Class[[B]] (*$Class[[func]])(); + typedef $Primitive[[int]] (*$Primitive[[func]])(); )cpp", R"cpp( template @@ -431,6 +443,20 @@ assert($Variable[[x]] != $Variable[[y]]); assert($Variable[[x]] != $Function[[f]]()); } + )cpp", + R"cpp( + template + class $Class[[A]] { + using $TemplateParameter[[D]] = $TemplateParameter[[T]]; + using $TemplateParameter[[DD]] = $TemplateParameter[[T]] *; + using $TemplateParameter[[DDD]] = $TemplateParameter[[T]] &; + using $TemplateParameter[[B]] = $TemplateParameter[[T]]*[3]; + using $TemplateParameter[[BB]] = $TemplateParameter[[T]]&&; + using $TemplateParameter[[Member]] = + BB (T::*)($Primitive[[int]]); + using $TemplateParameter[[MemberT]] = + $TemplateParameter[[T]]*& (T::*)($Class[[A]]); + }; )cpp"}; for (const auto &TestCase : TestCases) { checkHighlightings(TestCase);