Changeset View
Changeset View
Standalone View
Standalone View
clang-tools-extra/clangd/SemanticHighlighting.cpp
Show All 13 Lines | |||||
#include "SourceCode.h" | #include "SourceCode.h" | ||||
#include "support/Logger.h" | #include "support/Logger.h" | ||||
#include "clang/AST/ASTContext.h" | #include "clang/AST/ASTContext.h" | ||||
#include "clang/AST/Decl.h" | #include "clang/AST/Decl.h" | ||||
#include "clang/AST/DeclCXX.h" | #include "clang/AST/DeclCXX.h" | ||||
#include "clang/AST/DeclObjC.h" | #include "clang/AST/DeclObjC.h" | ||||
#include "clang/AST/DeclTemplate.h" | #include "clang/AST/DeclTemplate.h" | ||||
#include "clang/AST/DeclarationName.h" | #include "clang/AST/DeclarationName.h" | ||||
#include "clang/AST/Expr.h" | |||||
#include "clang/AST/ExprCXX.h" | #include "clang/AST/ExprCXX.h" | ||||
#include "clang/AST/RecursiveASTVisitor.h" | #include "clang/AST/RecursiveASTVisitor.h" | ||||
#include "clang/AST/Type.h" | #include "clang/AST/Type.h" | ||||
#include "clang/AST/TypeLoc.h" | #include "clang/AST/TypeLoc.h" | ||||
#include "clang/Basic/LangOptions.h" | #include "clang/Basic/LangOptions.h" | ||||
#include "clang/Basic/SourceLocation.h" | #include "clang/Basic/SourceLocation.h" | ||||
#include "clang/Basic/SourceManager.h" | #include "clang/Basic/SourceManager.h" | ||||
#include "clang/Basic/TokenKinds.h" | |||||
#include "clang/Tooling/Syntax/Tokens.h" | #include "clang/Tooling/Syntax/Tokens.h" | ||||
#include "llvm/ADT/None.h" | #include "llvm/ADT/None.h" | ||||
#include "llvm/ADT/Optional.h" | #include "llvm/ADT/Optional.h" | ||||
#include "llvm/ADT/STLExtras.h" | #include "llvm/ADT/STLExtras.h" | ||||
#include "llvm/Support/Base64.h" | #include "llvm/Support/Base64.h" | ||||
#include "llvm/Support/Casting.h" | #include "llvm/Support/Casting.h" | ||||
#include <algorithm> | #include <algorithm> | ||||
▲ Show 20 Lines • Show All 407 Lines • ▼ Show 20 Lines | std::vector<HighlightingToken> collect(ParsedAST &AST) && { | ||||
} | } | ||||
// Copy tokens after the last inactive line | // Copy tokens after the last inactive line | ||||
for (; It != NonConflicting.end(); ++It) | for (; It != NonConflicting.end(); ++It) | ||||
WithInactiveLines.push_back(std::move(*It)); | WithInactiveLines.push_back(std::move(*It)); | ||||
return WithInactiveLines; | return WithInactiveLines; | ||||
} | } | ||||
const HeuristicResolver *getResolver() const { return Resolver; } | const HeuristicResolver *getResolver() const { return Resolver; } | ||||
const SourceManager &getSourceMgr() const { return SourceMgr; } | |||||
const syntax::TokenBuffer &getTB() const { return TB; } | |||||
private: | |||||
llvm::Optional<Range> getRangeForSourceLocation(SourceLocation Loc) { | llvm::Optional<Range> getRangeForSourceLocation(SourceLocation Loc) { | ||||
Loc = getHighlightableSpellingToken(Loc, SourceMgr); | Loc = getHighlightableSpellingToken(Loc, SourceMgr); | ||||
if (Loc.isInvalid()) | if (Loc.isInvalid()) | ||||
return llvm::None; | return llvm::None; | ||||
const auto *Tok = TB.spelledTokenAt(Loc); | const auto *Tok = TB.spelledTokenAt(Loc); | ||||
assert(Tok); | assert(Tok); | ||||
return halfOpenToRange(SourceMgr, | return halfOpenToRange(SourceMgr, | ||||
Tok->range(SourceMgr).toCharRange(SourceMgr)); | Tok->range(SourceMgr).toCharRange(SourceMgr)); | ||||
} | } | ||||
private: | |||||
const syntax::TokenBuffer &TB; | const syntax::TokenBuffer &TB; | ||||
const SourceManager &SourceMgr; | const SourceManager &SourceMgr; | ||||
const LangOptions &LangOpts; | const LangOptions &LangOpts; | ||||
std::vector<HighlightingToken> Tokens; | std::vector<HighlightingToken> Tokens; | ||||
std::map<Range, llvm::SmallVector<HighlightingModifier, 1>> ExtraModifiers; | std::map<Range, llvm::SmallVector<HighlightingModifier, 1>> ExtraModifiers; | ||||
const HeuristicResolver *Resolver = nullptr; | const HeuristicResolver *Resolver = nullptr; | ||||
// returned from addToken(InvalidLoc) | // returned from addToken(InvalidLoc) | ||||
HighlightingToken InvalidHighlightingToken; | HighlightingToken InvalidHighlightingToken; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | public: | ||||
bool VisitCXXConstructExpr(CXXConstructExpr *E) { | bool VisitCXXConstructExpr(CXXConstructExpr *E) { | ||||
highlightMutableReferenceArguments(E->getConstructor(), | highlightMutableReferenceArguments(E->getConstructor(), | ||||
{E->getArgs(), E->getNumArgs()}); | {E->getArgs(), E->getNumArgs()}); | ||||
return true; | 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<FunctionDecl>(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) { | bool VisitCallExpr(CallExpr *E) { | ||||
// Highlighting parameters passed by non-const reference does not really | // Highlighting parameters passed by non-const reference does not really | ||||
// make sense for literals... | // make sense for literals... | ||||
if (isa<UserDefinedLiteral>(E)) | if (isa<UserDefinedLiteral>(E)) | ||||
return true; | return true; | ||||
// FIXME ...here it would make sense though. | // FIXME ...here it would make sense though. | ||||
nridge: This FIXME was specifically about highlighting mutable reference arguments for overloaded… | |||||
if (isa<CXXOperatorCallExpr>(E)) | if (isa<CXXOperatorCallExpr>(E)) | ||||
return true; | return true; | ||||
highlightMutableReferenceArguments( | highlightMutableReferenceArguments( | ||||
dyn_cast_or_null<FunctionDecl>(E->getCalleeDecl()), | dyn_cast_or_null<FunctionDecl>(E->getCalleeDecl()), | ||||
{E->getArgs(), E->getNumArgs()}); | {E->getArgs(), E->getNumArgs()}); | ||||
return true; | return true; | ||||
Show All 35 Lines | if (auto *ProtoType = FD->getType()->getAs<FunctionProtoType>()) { | ||||
H.addExtraModifier(*Location, | H.addExtraModifier(*Location, | ||||
HighlightingModifier::UsedAsMutableReference); | HighlightingModifier::UsedAsMutableReference); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool VisitDeclRefExpr(DeclRefExpr *DRE) { | |||||
if (auto *FD = dyn_cast<FunctionDecl>(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<FunctionDecl>(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) { | bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) { | ||||
if (auto K = kindForType(L.getTypePtr(), H.getResolver())) { | if (auto K = kindForType(L.getTypePtr(), H.getResolver())) { | ||||
auto &Tok = H.addToken(L.getBeginLoc(), *K) | auto &Tok = H.addToken(L.getBeginLoc(), *K) | ||||
.addModifier(HighlightingModifier::Deduced); | .addModifier(HighlightingModifier::Deduced); | ||||
if (auto Mod = scopeModifier(L.getTypePtr())) | if (auto Mod = scopeModifier(L.getTypePtr())) | ||||
Tok.addModifier(*Mod); | Tok.addModifier(*Mod); | ||||
if (isDefaultLibrary(L.getTypePtr())) | if (isDefaultLibrary(L.getTypePtr())) | ||||
Tok.addModifier(HighlightingModifier::DefaultLibrary); | Tok.addModifier(HighlightingModifier::DefaultLibrary); | ||||
▲ Show 20 Lines • Show All 458 Lines • Show Last 20 Lines |
This FIXME was specifically about highlighting mutable reference arguments for overloaded operator calls, so it remains unfixed. (I realize that wasn't super clear.)