Index: clang-tools-extra/clangd/SemanticHighlighting.cpp =================================================================== --- clang-tools-extra/clangd/SemanticHighlighting.cpp +++ clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -19,6 +19,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" @@ -26,6 +27,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" @@ -449,8 +451,9 @@ } const HeuristicResolver *getResolver() const { return Resolver; } + const SourceManager &getSourceMgr() const { return SourceMgr; } + const syntax::TokenBuffer &getTB() const { return TB; } -private: llvm::Optional getRangeForSourceLocation(SourceLocation Loc) { Loc = getHighlightableSpellingToken(Loc, SourceMgr); if (Loc.isInvalid()) @@ -463,6 +466,7 @@ Tok->range(SourceMgr).toCharRange(SourceMgr)); } +private: const syntax::TokenBuffer &TB; const SourceManager &SourceMgr; const LangOptions &LangOpts; @@ -527,13 +531,132 @@ return true; } + // Returns the token immediately after a given location. + // FIXME: Is this really the best way to do this? + const syntax::Token *nextToken(SourceLocation Loc) { + if (Loc.isInvalid()) + return nullptr; + + return llvm::partition_point( + H.getTB().spelledTokens(H.getSourceMgr().getFileID(Loc)), + [&](const syntax::Token &T) { return T.location() <= Loc; }); + } + + // Expands the range for a HighlightingToken to another SourceLocation. + void expandToken(Range &Range, SourceLocation Loc) { + if (Loc.isInvalid()) + return; + Range.end = H.getRangeForSourceLocation(Loc)->end; + } + + // For an overloaded operator `operator<<(...)`, we want to the highlighting + // to also include the `<<` part. If we don't do this, only the `operator` + // part will be correctly highlighted. + void expandOverloadedOperator(SourceLocation Loc, + Range &OpRange) { + const auto *NextTok = nextToken(Loc); + if (!NextTok) + return; + + expandToken(OpRange, NextTok->location()); + + // If this operator is `[` or `(`, we also want to highlight the matching + // `]` or `)`. + if (NextTok->kind() == tok::l_paren || NextTok->kind() == tok::l_square) { + const auto *ParenEndTok = nextToken(NextTok->location()); + if (ParenEndTok) + expandToken(OpRange, ParenEndTok->location()); // `]` or `)`. + } + + // If this operator is `new[]` or `delete[]`, + // we also want to highlight the `[]` part. + else if (NextTok->kind() == tok::kw_new || + NextTok->kind() == tok::kw_delete) { + NextTok = nextToken(NextTok->location()); + if (!NextTok || NextTok->kind() != tok::l_square) + return; + expandToken(OpRange, NextTok->location()); // `[`. + const auto *ParenEndTok = nextToken(NextTok->location()); + if (ParenEndTok) + expandToken(OpRange, ParenEndTok->location()); // `]`. + } + } + + void highlightOperatorCallExpr(SourceLocation Loc, FunctionDecl *FD) { + auto HighlightingKind = kindForDecl(FD, H.getResolver()); + if (!HighlightingKind) + return; + + auto &Tok = H.addToken(Loc, *HighlightingKind); + if (auto Mod = scopeModifier(FD)) + Tok.addModifier(*Mod); + if (isConst(FD)) + Tok.addModifier(HighlightingModifier::Readonly); + if (isStatic(FD)) + Tok.addModifier(HighlightingModifier::Static); + if (isAbstract(FD)) + Tok.addModifier(HighlightingModifier::Abstract); + if (isVirtual(FD)) + Tok.addModifier(HighlightingModifier::Virtual); + if (isDependent(FD)) + Tok.addModifier(HighlightingModifier::DependentName); + if (isDefaultLibrary(FD)) + Tok.addModifier(HighlightingModifier::DefaultLibrary); + if (FD->isDeprecated()) + Tok.addModifier(HighlightingModifier::Deprecated); + } + + // Highlight called overloaded operators. + // E.g. `<<` in `std::cout << "foo"`. + bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *O) { + // Highlight calls to overloaded operators as function-like types. + auto *FD = dyn_cast_or_null(O->getCalleeDecl()); + if (!FD) + return true; + + auto HighlightingKind = kindForDecl(FD, H.getResolver()); + if (!HighlightingKind) + return true; + + auto Loc = O->getOperatorLoc(); + if (Loc.isMacroID()) + return true; + + highlightOperatorCallExpr(Loc, FD); + return true; + } + + bool VisitFunctionDecl(FunctionDecl *FD) { + if (FD->isOverloadedOperator()) { + auto HighlightingKind = kindForDecl(FD, H.getResolver()); + if (!HighlightingKind) + return true; + + auto Loc = FD->getLocation(); + if (Loc.isInvalid() || Loc.isMacroID()) + return true; + + Range OpRange = *H.getRangeForSourceLocation(Loc); + + // Also highlight the operator token as part of the function name. + // E.g. `<<` in `operator<<`. + expandOverloadedOperator(Loc, OpRange); + + auto &Kind = *HighlightingKind; + auto &OpKeywTok = H.addToken(OpRange, Kind); + OpKeywTok.addModifier(HighlightingModifier::Declaration); + } + + return true; + } + bool VisitCallExpr(CallExpr *E) { // Highlighting parameters passed by non-const reference does not really // make sense for literals... if (isa(E)) return true; - // FIXME ...here it would make sense though. + // Already handled by VisitCXXOperatorCallExpr. if (isa(E)) return true; @@ -585,6 +708,86 @@ } } + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + if (auto *FD = dyn_cast(DRE->getDecl())) { + if (FD->isOverloadedOperator()) { + auto Loc = DRE->getLocation(); + if (Loc.isMacroID()) + return true; + + const auto *Tok = H.getTB().spelledTokenAt(Loc); + if (!Tok) + return true; + + if (Tok->kind() == tok::kw_operator) { + // Highlight the operator token as part of the function name. + // E.g. `<<` in `operator<<`. + highlightOverloadedOperatorFunctionNotation(FD, Loc); + } else if (Tok->kind() == tok::l_square || + Tok->kind() == tok::l_paren) { + // Highlight the `[` or `(` token that `VisitCXXOperatorCallExpr` + // did not highlight. + highlightOperatorCallExpr(Loc, FD); + } + } + } + + return true; + } + + bool VisitMemberExpr(MemberExpr *ME) { + if (auto *FD = dyn_cast(ME->getMemberDecl())) { + if (FD->isOverloadedOperator()) { + auto Loc = ME->getMemberLoc(); + if (Loc.isMacroID()) + return true; + + const auto *Tok = H.getTB().spelledTokenAt(Loc); + if (Tok && Tok->kind() == tok::kw_operator) { + // Highlight the operator token as part of the function name. + // E.g. `<<` in `operator<<`. + highlightOverloadedOperatorFunctionNotation(FD, Loc); + } + } + } + + return true; + } + + // Handle operator calls in function notation such as `operator<<(...)`. + void highlightOverloadedOperatorFunctionNotation(FunctionDecl *FD, + SourceLocation Loc) { + auto HighlightingKind = kindForDecl(FD, H.getResolver()); + if (!HighlightingKind || Loc.isInvalid()) + return; + + Range OpRange = *H.getRangeForSourceLocation(Loc); + + if (!HighlightingKind || Loc.isInvalid()) + return; + // Also highlight the operator token as part of the function name. + // E.g. `<<` in `operator<<`. + expandOverloadedOperator(Loc, OpRange); + + auto &OpKeywTok = H.addToken(OpRange, *HighlightingKind); + if (auto Mod = scopeModifier(FD)) + OpKeywTok.addModifier(*Mod); + if (isConst(FD)) + OpKeywTok.addModifier(HighlightingModifier::Readonly); + if (isStatic(FD)) + OpKeywTok.addModifier(HighlightingModifier::Static); + if (isAbstract(FD)) + OpKeywTok.addModifier(HighlightingModifier::Abstract); + if (isVirtual(FD)) + OpKeywTok.addModifier(HighlightingModifier::Virtual); + if (isDependent(FD)) + OpKeywTok.addModifier(HighlightingModifier::DependentName); + if (isDefaultLibrary(FD)) + OpKeywTok.addModifier(HighlightingModifier::DefaultLibrary); + if (FD->isDeprecated()) + OpKeywTok.addModifier(HighlightingModifier::Deprecated); + } + bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) { if (auto K = kindForType(L.getTypePtr(), H.getResolver())) { auto &Tok = H.addToken(L.getBeginLoc(), *K) Index: clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -109,7 +109,7 @@ $Class[[AS]] $LocalVariable_decl[[AA]]; $Primitive_deduced_defaultLibrary[[auto]] $LocalVariable_decl[[L]] = $LocalVariable[[AA]].$Field[[SomeMember]] + $Parameter[[A]]; auto $LocalVariable_decl[[FN]] = [ $LocalVariable[[AA]]](int $Parameter_decl[[A]]) -> void {}; - $LocalVariable[[FN]](12312); + $LocalVariable[[FN]]$Method_readonly[[(]]12312$Method_readonly[[)]]; } )cpp", R"cpp( @@ -138,7 +138,7 @@ struct $Class_decl[[B]] { $Class_decl[[B]](); ~$Class[[B]](); // FIXME: inconsistent with constructor - void operator<<($Class[[B]]); + void $Method_decl[[operator<<]]($Class[[B]]); $Class[[AAA]] $Field_decl[[AA]]; }; $Class[[B]]::$Class_decl[[B]]() {} @@ -289,10 +289,10 @@ struct $Class_decl[[B]] {}; struct $Class_decl[[A]] { $Class[[B]] $Field_decl[[BB]]; - $Class[[A]] &operator=($Class[[A]] &&$Parameter_decl[[O]]); + $Class[[A]] &$Method_decl[[operator=]]($Class[[A]] &&$Parameter_decl[[O]]); }; - $Class[[A]] &$Class[[A]]::operator=($Class[[A]] &&$Parameter_decl[[O]]) = default; + $Class[[A]] &$Class[[A]]::$Method_decl[[operator=]]($Class[[A]] &&$Parameter_decl[[O]]) = default; )cpp", R"cpp( enum $Enum_decl[[En]] { @@ -777,7 +777,7 @@ void $Function_decl[[foo]]() { int $LocalVariable_decl[[a]], $LocalVariable_decl[[b]]; [ $LocalVariable_decl[[c]] = $LocalVariable[[a]], - $LocalVariable_decl[[d]]($LocalVariable[[b]]) ]() {}(); + $LocalVariable_decl[[d]]($LocalVariable[[b]]) ]() {}$Method_readonly[[(]]$Method_readonly[[)]]; } )cpp", // Enum base specifier @@ -790,6 +790,34 @@ typedef int $Primitive_decl[[MyTypedef]]; enum $Enum_decl[[MyEnum]] : $Primitive[[MyTypedef]] {}; )cpp", + // Overloaded operators + R"cpp( + struct $Class_decl[[Foo]] { + bool $Method_decl[[operator()]]() { return true; } + void $Method_decl[[operator<<]](int $Parameter_decl[[K]]) {} + operator bool() {} // FIXME: consider how this should be highlighted. + }; + + namespace $Namespace_decl[[namesp]] { + void $Function_decl[[operator--]]($Class[[Foo]] $Parameter_decl[[F]]) + {} + }; + + auto $Variable_decl[[a]] = + &$Namespace[[namesp]]::$Function[[operator--]]; + + void $Function_decl[[operator++]]($Class[[Foo]] &$Parameter_decl[[F]]) + {} + + int $Function_decl[[main]]() { + $Class[[Foo]] $LocalVariable_decl[[foo]]; + $LocalVariable[[foo]].$Method[[operator()]](); + $LocalVariable[[foo]].$Method[[operator<<]](1); + + $Function[[operator++]]($LocalVariable_usedAsMutableReference[[foo]]); + $Function[[operator delete[]]]($Function[[operator new[]]](1)); + } + )cpp", }; for (const auto &TestCase : TestCases) // Mask off scope modifiers to keep the tests manageable.