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,6 +451,8 @@ } 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) { @@ -527,13 +531,135 @@ 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 a HighlightingToken to another SourceLocation. The token at the + // given location will be highlighted exactly the same way. + void expandToken(HighlightingToken &OldTok, SourceLocation Loc) { + if (Loc.isInvalid()) + return; + + auto &NewTok = H.addToken(Loc, OldTok.Kind); + NewTok.Modifiers = OldTok.Modifiers; + } + + // 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, + HighlightingToken &OpKeywTok) { + if (Loc.isInvalid()) + return; + + const auto *NextTok = nextToken(Loc); + if (!NextTok) + return; + + expandToken(OpKeywTok, 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(OpKeywTok, 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(OpKeywTok, NextTok->location()); // `[`. + const auto *ParenEndTok = nextToken(NextTok->location()); + if (ParenEndTok) + expandToken(OpKeywTok, 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.isMacroID()) + return true; + + auto &Kind = *HighlightingKind; + auto &OpKeywTok = H.addToken(Loc, Kind); + + // Also highlight the operator token as part of the function name. + // E.g. `<<` in `operator<<`. + expandOverloadedOperator(Loc, OpKeywTok); + } + + 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; @@ -547,9 +673,6 @@ void highlightMutableReferenceArguments(const FunctionDecl *FD, llvm::ArrayRef Args) { - if (!FD) - return; - if (auto *ProtoType = FD->getType()->getAs()) { // Iterate over the types of the function parameters. // If any of them are non-const reference paramteres, add it as a @@ -585,6 +708,85 @@ } } + 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) + return; + + auto &OpKeywTok = H.addToken(Loc, *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); + + if (!HighlightingKind) + return; + // Also highlight the operator token as part of the function name. + // E.g. `<<` in `operator<<`. + expandOverloadedOperator(Loc, OpKeywTok); + } + bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) { if (auto K = kindForType(L.getTypePtr(), H.getResolver())) { auto &Tok = H.addToken(L.getBeginLoc(), *K)