2019-06-27 12:22:18 +00:00
|
|
|
//===--- SemanticHighlighting.cpp - ------------------------- ---*- C++ -*-===//
|
2019-06-26 13:08:36 +00:00
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "SemanticHighlighting.h"
|
2023-05-26 03:15:10 -04:00
|
|
|
#include "Config.h"
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
#include "FindTarget.h"
|
2019-09-04 09:46:06 +00:00
|
|
|
#include "ParsedAST.h"
|
2019-07-04 07:53:12 +00:00
|
|
|
#include "Protocol.h"
|
2019-06-26 13:08:36 +00:00
|
|
|
#include "SourceCode.h"
|
[clangd] Move non-clang base pieces into separate support/ lib. NFCI
Summary:
This enforces layering, reduces a sprawling clangd/ directory, and makes life
easier for embedders.
Reviewers: kbobyrev
Subscribers: mgorny, ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, jfb, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D79014
2020-04-28 17:49:17 +02:00
|
|
|
#include "support/Logger.h"
|
2019-06-26 13:08:36 +00:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2019-08-19 07:51:39 +00:00
|
|
|
#include "clang/AST/Decl.h"
|
2019-08-09 12:19:10 +00:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2021-01-27 09:47:17 +01:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
|
|
|
#include "clang/AST/DeclTemplate.h"
|
2019-09-16 16:16:03 +00:00
|
|
|
#include "clang/AST/DeclarationName.h"
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
#include "clang/AST/ExprCXX.h"
|
2019-06-26 13:08:36 +00:00
|
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
[clangd] Highlight typedefs to template parameters as template parameters
Summary:
Template parameters were handled outside `addType`, this led to lack of highlightings for typedefs
to template types.
This was never desirable, we want to highlight our typedefs as their underlying type.
Note that typedefs to more complicated types, like pointers and references are still not highlighted.
Original patch by Johan Vikström.
Reviewers: hokein, jvikstrom
Reviewed By: hokein
Subscribers: nridge, javed.absar, kristof.beyls, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D66516
llvm-svn: 371379
2019-09-09 09:37:17 +00:00
|
|
|
#include "clang/AST/Type.h"
|
|
|
|
#include "clang/AST/TypeLoc.h"
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2019-09-09 14:33:10 +00:00
|
|
|
#include "clang/Basic/SourceLocation.h"
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2025-01-17 17:01:00 -05:00
|
|
|
#include "clang/Sema/HeuristicResolver.h"
|
2020-03-01 16:04:07 +01:00
|
|
|
#include "clang/Tooling/Syntax/Tokens.h"
|
2019-09-24 18:17:55 -04:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2022-06-15 15:44:09 +02:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
#include "llvm/Support/Casting.h"
|
2022-06-15 15:44:09 +02:00
|
|
|
#include "llvm/Support/Error.h"
|
2019-08-01 08:08:44 +00:00
|
|
|
#include <algorithm>
|
2023-01-07 20:02:20 -08:00
|
|
|
#include <optional>
|
2019-06-26 13:08:36 +00:00
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
namespace {
|
|
|
|
|
2023-05-23 04:17:09 -04:00
|
|
|
/// Get the last Position on a given line.
|
|
|
|
llvm::Expected<Position> endOfLine(llvm::StringRef Code, int Line) {
|
|
|
|
auto StartOfLine = positionToOffset(Code, Position{Line, 0});
|
|
|
|
if (!StartOfLine)
|
|
|
|
return StartOfLine.takeError();
|
|
|
|
StringRef LineText = Code.drop_front(*StartOfLine).take_until([](char C) {
|
|
|
|
return C == '\n';
|
|
|
|
});
|
|
|
|
return Position{Line, static_cast<int>(lspLength(LineText))};
|
|
|
|
}
|
|
|
|
|
2019-09-16 16:16:03 +00:00
|
|
|
/// Some names are not written in the source code and cannot be highlighted,
|
|
|
|
/// e.g. anonymous classes. This function detects those cases.
|
|
|
|
bool canHighlightName(DeclarationName Name) {
|
2021-02-27 22:08:07 +01:00
|
|
|
switch (Name.getNameKind()) {
|
|
|
|
case DeclarationName::Identifier: {
|
|
|
|
auto *II = Name.getAsIdentifierInfo();
|
|
|
|
return II && !II->getName().empty();
|
|
|
|
}
|
|
|
|
case DeclarationName::CXXConstructorName:
|
|
|
|
case DeclarationName::CXXDestructorName:
|
2019-09-16 16:16:03 +00:00
|
|
|
return true;
|
2021-02-27 22:08:07 +01:00
|
|
|
case DeclarationName::ObjCZeroArgSelector:
|
|
|
|
case DeclarationName::ObjCOneArgSelector:
|
|
|
|
case DeclarationName::ObjCMultiArgSelector:
|
|
|
|
// Multi-arg selectors need special handling, and we handle 0/1 arg
|
|
|
|
// selectors there too.
|
|
|
|
return false;
|
|
|
|
case DeclarationName::CXXConversionFunctionName:
|
|
|
|
case DeclarationName::CXXOperatorName:
|
|
|
|
case DeclarationName::CXXDeductionGuideName:
|
|
|
|
case DeclarationName::CXXLiteralOperatorName:
|
|
|
|
case DeclarationName::CXXUsingDirective:
|
|
|
|
return false;
|
|
|
|
}
|
2021-03-05 11:24:55 +08:00
|
|
|
llvm_unreachable("invalid name kind");
|
2019-09-16 16:16:03 +00:00
|
|
|
}
|
|
|
|
|
2022-10-17 17:12:39 -04:00
|
|
|
bool isUniqueDefinition(const NamedDecl *Decl) {
|
|
|
|
if (auto *Func = dyn_cast<FunctionDecl>(Decl))
|
|
|
|
return Func->isThisDeclarationADefinition();
|
|
|
|
if (auto *Klass = dyn_cast<CXXRecordDecl>(Decl))
|
|
|
|
return Klass->isThisDeclarationADefinition();
|
|
|
|
if (auto *Iface = dyn_cast<ObjCInterfaceDecl>(Decl))
|
|
|
|
return Iface->isThisDeclarationADefinition();
|
|
|
|
if (auto *Proto = dyn_cast<ObjCProtocolDecl>(Decl))
|
|
|
|
return Proto->isThisDeclarationADefinition();
|
|
|
|
if (auto *Var = dyn_cast<VarDecl>(Decl))
|
|
|
|
return Var->isThisDeclarationADefinition();
|
|
|
|
return isa<TemplateTypeParmDecl>(Decl) ||
|
|
|
|
isa<NonTypeTemplateParmDecl>(Decl) ||
|
|
|
|
isa<TemplateTemplateParmDecl>(Decl) || isa<ObjCCategoryDecl>(Decl) ||
|
|
|
|
isa<ObjCImplDecl>(Decl);
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<HighlightingKind> kindForType(const Type *TP,
|
|
|
|
const HeuristicResolver *Resolver);
|
|
|
|
std::optional<HighlightingKind> kindForDecl(const NamedDecl *D,
|
|
|
|
const HeuristicResolver *Resolver) {
|
2019-10-28 13:42:20 +01:00
|
|
|
if (auto *USD = dyn_cast<UsingShadowDecl>(D)) {
|
|
|
|
if (auto *Target = USD->getTargetDecl())
|
|
|
|
D = Target;
|
|
|
|
}
|
2019-10-14 18:26:13 +00:00
|
|
|
if (auto *TD = dyn_cast<TemplateDecl>(D)) {
|
|
|
|
if (auto *Templated = TD->getTemplatedDecl())
|
|
|
|
D = Templated;
|
|
|
|
}
|
2019-09-16 16:16:03 +00:00
|
|
|
if (auto *TD = dyn_cast<TypedefNameDecl>(D)) {
|
|
|
|
// We try to highlight typedefs as their underlying type.
|
2021-03-22 02:43:41 -04:00
|
|
|
if (auto K =
|
|
|
|
kindForType(TD->getUnderlyingType().getTypePtrOrNull(), Resolver))
|
2019-09-16 16:16:03 +00:00
|
|
|
return K;
|
|
|
|
// And fallback to a generic kind if this fails.
|
|
|
|
return HighlightingKind::Typedef;
|
|
|
|
}
|
|
|
|
// We highlight class decls, constructor decls and destructor decls as
|
2019-10-28 11:31:06 +01:00
|
|
|
// `Class` type. The destructor decls are handled in `VisitTagTypeLoc` (we
|
2019-09-16 16:16:03 +00:00
|
|
|
// will visit a TypeLoc where the underlying Type is a CXXRecordDecl).
|
|
|
|
if (auto *RD = llvm::dyn_cast<RecordDecl>(D)) {
|
|
|
|
// We don't want to highlight lambdas like classes.
|
|
|
|
if (RD->isLambda())
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2019-09-16 16:16:03 +00:00
|
|
|
return HighlightingKind::Class;
|
|
|
|
}
|
2021-02-27 22:08:07 +01:00
|
|
|
if (isa<ClassTemplateDecl, RecordDecl, CXXConstructorDecl, ObjCInterfaceDecl,
|
|
|
|
ObjCImplementationDecl>(D))
|
2019-09-16 16:16:03 +00:00
|
|
|
return HighlightingKind::Class;
|
2021-02-27 22:08:07 +01:00
|
|
|
if (isa<ObjCProtocolDecl>(D))
|
|
|
|
return HighlightingKind::Interface;
|
2023-06-26 12:25:56 -04:00
|
|
|
if (isa<ObjCCategoryDecl, ObjCCategoryImplDecl>(D))
|
2021-02-27 22:08:07 +01:00
|
|
|
return HighlightingKind::Namespace;
|
2019-09-16 16:16:03 +00:00
|
|
|
if (auto *MD = dyn_cast<CXXMethodDecl>(D))
|
|
|
|
return MD->isStatic() ? HighlightingKind::StaticMethod
|
|
|
|
: HighlightingKind::Method;
|
2021-02-27 22:08:07 +01:00
|
|
|
if (auto *OMD = dyn_cast<ObjCMethodDecl>(D))
|
|
|
|
return OMD->isClassMethod() ? HighlightingKind::StaticMethod
|
|
|
|
: HighlightingKind::Method;
|
2024-02-05 03:22:17 -05:00
|
|
|
if (isa<FieldDecl, IndirectFieldDecl, ObjCPropertyDecl>(D))
|
2019-09-16 16:16:03 +00:00
|
|
|
return HighlightingKind::Field;
|
|
|
|
if (isa<EnumDecl>(D))
|
|
|
|
return HighlightingKind::Enum;
|
|
|
|
if (isa<EnumConstantDecl>(D))
|
|
|
|
return HighlightingKind::EnumConstant;
|
|
|
|
if (isa<ParmVarDecl>(D))
|
|
|
|
return HighlightingKind::Parameter;
|
2021-02-27 22:08:07 +01:00
|
|
|
if (auto *VD = dyn_cast<VarDecl>(D)) {
|
|
|
|
if (isa<ImplicitParamDecl>(VD)) // e.g. ObjC Self
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2019-09-16 16:16:03 +00:00
|
|
|
return VD->isStaticDataMember()
|
|
|
|
? HighlightingKind::StaticField
|
|
|
|
: VD->isLocalVarDecl() ? HighlightingKind::LocalVariable
|
|
|
|
: HighlightingKind::Variable;
|
2021-02-27 22:08:07 +01:00
|
|
|
}
|
2020-08-07 01:31:03 -04:00
|
|
|
if (const auto *BD = dyn_cast<BindingDecl>(D))
|
|
|
|
return BD->getDeclContext()->isFunctionOrMethod()
|
|
|
|
? HighlightingKind::LocalVariable
|
|
|
|
: HighlightingKind::Variable;
|
2019-09-16 16:16:03 +00:00
|
|
|
if (isa<FunctionDecl>(D))
|
|
|
|
return HighlightingKind::Function;
|
|
|
|
if (isa<NamespaceDecl>(D) || isa<NamespaceAliasDecl>(D) ||
|
|
|
|
isa<UsingDirectiveDecl>(D))
|
|
|
|
return HighlightingKind::Namespace;
|
|
|
|
if (isa<TemplateTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
|
|
|
|
isa<NonTypeTemplateParmDecl>(D))
|
|
|
|
return HighlightingKind::TemplateParameter;
|
2020-01-21 13:21:08 -05:00
|
|
|
if (isa<ConceptDecl>(D))
|
|
|
|
return HighlightingKind::Concept;
|
2023-02-03 12:37:58 +01:00
|
|
|
if (isa<LabelDecl>(D))
|
|
|
|
return HighlightingKind::Label;
|
2021-03-22 02:43:41 -04:00
|
|
|
if (const auto *UUVD = dyn_cast<UnresolvedUsingValueDecl>(D)) {
|
|
|
|
auto Targets = Resolver->resolveUsingValueDecl(UUVD);
|
2022-09-28 09:57:56 -10:00
|
|
|
if (!Targets.empty() && Targets[0] != UUVD) {
|
2021-03-22 02:43:41 -04:00
|
|
|
return kindForDecl(Targets[0], Resolver);
|
|
|
|
}
|
2021-03-22 02:13:53 -04:00
|
|
|
return HighlightingKind::Unknown;
|
|
|
|
}
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2019-09-16 16:16:03 +00:00
|
|
|
}
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<HighlightingKind> kindForType(const Type *TP,
|
|
|
|
const HeuristicResolver *Resolver) {
|
2019-09-16 16:16:03 +00:00
|
|
|
if (!TP)
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2019-09-16 16:16:03 +00:00
|
|
|
if (TP->isBuiltinType()) // Builtins are special, they do not have decls.
|
|
|
|
return HighlightingKind::Primitive;
|
|
|
|
if (auto *TD = dyn_cast<TemplateTypeParmType>(TP))
|
2021-03-22 02:43:41 -04:00
|
|
|
return kindForDecl(TD->getDecl(), Resolver);
|
2021-02-27 22:08:07 +01:00
|
|
|
if (isa<ObjCObjectPointerType>(TP))
|
|
|
|
return HighlightingKind::Class;
|
2019-09-16 16:16:03 +00:00
|
|
|
if (auto *TD = TP->getAsTagDecl())
|
2021-03-22 02:43:41 -04:00
|
|
|
return kindForDecl(TD, Resolver);
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2019-09-16 16:16:03 +00:00
|
|
|
}
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
|
2021-01-27 09:47:17 +01:00
|
|
|
// Whether T is const in a loose sense - is a variable with this type readonly?
|
|
|
|
bool isConst(QualType T) {
|
2022-10-07 12:46:11 +02:00
|
|
|
if (T.isNull())
|
2021-01-27 09:47:17 +01:00
|
|
|
return false;
|
|
|
|
T = T.getNonReferenceType();
|
|
|
|
if (T.isConstQualified())
|
|
|
|
return true;
|
|
|
|
if (const auto *AT = T->getAsArrayTypeUnsafe())
|
|
|
|
return isConst(AT->getElementType());
|
|
|
|
if (isConst(T->getPointeeType()))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Whether D is const in a loose sense (should it be highlighted as such?)
|
|
|
|
// FIXME: This is separate from whether *a particular usage* can mutate D.
|
|
|
|
// We may want V in V.size() to be readonly even if V is mutable.
|
|
|
|
bool isConst(const Decl *D) {
|
|
|
|
if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D))
|
|
|
|
return true;
|
|
|
|
if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) ||
|
|
|
|
llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) {
|
|
|
|
if (isConst(llvm::cast<ValueDecl>(D)->getType()))
|
|
|
|
return true;
|
2019-10-14 18:26:13 +00:00
|
|
|
}
|
2021-01-27 09:47:17 +01:00
|
|
|
if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) {
|
|
|
|
if (OCPD->isReadOnly())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) {
|
|
|
|
if (!MPD->hasSetter())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
|
|
|
|
if (CMD->isConst())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "Static" means many things in C++, only some get the "static" modifier.
|
|
|
|
//
|
|
|
|
// Meanings that do:
|
|
|
|
// - Members associated with the class rather than the instance.
|
|
|
|
// This is what 'static' most often means across languages.
|
|
|
|
// - static local variables
|
|
|
|
// These are similarly "detached from their context" by the static keyword.
|
|
|
|
// In practice, these are rarely used inside classes, reducing confusion.
|
|
|
|
//
|
|
|
|
// Meanings that don't:
|
|
|
|
// - Namespace-scoped variables, which have static storage class.
|
|
|
|
// This is implicit, so the keyword "static" isn't so strongly associated.
|
|
|
|
// If we want a modifier for these, "global scope" is probably the concept.
|
|
|
|
// - Namespace-scoped variables/functions explicitly marked "static".
|
|
|
|
// There the keyword changes *linkage* , which is a totally different concept.
|
|
|
|
// If we want to model this, "file scope" would be a nice modifier.
|
|
|
|
//
|
|
|
|
// This is confusing, and maybe we should use another name, but because "static"
|
|
|
|
// is a standard LSP modifier, having one with that name has advantages.
|
|
|
|
bool isStatic(const Decl *D) {
|
|
|
|
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
|
|
|
|
return CMD->isStatic();
|
|
|
|
if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D))
|
|
|
|
return VD->isStaticDataMember() || VD->isStaticLocal();
|
|
|
|
if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D))
|
|
|
|
return OPD->isClassProperty();
|
|
|
|
if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D))
|
|
|
|
return OMD->isClassMethod();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isAbstract(const Decl *D) {
|
|
|
|
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
|
2024-01-18 15:30:58 +01:00
|
|
|
return CMD->isPureVirtual();
|
2021-01-27 09:47:17 +01:00
|
|
|
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
|
|
|
|
return CRD->hasDefinition() && CRD->isAbstract();
|
|
|
|
return false;
|
2019-10-14 18:26:13 +00:00
|
|
|
}
|
2019-09-16 16:16:03 +00:00
|
|
|
|
2021-08-03 19:53:01 -07:00
|
|
|
bool isVirtual(const Decl *D) {
|
|
|
|
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
|
|
|
|
return CMD->isVirtual();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-22 02:13:53 -04:00
|
|
|
bool isDependent(const Decl *D) {
|
|
|
|
if (isa<UnresolvedUsingValueDecl>(D))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-04-29 13:03:10 -04:00
|
|
|
/// Returns true if `Decl` is considered to be from a default/system library.
|
|
|
|
/// This currently checks the systemness of the file by include type, although
|
|
|
|
/// different heuristics may be used in the future (e.g. sysroot paths).
|
|
|
|
bool isDefaultLibrary(const Decl *D) {
|
|
|
|
SourceLocation Loc = D->getLocation();
|
|
|
|
if (!Loc.isValid())
|
|
|
|
return false;
|
|
|
|
return D->getASTContext().getSourceManager().isInSystemHeader(Loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDefaultLibrary(const Type *T) {
|
|
|
|
if (!T)
|
|
|
|
return false;
|
|
|
|
const Type *Underlying = T->getPointeeOrArrayElementType();
|
|
|
|
if (Underlying->isBuiltinType())
|
|
|
|
return true;
|
|
|
|
if (auto *TD = dyn_cast<TemplateTypeParmType>(Underlying))
|
|
|
|
return isDefaultLibrary(TD->getDecl());
|
|
|
|
if (auto *TD = Underlying->getAsTagDecl())
|
|
|
|
return isDefaultLibrary(TD);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-01 16:04:07 +01:00
|
|
|
// For a macro usage `DUMP(foo)`, we want:
|
|
|
|
// - DUMP --> "macro"
|
|
|
|
// - foo --> "variable".
|
|
|
|
SourceLocation getHighlightableSpellingToken(SourceLocation L,
|
|
|
|
const SourceManager &SM) {
|
|
|
|
if (L.isFileID())
|
|
|
|
return SM.isWrittenInMainFile(L) ? L : SourceLocation{};
|
|
|
|
// Tokens expanded from the macro body contribute no highlightings.
|
|
|
|
if (!SM.isMacroArgExpansion(L))
|
|
|
|
return {};
|
|
|
|
// Tokens expanded from macro args are potentially highlightable.
|
|
|
|
return getHighlightableSpellingToken(SM.getImmediateSpellingLoc(L), SM);
|
|
|
|
}
|
|
|
|
|
2021-01-30 01:12:36 +01:00
|
|
|
unsigned evaluateHighlightPriority(const HighlightingToken &Tok) {
|
2020-03-19 15:42:10 -04:00
|
|
|
enum HighlightPriority { Dependent = 0, Resolved = 1 };
|
2021-01-30 01:12:36 +01:00
|
|
|
return (Tok.Modifiers & (1 << uint32_t(HighlightingModifier::DependentName)))
|
2020-03-19 15:42:10 -04:00
|
|
|
? Dependent
|
|
|
|
: Resolved;
|
|
|
|
}
|
|
|
|
|
2021-01-30 01:12:36 +01:00
|
|
|
// Sometimes we get multiple tokens at the same location:
|
|
|
|
//
|
|
|
|
// - findExplicitReferences() returns a heuristic result for a dependent name
|
|
|
|
// (e.g. Method) and CollectExtraHighlighting returning a fallback dependent
|
|
|
|
// highlighting (e.g. Unknown+Dependent).
|
|
|
|
// - macro arguments are expanded multiple times and have different roles
|
|
|
|
// - broken code recovery produces several AST nodes at the same location
|
|
|
|
//
|
|
|
|
// We should either resolve these to a single token, or drop them all.
|
|
|
|
// Our heuristics are:
|
|
|
|
//
|
|
|
|
// - token kinds that come with "dependent-name" modifiers are less reliable
|
|
|
|
// (these tend to be vague, like Type or Unknown)
|
|
|
|
// - if we have multiple equally reliable kinds, drop token rather than guess
|
|
|
|
// - take the union of modifiers from all tokens
|
|
|
|
//
|
|
|
|
// In particular, heuristically resolved dependent names get their heuristic
|
|
|
|
// kind, plus the dependent modifier.
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<HighlightingToken> resolveConflict(const HighlightingToken &A,
|
|
|
|
const HighlightingToken &B) {
|
2022-03-16 04:01:43 -04:00
|
|
|
unsigned Priority1 = evaluateHighlightPriority(A);
|
|
|
|
unsigned Priority2 = evaluateHighlightPriority(B);
|
|
|
|
if (Priority1 == Priority2 && A.Kind != B.Kind)
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2022-03-16 04:01:43 -04:00
|
|
|
auto Result = Priority1 > Priority2 ? A : B;
|
|
|
|
Result.Modifiers = A.Modifiers | B.Modifiers;
|
|
|
|
return Result;
|
|
|
|
}
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<HighlightingToken>
|
2020-03-19 15:42:10 -04:00
|
|
|
resolveConflict(ArrayRef<HighlightingToken> Tokens) {
|
|
|
|
if (Tokens.size() == 1)
|
|
|
|
return Tokens[0];
|
|
|
|
|
2022-03-16 04:01:43 -04:00
|
|
|
assert(Tokens.size() >= 2);
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<HighlightingToken> Winner =
|
|
|
|
resolveConflict(Tokens[0], Tokens[1]);
|
2022-03-16 04:01:43 -04:00
|
|
|
for (size_t I = 2; Winner && I < Tokens.size(); ++I)
|
|
|
|
Winner = resolveConflict(*Winner, Tokens[I]);
|
|
|
|
return Winner;
|
2020-03-19 15:42:10 -04:00
|
|
|
}
|
|
|
|
|
2023-05-26 03:15:10 -04:00
|
|
|
/// Filter to remove particular kinds of highlighting tokens and modifiers from
|
|
|
|
/// the output.
|
|
|
|
class HighlightingFilter {
|
|
|
|
public:
|
|
|
|
HighlightingFilter() {
|
|
|
|
for (auto &Active : ActiveKindLookup)
|
|
|
|
Active = true;
|
|
|
|
|
|
|
|
ActiveModifiersMask = ~0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void disableKind(HighlightingKind Kind) {
|
|
|
|
ActiveKindLookup[static_cast<size_t>(Kind)] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void disableModifier(HighlightingModifier Modifier) {
|
|
|
|
ActiveModifiersMask &= ~(1 << static_cast<uint32_t>(Modifier));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isHighlightKindActive(HighlightingKind Kind) const {
|
|
|
|
return ActiveKindLookup[static_cast<size_t>(Kind)];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t maskModifiers(uint32_t Modifiers) const {
|
|
|
|
return Modifiers & ActiveModifiersMask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HighlightingFilter fromCurrentConfig() {
|
|
|
|
const Config &C = Config::current();
|
|
|
|
HighlightingFilter Filter;
|
|
|
|
for (const auto &Kind : C.SemanticTokens.DisabledKinds)
|
|
|
|
if (auto K = highlightingKindFromString(Kind))
|
|
|
|
Filter.disableKind(*K);
|
|
|
|
for (const auto &Modifier : C.SemanticTokens.DisabledModifiers)
|
|
|
|
if (auto M = highlightingModifierFromString(Modifier))
|
|
|
|
Filter.disableModifier(*M);
|
|
|
|
|
|
|
|
return Filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool ActiveKindLookup[static_cast<size_t>(HighlightingKind::LastKind) + 1];
|
|
|
|
uint32_t ActiveModifiersMask;
|
|
|
|
};
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
/// Consumes source locations and maps them to text ranges for highlightings.
|
|
|
|
class HighlightingsBuilder {
|
2019-06-26 13:08:36 +00:00
|
|
|
public:
|
2023-05-26 03:15:10 -04:00
|
|
|
HighlightingsBuilder(const ParsedAST &AST, const HighlightingFilter &Filter)
|
2020-03-01 16:04:07 +01:00
|
|
|
: TB(AST.getTokens()), SourceMgr(AST.getSourceManager()),
|
2023-12-10 22:05:51 -05:00
|
|
|
LangOpts(AST.getLangOpts()), Filter(Filter),
|
|
|
|
Resolver(AST.getHeuristicResolver()) {}
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
|
2021-01-27 09:47:17 +01:00
|
|
|
HighlightingToken &addToken(SourceLocation Loc, HighlightingKind Kind) {
|
2021-09-13 00:34:02 -04:00
|
|
|
auto Range = getRangeForSourceLocation(Loc);
|
|
|
|
if (!Range)
|
2021-03-22 11:18:18 +01:00
|
|
|
return InvalidHighlightingToken;
|
2021-09-13 00:34:02 -04:00
|
|
|
|
|
|
|
return addToken(*Range, Kind);
|
2021-01-27 09:47:17 +01:00
|
|
|
}
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
|
2022-07-20 10:57:59 +02:00
|
|
|
// Most of this function works around
|
|
|
|
// https://github.com/clangd/clangd/issues/871.
|
|
|
|
void addAngleBracketTokens(SourceLocation LLoc, SourceLocation RLoc) {
|
|
|
|
if (!LLoc.isValid() || !RLoc.isValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto LRange = getRangeForSourceLocation(LLoc);
|
|
|
|
if (!LRange)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// RLoc might be pointing at a virtual buffer when it's part of a `>>`
|
|
|
|
// token.
|
|
|
|
RLoc = SourceMgr.getFileLoc(RLoc);
|
|
|
|
// Make sure token is part of the main file.
|
|
|
|
RLoc = getHighlightableSpellingToken(RLoc, SourceMgr);
|
|
|
|
if (!RLoc.isValid())
|
|
|
|
return;
|
|
|
|
|
2024-06-07 11:08:25 +02:00
|
|
|
const auto *RTok = TB.spelledTokenContaining(RLoc);
|
|
|
|
// Handle `>>`. RLoc is either part of `>>` or a spelled token on its own
|
|
|
|
// `>`. If it's the former, slice to have length of 1, if latter use the
|
|
|
|
// token as-is.
|
2022-07-20 10:57:59 +02:00
|
|
|
if (!RTok || RTok->kind() == tok::greatergreater) {
|
|
|
|
Position Begin = sourceLocToPosition(SourceMgr, RLoc);
|
|
|
|
Position End = sourceLocToPosition(SourceMgr, RLoc.getLocWithOffset(1));
|
|
|
|
addToken(*LRange, HighlightingKind::Bracket);
|
|
|
|
addToken({Begin, End}, HighlightingKind::Bracket);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Easy case, we have the `>` token directly available.
|
|
|
|
if (RTok->kind() == tok::greater) {
|
|
|
|
if (auto RRange = getRangeForSourceLocation(RLoc)) {
|
|
|
|
addToken(*LRange, HighlightingKind::Bracket);
|
|
|
|
addToken(*RRange, HighlightingKind::Bracket);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 09:47:17 +01:00
|
|
|
HighlightingToken &addToken(Range R, HighlightingKind Kind) {
|
2023-05-26 03:15:10 -04:00
|
|
|
if (!Filter.isHighlightKindActive(Kind))
|
|
|
|
return InvalidHighlightingToken;
|
|
|
|
|
2021-01-27 09:47:17 +01:00
|
|
|
HighlightingToken HT;
|
|
|
|
HT.R = std::move(R);
|
|
|
|
HT.Kind = Kind;
|
|
|
|
Tokens.push_back(std::move(HT));
|
|
|
|
return Tokens.back();
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
}
|
|
|
|
|
2021-09-13 00:34:02 -04:00
|
|
|
void addExtraModifier(SourceLocation Loc, HighlightingModifier Modifier) {
|
|
|
|
if (auto Range = getRangeForSourceLocation(Loc))
|
|
|
|
ExtraModifiers[*Range].push_back(Modifier);
|
|
|
|
}
|
|
|
|
|
2019-09-24 18:17:55 -04:00
|
|
|
std::vector<HighlightingToken> collect(ParsedAST &AST) && {
|
2019-07-15 15:08:27 +00:00
|
|
|
// Initializer lists can give duplicates of tokens, therefore all tokens
|
|
|
|
// must be deduplicated.
|
2019-08-01 08:08:44 +00:00
|
|
|
llvm::sort(Tokens);
|
2019-07-15 15:08:27 +00:00
|
|
|
auto Last = std::unique(Tokens.begin(), Tokens.end());
|
|
|
|
Tokens.erase(Last, Tokens.end());
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
|
2019-08-19 16:27:49 +00:00
|
|
|
// Macros can give tokens that have the same source range but conflicting
|
|
|
|
// kinds. In this case all tokens sharing this source range should be
|
|
|
|
// removed.
|
|
|
|
std::vector<HighlightingToken> NonConflicting;
|
|
|
|
NonConflicting.reserve(Tokens.size());
|
|
|
|
for (ArrayRef<HighlightingToken> TokRef = Tokens; !TokRef.empty();) {
|
|
|
|
ArrayRef<HighlightingToken> Conflicting =
|
|
|
|
TokRef.take_while([&](const HighlightingToken &T) {
|
|
|
|
// TokRef is guaranteed at least one element here because otherwise
|
|
|
|
// this predicate would never fire.
|
|
|
|
return T.R == TokRef.front().R;
|
|
|
|
});
|
2021-09-13 00:34:02 -04:00
|
|
|
if (auto Resolved = resolveConflict(Conflicting)) {
|
|
|
|
// Apply extra collected highlighting modifiers
|
|
|
|
auto Modifiers = ExtraModifiers.find(Resolved->R);
|
|
|
|
if (Modifiers != ExtraModifiers.end()) {
|
|
|
|
for (HighlightingModifier Mod : Modifiers->second) {
|
|
|
|
Resolved->addModifier(Mod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 03:15:10 -04:00
|
|
|
Resolved->Modifiers = Filter.maskModifiers(Resolved->Modifiers);
|
2020-03-19 15:42:10 -04:00
|
|
|
NonConflicting.push_back(*Resolved);
|
2021-09-13 00:34:02 -04:00
|
|
|
}
|
2019-08-19 16:27:49 +00:00
|
|
|
// TokRef[Conflicting.size()] is the next token with a different range (or
|
|
|
|
// the end of the Tokens).
|
|
|
|
TokRef = TokRef.drop_front(Conflicting.size());
|
|
|
|
}
|
2021-09-13 00:34:02 -04:00
|
|
|
|
2023-05-26 03:15:10 -04:00
|
|
|
if (!Filter.isHighlightKindActive(HighlightingKind::InactiveCode))
|
2022-04-05 03:19:15 -04:00
|
|
|
return NonConflicting;
|
|
|
|
|
2020-08-26 10:50:31 +02:00
|
|
|
const auto &SM = AST.getSourceManager();
|
2020-10-14 14:36:00 -04:00
|
|
|
StringRef MainCode = SM.getBufferOrFake(SM.getMainFileID()).getBuffer();
|
2020-08-26 10:50:31 +02:00
|
|
|
|
|
|
|
// Merge token stream with "inactive line" markers.
|
|
|
|
std::vector<HighlightingToken> WithInactiveLines;
|
2023-05-23 04:17:09 -04:00
|
|
|
auto SortedInactiveRegions = getInactiveRegions(AST);
|
|
|
|
llvm::sort(SortedInactiveRegions);
|
2020-08-26 10:50:31 +02:00
|
|
|
auto It = NonConflicting.begin();
|
2023-05-23 04:17:09 -04:00
|
|
|
for (const Range &R : SortedInactiveRegions) {
|
|
|
|
// Create one token for each line in the inactive range, so it works
|
2019-09-24 18:17:55 -04:00
|
|
|
// with line-based diffing.
|
|
|
|
assert(R.start.line <= R.end.line);
|
2020-07-09 14:56:06 -07:00
|
|
|
for (int Line = R.start.line; Line <= R.end.line; ++Line) {
|
2020-08-26 10:50:31 +02:00
|
|
|
// Copy tokens before the inactive line
|
|
|
|
for (; It != NonConflicting.end() && It->R.start.line < Line; ++It)
|
|
|
|
WithInactiveLines.push_back(std::move(*It));
|
|
|
|
// Add a token for the inactive line itself.
|
2023-05-23 04:17:09 -04:00
|
|
|
auto EndOfLine = endOfLine(MainCode, Line);
|
|
|
|
if (EndOfLine) {
|
2021-01-27 09:47:17 +01:00
|
|
|
HighlightingToken HT;
|
|
|
|
WithInactiveLines.emplace_back();
|
|
|
|
WithInactiveLines.back().Kind = HighlightingKind::InactiveCode;
|
|
|
|
WithInactiveLines.back().R.start.line = Line;
|
2023-05-23 04:17:09 -04:00
|
|
|
WithInactiveLines.back().R.end = *EndOfLine;
|
2020-08-26 10:50:31 +02:00
|
|
|
} else {
|
2023-05-23 04:17:09 -04:00
|
|
|
elog("Failed to determine end of line: {0}", EndOfLine.takeError());
|
2020-08-26 10:50:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Skip any other tokens on the inactive line. e.g.
|
|
|
|
// `#ifndef Foo` is considered as part of an inactive region when Foo is
|
|
|
|
// defined, and there is a Foo macro token.
|
|
|
|
// FIXME: we should reduce the scope of the inactive region to not
|
|
|
|
// include the directive itself.
|
|
|
|
while (It != NonConflicting.end() && It->R.start.line == Line)
|
|
|
|
++It;
|
2019-09-24 18:17:55 -04:00
|
|
|
}
|
|
|
|
}
|
2020-08-26 10:50:31 +02:00
|
|
|
// Copy tokens after the last inactive line
|
|
|
|
for (; It != NonConflicting.end(); ++It)
|
|
|
|
WithInactiveLines.push_back(std::move(*It));
|
|
|
|
return WithInactiveLines;
|
2019-06-26 13:08:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-22 02:43:41 -04:00
|
|
|
const HeuristicResolver *getResolver() const { return Resolver; }
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
private:
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<Range> getRangeForSourceLocation(SourceLocation Loc) {
|
2021-09-13 00:34:02 -04:00
|
|
|
Loc = getHighlightableSpellingToken(Loc, SourceMgr);
|
|
|
|
if (Loc.isInvalid())
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2023-02-07 12:40:00 +01:00
|
|
|
// We might have offsets in the main file that don't correspond to any
|
|
|
|
// spelled tokens.
|
2024-06-07 11:08:25 +02:00
|
|
|
const auto *Tok = TB.spelledTokenContaining(Loc);
|
2023-02-07 12:40:00 +01:00
|
|
|
if (!Tok)
|
|
|
|
return std::nullopt;
|
2021-09-13 00:34:02 -04:00
|
|
|
return halfOpenToRange(SourceMgr,
|
|
|
|
Tok->range(SourceMgr).toCharRange(SourceMgr));
|
|
|
|
}
|
|
|
|
|
2020-03-01 16:04:07 +01:00
|
|
|
const syntax::TokenBuffer &TB;
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
const SourceManager &SourceMgr;
|
|
|
|
const LangOptions &LangOpts;
|
2023-05-26 03:15:10 -04:00
|
|
|
HighlightingFilter Filter;
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
std::vector<HighlightingToken> Tokens;
|
2021-09-13 00:34:02 -04:00
|
|
|
std::map<Range, llvm::SmallVector<HighlightingModifier, 1>> ExtraModifiers;
|
2023-12-10 22:05:51 -05:00
|
|
|
const HeuristicResolver *Resolver;
|
2021-03-22 11:18:18 +01:00
|
|
|
// returned from addToken(InvalidLoc)
|
|
|
|
HighlightingToken InvalidHighlightingToken;
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
};
|
2019-07-05 13:06:03 +00:00
|
|
|
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<HighlightingModifier> scopeModifier(const NamedDecl *D) {
|
2021-01-29 23:18:34 +01:00
|
|
|
const DeclContext *DC = D->getDeclContext();
|
|
|
|
// Injected "Foo" within the class "Foo" has file scope, not class scope.
|
|
|
|
if (auto *R = dyn_cast_or_null<RecordDecl>(D))
|
|
|
|
if (R->isInjectedClassName())
|
|
|
|
DC = DC->getParent();
|
|
|
|
// Lambda captures are considered function scope, not class scope.
|
|
|
|
if (llvm::isa<FieldDecl>(D))
|
|
|
|
if (const auto *RD = llvm::dyn_cast<RecordDecl>(DC))
|
|
|
|
if (RD->isLambda())
|
|
|
|
return HighlightingModifier::FunctionScope;
|
|
|
|
// Walk up the DeclContext hierarchy until we find something interesting.
|
|
|
|
for (; !DC->isFileContext(); DC = DC->getParent()) {
|
|
|
|
if (DC->isFunctionOrMethod())
|
|
|
|
return HighlightingModifier::FunctionScope;
|
|
|
|
if (DC->isRecord())
|
|
|
|
return HighlightingModifier::ClassScope;
|
|
|
|
}
|
|
|
|
// Some template parameters (e.g. those for variable templates) don't have
|
|
|
|
// meaningful DeclContexts. That doesn't mean they're global!
|
|
|
|
if (DC->isTranslationUnit() && D->isTemplateParameter())
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2021-01-29 23:18:34 +01:00
|
|
|
// ExternalLinkage threshold could be tweaked, e.g. module-visible as global.
|
2023-11-02 20:57:29 +04:00
|
|
|
if (llvm::to_underlying(D->getLinkageInternal()) <
|
|
|
|
llvm::to_underlying(Linkage::External))
|
2021-01-29 23:18:34 +01:00
|
|
|
return HighlightingModifier::FileScope;
|
|
|
|
return HighlightingModifier::GlobalScope;
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<HighlightingModifier> scopeModifier(const Type *T) {
|
2021-01-29 23:18:34 +01:00
|
|
|
if (!T)
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2021-01-29 23:18:34 +01:00
|
|
|
if (T->isBuiltinType())
|
|
|
|
return HighlightingModifier::GlobalScope;
|
|
|
|
if (auto *TD = dyn_cast<TemplateTypeParmType>(T))
|
|
|
|
return scopeModifier(TD->getDecl());
|
|
|
|
if (auto *TD = T->getAsTagDecl())
|
|
|
|
return scopeModifier(TD);
|
2022-12-03 11:54:50 -08:00
|
|
|
return std::nullopt;
|
2021-01-29 23:18:34 +01:00
|
|
|
}
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
/// Produces highlightings, which are not captured by findExplicitReferences,
|
|
|
|
/// e.g. highlights dependent names and 'auto' as the underlying type.
|
|
|
|
class CollectExtraHighlightings
|
|
|
|
: public RecursiveASTVisitor<CollectExtraHighlightings> {
|
2022-07-08 04:12:31 -04:00
|
|
|
using Base = RecursiveASTVisitor<CollectExtraHighlightings>;
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
public:
|
|
|
|
CollectExtraHighlightings(HighlightingsBuilder &H) : H(H) {}
|
2019-10-28 13:42:20 +01:00
|
|
|
|
2021-09-13 00:34:02 -04:00
|
|
|
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
|
|
|
|
highlightMutableReferenceArguments(E->getConstructor(),
|
|
|
|
{E->getArgs(), E->getNumArgs()});
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-08 04:12:31 -04:00
|
|
|
bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
|
|
|
|
if (Init->isMemberInitializer())
|
|
|
|
if (auto *Member = Init->getMember())
|
|
|
|
highlightMutableReferenceArgument(Member->getType(), Init->getInit());
|
|
|
|
return Base::TraverseConstructorInitializer(Init);
|
|
|
|
}
|
|
|
|
|
2022-07-20 10:57:59 +02:00
|
|
|
bool TraverseTypeConstraint(const TypeConstraint *C) {
|
|
|
|
if (auto *Args = C->getTemplateArgsAsWritten())
|
|
|
|
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
|
|
|
|
return Base::TraverseTypeConstraint(C);
|
|
|
|
}
|
|
|
|
|
2022-08-18 16:12:50 +02:00
|
|
|
bool VisitPredefinedExpr(PredefinedExpr *E) {
|
|
|
|
H.addToken(E->getLocation(), HighlightingKind::LocalVariable)
|
|
|
|
.addModifier(HighlightingModifier::Static)
|
|
|
|
.addModifier(HighlightingModifier::Readonly)
|
|
|
|
.addModifier(HighlightingModifier::FunctionScope);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-20 10:57:59 +02:00
|
|
|
bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
|
|
|
|
if (auto *Args = E->getTemplateArgsAsWritten())
|
|
|
|
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitTemplateDecl(TemplateDecl *D) {
|
|
|
|
if (auto *TPL = D->getTemplateParameters())
|
|
|
|
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitTagDecl(TagDecl *D) {
|
|
|
|
for (unsigned i = 0; i < D->getNumTemplateParameterLists(); ++i) {
|
|
|
|
if (auto *TPL = D->getTemplateParameterList(i))
|
|
|
|
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-05-14 16:09:57 -04:00
|
|
|
bool
|
|
|
|
VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D) {
|
|
|
|
if (auto *Args = D->getTemplateArgsAsWritten())
|
|
|
|
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-20 10:57:59 +02:00
|
|
|
bool VisitClassTemplatePartialSpecializationDecl(
|
|
|
|
ClassTemplatePartialSpecializationDecl *D) {
|
|
|
|
if (auto *TPL = D->getTemplateParameters())
|
|
|
|
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *D) {
|
2024-05-14 16:09:57 -04:00
|
|
|
if (auto *Args = D->getTemplateArgsAsWritten())
|
2022-07-20 10:57:59 +02:00
|
|
|
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitVarTemplatePartialSpecializationDecl(
|
|
|
|
VarTemplatePartialSpecializationDecl *D) {
|
|
|
|
if (auto *TPL = D->getTemplateParameters())
|
|
|
|
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitDeclRefExpr(DeclRefExpr *E) {
|
|
|
|
H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool VisitMemberExpr(MemberExpr *E) {
|
|
|
|
H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) {
|
|
|
|
H.addAngleBracketTokens(L.getLAngleLoc(), L.getRAngleLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-20 18:05:44 +02:00
|
|
|
bool VisitFunctionDecl(FunctionDecl *D) {
|
|
|
|
if (D->isOverloadedOperator()) {
|
2022-12-13 13:38:36 +01:00
|
|
|
const auto AddOpDeclToken = [&](SourceLocation Loc) {
|
2022-07-20 18:05:44 +02:00
|
|
|
auto &Token = H.addToken(Loc, HighlightingKind::Operator)
|
|
|
|
.addModifier(HighlightingModifier::Declaration);
|
|
|
|
if (D->isThisDeclarationADefinition())
|
|
|
|
Token.addModifier(HighlightingModifier::Definition);
|
|
|
|
};
|
|
|
|
const auto Range = D->getNameInfo().getCXXOperatorNameRange();
|
2022-12-13 13:38:36 +01:00
|
|
|
AddOpDeclToken(Range.getBegin());
|
2022-07-20 18:05:44 +02:00
|
|
|
const auto Kind = D->getOverloadedOperator();
|
|
|
|
if (Kind == OO_Call || Kind == OO_Subscript)
|
2022-12-13 13:38:36 +01:00
|
|
|
AddOpDeclToken(Range.getEnd());
|
2022-07-20 18:05:44 +02:00
|
|
|
}
|
2022-07-20 10:57:59 +02:00
|
|
|
if (auto *Args = D->getTemplateSpecializationArgsAsWritten())
|
|
|
|
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
|
2022-07-20 18:05:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
|
2022-12-13 13:38:36 +01:00
|
|
|
const auto AddOpToken = [&](SourceLocation Loc) {
|
2022-07-20 18:05:44 +02:00
|
|
|
H.addToken(Loc, HighlightingKind::Operator)
|
|
|
|
.addModifier(HighlightingModifier::UserDefined);
|
|
|
|
};
|
2022-12-13 13:38:36 +01:00
|
|
|
AddOpToken(E->getOperatorLoc());
|
2022-07-20 18:05:44 +02:00
|
|
|
const auto Kind = E->getOperator();
|
|
|
|
if (Kind == OO_Call || Kind == OO_Subscript) {
|
|
|
|
if (auto *Callee = E->getCallee())
|
2022-12-13 13:38:36 +01:00
|
|
|
AddOpToken(Callee->getBeginLoc());
|
2022-07-20 18:05:44 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitUnaryOperator(UnaryOperator *Op) {
|
|
|
|
auto &Token = H.addToken(Op->getOperatorLoc(), HighlightingKind::Operator);
|
|
|
|
if (Op->getSubExpr()->isTypeDependent())
|
|
|
|
Token.addModifier(HighlightingModifier::UserDefined);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitBinaryOperator(BinaryOperator *Op) {
|
|
|
|
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) {
|
|
|
|
H.addToken(Op->getQuestionLoc(), HighlightingKind::Operator);
|
|
|
|
H.addToken(Op->getColonLoc(), HighlightingKind::Operator);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitCXXNewExpr(CXXNewExpr *E) {
|
|
|
|
auto &Token = H.addToken(E->getBeginLoc(), HighlightingKind::Operator);
|
2022-12-13 12:15:39 +01:00
|
|
|
if (isa_and_present<CXXMethodDecl>(E->getOperatorNew()))
|
2022-07-20 18:05:44 +02:00
|
|
|
Token.addModifier(HighlightingModifier::UserDefined);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitCXXDeleteExpr(CXXDeleteExpr *E) {
|
|
|
|
auto &Token = H.addToken(E->getBeginLoc(), HighlightingKind::Operator);
|
2022-12-13 12:15:39 +01:00
|
|
|
if (isa_and_present<CXXMethodDecl>(E->getOperatorDelete()))
|
2022-07-20 18:05:44 +02:00
|
|
|
Token.addModifier(HighlightingModifier::UserDefined);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-20 10:57:59 +02:00
|
|
|
bool VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
|
|
|
|
const auto &B = E->getAngleBrackets();
|
|
|
|
H.addAngleBracketTokens(B.getBegin(), B.getEnd());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-13 00:34:02 -04:00
|
|
|
bool VisitCallExpr(CallExpr *E) {
|
|
|
|
// Highlighting parameters passed by non-const reference does not really
|
|
|
|
// make sense for literals...
|
|
|
|
if (isa<UserDefinedLiteral>(E))
|
|
|
|
return true;
|
|
|
|
|
2022-06-29 20:12:36 -04:00
|
|
|
// FIXME: consider highlighting parameters of some other overloaded
|
|
|
|
// operators as well
|
|
|
|
llvm::ArrayRef<const Expr *> Args = {E->getArgs(), E->getNumArgs()};
|
2022-07-08 04:12:31 -04:00
|
|
|
if (auto *CallOp = dyn_cast<CXXOperatorCallExpr>(E)) {
|
|
|
|
switch (CallOp->getOperator()) {
|
2022-07-01 04:43:23 -04:00
|
|
|
case OO_Call:
|
|
|
|
case OO_Subscript:
|
|
|
|
Args = Args.drop_front(); // Drop object parameter
|
|
|
|
break;
|
|
|
|
default:
|
2022-06-29 20:12:36 -04:00
|
|
|
return true;
|
2022-07-01 04:43:23 -04:00
|
|
|
}
|
2022-06-29 20:12:36 -04:00
|
|
|
}
|
2021-09-13 00:34:02 -04:00
|
|
|
|
|
|
|
highlightMutableReferenceArguments(
|
2022-06-29 20:12:36 -04:00
|
|
|
dyn_cast_or_null<FunctionDecl>(E->getCalleeDecl()), Args);
|
2021-09-13 00:34:02 -04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-08 04:12:31 -04:00
|
|
|
void highlightMutableReferenceArgument(QualType T, const Expr *Arg) {
|
|
|
|
if (!Arg)
|
|
|
|
return;
|
|
|
|
|
2022-06-21 15:56:21 +02:00
|
|
|
// Is this parameter passed by non-const pointer or reference?
|
2022-07-08 04:12:31 -04:00
|
|
|
// FIXME The condition T->idDependentType() could be relaxed a bit,
|
|
|
|
// e.g. std::vector<T>& is dependent but we would want to highlight it
|
2022-06-21 15:56:21 +02:00
|
|
|
bool IsRef = T->isLValueReferenceType();
|
|
|
|
bool IsPtr = T->isPointerType();
|
|
|
|
if ((!IsRef && !IsPtr) || T->getPointeeType().isConstQualified() ||
|
|
|
|
T->isDependentType()) {
|
2022-07-08 04:12:31 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<SourceLocation> Location;
|
2022-07-08 04:12:31 -04:00
|
|
|
|
2022-06-21 15:56:21 +02:00
|
|
|
// FIXME Add "unwrapping" for ArraySubscriptExpr,
|
2022-07-08 04:12:31 -04:00
|
|
|
// e.g. highlight `a` in `a[i]`
|
|
|
|
// FIXME Handle dependent expression types
|
2022-06-21 15:56:21 +02:00
|
|
|
if (auto *IC = dyn_cast<ImplicitCastExpr>(Arg))
|
|
|
|
Arg = IC->getSubExprAsWritten();
|
|
|
|
if (auto *UO = dyn_cast<UnaryOperator>(Arg)) {
|
|
|
|
if (UO->getOpcode() == UO_AddrOf)
|
|
|
|
Arg = UO->getSubExpr();
|
|
|
|
}
|
2022-07-08 04:12:31 -04:00
|
|
|
if (auto *DR = dyn_cast<DeclRefExpr>(Arg))
|
|
|
|
Location = DR->getLocation();
|
|
|
|
else if (auto *M = dyn_cast<MemberExpr>(Arg))
|
|
|
|
Location = M->getMemberLoc();
|
|
|
|
|
|
|
|
if (Location)
|
|
|
|
H.addExtraModifier(*Location,
|
2022-06-21 15:56:21 +02:00
|
|
|
IsRef ? HighlightingModifier::UsedAsMutableReference
|
|
|
|
: HighlightingModifier::UsedAsMutablePointer);
|
2022-07-08 04:12:31 -04:00
|
|
|
}
|
|
|
|
|
2021-09-13 00:34:02 -04:00
|
|
|
void
|
|
|
|
highlightMutableReferenceArguments(const FunctionDecl *FD,
|
|
|
|
llvm::ArrayRef<const Expr *const> Args) {
|
|
|
|
if (!FD)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (auto *ProtoType = FD->getType()->getAs<FunctionProtoType>()) {
|
|
|
|
// Iterate over the types of the function parameters.
|
|
|
|
// If any of them are non-const reference paramteres, add it as a
|
|
|
|
// highlighting modifier to the corresponding expression
|
|
|
|
for (size_t I = 0;
|
|
|
|
I < std::min(size_t(ProtoType->getNumParams()), Args.size()); ++I) {
|
2022-07-08 04:12:31 -04:00
|
|
|
highlightMutableReferenceArgument(ProtoType->getParamType(I), Args[I]);
|
2021-09-13 00:34:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) {
|
2021-03-22 02:43:41 -04:00
|
|
|
if (auto K = kindForType(L.getTypePtr(), H.getResolver())) {
|
2021-01-29 23:18:34 +01:00
|
|
|
auto &Tok = H.addToken(L.getBeginLoc(), *K)
|
|
|
|
.addModifier(HighlightingModifier::Deduced);
|
|
|
|
if (auto Mod = scopeModifier(L.getTypePtr()))
|
|
|
|
Tok.addModifier(*Mod);
|
2021-04-29 13:03:10 -04:00
|
|
|
if (isDefaultLibrary(L.getTypePtr()))
|
|
|
|
Tok.addModifier(HighlightingModifier::DefaultLibrary);
|
2021-01-29 23:18:34 +01:00
|
|
|
}
|
2019-07-16 13:23:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-08-04 13:33:33 +02:00
|
|
|
bool VisitCXXDestructorDecl(CXXDestructorDecl *D) {
|
|
|
|
if (auto *TI = D->getNameInfo().getNamedTypeInfo()) {
|
|
|
|
SourceLocation Loc = TI->getTypeLoc().getBeginLoc();
|
|
|
|
H.addExtraModifier(Loc, HighlightingModifier::ConstructorOrDestructor);
|
|
|
|
H.addExtraModifier(Loc, HighlightingModifier::Declaration);
|
|
|
|
if (D->isThisDeclarationADefinition())
|
|
|
|
H.addExtraModifier(Loc, HighlightingModifier::Definition);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitCXXMemberCallExpr(CXXMemberCallExpr *CE) {
|
2022-10-31 09:58:58 +01:00
|
|
|
// getMethodDecl can return nullptr with member pointers, e.g.
|
|
|
|
// `(foo.*pointer_to_member_fun)(arg);`
|
2022-07-20 18:05:44 +02:00
|
|
|
if (auto *D = CE->getMethodDecl()) {
|
|
|
|
if (isa<CXXDestructorDecl>(D)) {
|
|
|
|
if (auto *ME = dyn_cast<MemberExpr>(CE->getCallee())) {
|
|
|
|
if (auto *TI = ME->getMemberNameInfo().getNamedTypeInfo()) {
|
|
|
|
H.addExtraModifier(TI->getTypeLoc().getBeginLoc(),
|
|
|
|
HighlightingModifier::ConstructorOrDestructor);
|
|
|
|
}
|
2022-08-04 13:33:33 +02:00
|
|
|
}
|
2022-07-20 18:05:44 +02:00
|
|
|
} else if (D->isOverloadedOperator()) {
|
|
|
|
if (auto *ME = dyn_cast<MemberExpr>(CE->getCallee()))
|
|
|
|
H.addToken(
|
|
|
|
ME->getMemberNameInfo().getCXXOperatorNameRange().getBegin(),
|
|
|
|
HighlightingKind::Operator)
|
|
|
|
.addModifier(HighlightingModifier::UserDefined);
|
2022-08-04 13:33:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
bool VisitDeclaratorDecl(DeclaratorDecl *D) {
|
2022-07-20 10:57:59 +02:00
|
|
|
for (unsigned i = 0; i < D->getNumTemplateParameterLists(); ++i) {
|
|
|
|
if (auto *TPL = D->getTemplateParameterList(i))
|
|
|
|
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
|
|
|
|
}
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
auto *AT = D->getType()->getContainedAutoType();
|
|
|
|
if (!AT)
|
|
|
|
return true;
|
2021-09-21 03:44:31 -04:00
|
|
|
auto K =
|
|
|
|
kindForType(AT->getDeducedType().getTypePtrOrNull(), H.getResolver());
|
|
|
|
if (!K)
|
|
|
|
return true;
|
2023-07-06 10:31:57 +02:00
|
|
|
auto *TSI = D->getTypeSourceInfo();
|
|
|
|
if (!TSI)
|
|
|
|
return true;
|
|
|
|
SourceLocation StartLoc =
|
|
|
|
TSI->getTypeLoc().getContainedAutoTypeLoc().getNameLoc();
|
2021-09-21 03:44:31 -04:00
|
|
|
// The AutoType may not have a corresponding token, e.g. in the case of
|
|
|
|
// init-captures. In this case, StartLoc overlaps with the location
|
|
|
|
// of the decl itself, and producing a token for the type here would result
|
|
|
|
// in both it and the token for the decl being dropped due to conflict.
|
|
|
|
if (StartLoc == D->getLocation())
|
|
|
|
return true;
|
2023-07-06 10:31:57 +02:00
|
|
|
|
2021-09-21 03:44:31 -04:00
|
|
|
auto &Tok =
|
|
|
|
H.addToken(StartLoc, *K).addModifier(HighlightingModifier::Deduced);
|
|
|
|
const Type *Deduced = AT->getDeducedType().getTypePtrOrNull();
|
|
|
|
if (auto Mod = scopeModifier(Deduced))
|
|
|
|
Tok.addModifier(*Mod);
|
|
|
|
if (isDefaultLibrary(Deduced))
|
|
|
|
Tok.addModifier(HighlightingModifier::DefaultLibrary);
|
2019-07-18 09:56:38 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-27 22:08:07 +01:00
|
|
|
// We handle objective-C selectors specially, because one reference can
|
|
|
|
// cover several non-contiguous tokens.
|
|
|
|
void highlightObjCSelector(const ArrayRef<SourceLocation> &Locs, bool Decl,
|
2022-10-17 17:12:39 -04:00
|
|
|
bool Def, bool Class, bool DefaultLibrary) {
|
2021-02-27 22:08:07 +01:00
|
|
|
HighlightingKind Kind =
|
|
|
|
Class ? HighlightingKind::StaticMethod : HighlightingKind::Method;
|
|
|
|
for (SourceLocation Part : Locs) {
|
|
|
|
auto &Tok =
|
|
|
|
H.addToken(Part, Kind).addModifier(HighlightingModifier::ClassScope);
|
|
|
|
if (Decl)
|
|
|
|
Tok.addModifier(HighlightingModifier::Declaration);
|
2022-10-17 17:12:39 -04:00
|
|
|
if (Def)
|
|
|
|
Tok.addModifier(HighlightingModifier::Definition);
|
2021-02-27 22:08:07 +01:00
|
|
|
if (Class)
|
|
|
|
Tok.addModifier(HighlightingModifier::Static);
|
2021-04-29 13:03:10 -04:00
|
|
|
if (DefaultLibrary)
|
|
|
|
Tok.addModifier(HighlightingModifier::DefaultLibrary);
|
2021-02-27 22:08:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
|
|
|
|
llvm::SmallVector<SourceLocation> Locs;
|
|
|
|
OMD->getSelectorLocs(Locs);
|
2022-10-17 17:12:39 -04:00
|
|
|
highlightObjCSelector(Locs, /*Decl=*/true,
|
|
|
|
OMD->isThisDeclarationADefinition(),
|
|
|
|
OMD->isClassMethod(), isDefaultLibrary(OMD));
|
2021-02-27 22:08:07 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitObjCMessageExpr(ObjCMessageExpr *OME) {
|
|
|
|
llvm::SmallVector<SourceLocation> Locs;
|
|
|
|
OME->getSelectorLocs(Locs);
|
2021-04-29 13:03:10 -04:00
|
|
|
bool DefaultLibrary = false;
|
|
|
|
if (ObjCMethodDecl *OMD = OME->getMethodDecl())
|
|
|
|
DefaultLibrary = isDefaultLibrary(OMD);
|
2022-10-17 17:12:39 -04:00
|
|
|
highlightObjCSelector(Locs, /*Decl=*/false, /*Def=*/false,
|
|
|
|
OME->isClassMessage(), DefaultLibrary);
|
2021-02-27 22:08:07 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-06-11 10:16:19 -04:00
|
|
|
// Objective-C allows you to use property syntax `self.prop` as sugar for
|
|
|
|
// `[self prop]` and `[self setProp:]` when there's no explicit `@property`
|
|
|
|
// for `prop` as well as for class properties. We treat this like a property
|
|
|
|
// even though semantically it's equivalent to a method expression.
|
|
|
|
void highlightObjCImplicitPropertyRef(const ObjCMethodDecl *OMD,
|
|
|
|
SourceLocation Loc) {
|
|
|
|
auto &Tok = H.addToken(Loc, HighlightingKind::Field)
|
|
|
|
.addModifier(HighlightingModifier::ClassScope);
|
|
|
|
if (OMD->isClassMethod())
|
|
|
|
Tok.addModifier(HighlightingModifier::Static);
|
|
|
|
if (isDefaultLibrary(OMD))
|
|
|
|
Tok.addModifier(HighlightingModifier::DefaultLibrary);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *OPRE) {
|
|
|
|
// We need to handle implicit properties here since they will appear to
|
|
|
|
// reference `ObjCMethodDecl` via an implicit `ObjCMessageExpr`, so normal
|
|
|
|
// highlighting will not work.
|
|
|
|
if (!OPRE->isImplicitProperty())
|
|
|
|
return true;
|
|
|
|
// A single property expr can reference both a getter and setter, but we can
|
|
|
|
// only provide a single semantic token, so prefer the getter. In most cases
|
|
|
|
// the end result should be the same, although it's technically possible
|
|
|
|
// that the user defines a setter for a system SDK.
|
|
|
|
if (OPRE->isMessagingGetter()) {
|
|
|
|
highlightObjCImplicitPropertyRef(OPRE->getImplicitPropertyGetter(),
|
|
|
|
OPRE->getLocation());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (OPRE->isMessagingSetter()) {
|
|
|
|
highlightObjCImplicitPropertyRef(OPRE->getImplicitPropertySetter(),
|
|
|
|
OPRE->getLocation());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
bool VisitOverloadExpr(OverloadExpr *E) {
|
2022-07-20 10:57:59 +02:00
|
|
|
H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
if (!E->decls().empty())
|
|
|
|
return true; // handled by findExplicitReferences.
|
2021-01-30 01:12:36 +01:00
|
|
|
auto &Tok = H.addToken(E->getNameLoc(), HighlightingKind::Unknown)
|
|
|
|
.addModifier(HighlightingModifier::DependentName);
|
2021-01-29 23:18:34 +01:00
|
|
|
if (llvm::isa<UnresolvedMemberExpr>(E))
|
|
|
|
Tok.addModifier(HighlightingModifier::ClassScope);
|
|
|
|
// other case is UnresolvedLookupExpr, scope is unknown.
|
2019-07-18 09:56:38 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
|
2021-01-30 01:12:36 +01:00
|
|
|
H.addToken(E->getMemberNameInfo().getLoc(), HighlightingKind::Unknown)
|
|
|
|
.addModifier(HighlightingModifier::DependentName)
|
2021-01-29 23:18:34 +01:00
|
|
|
.addModifier(HighlightingModifier::ClassScope);
|
2022-07-20 10:57:59 +02:00
|
|
|
H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
|
2019-10-28 11:31:06 +01:00
|
|
|
return true;
|
2019-09-16 16:16:03 +00:00
|
|
|
}
|
|
|
|
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
|
2021-01-30 01:12:36 +01:00
|
|
|
H.addToken(E->getNameInfo().getLoc(), HighlightingKind::Unknown)
|
|
|
|
.addModifier(HighlightingModifier::DependentName)
|
2021-01-29 23:18:34 +01:00
|
|
|
.addModifier(HighlightingModifier::ClassScope);
|
2022-07-20 10:57:59 +02:00
|
|
|
H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc());
|
2019-09-16 16:16:03 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-21 22:00:23 +01:00
|
|
|
bool VisitAttr(Attr *A) {
|
|
|
|
switch (A->getKind()) {
|
|
|
|
case attr::Override:
|
|
|
|
case attr::Final:
|
|
|
|
H.addToken(A->getLocation(), HighlightingKind::Modifier);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-10-28 11:31:06 +01:00
|
|
|
bool VisitDependentNameTypeLoc(DependentNameTypeLoc L) {
|
2021-01-30 01:12:36 +01:00
|
|
|
H.addToken(L.getNameLoc(), HighlightingKind::Type)
|
|
|
|
.addModifier(HighlightingModifier::DependentName)
|
2021-01-29 23:18:34 +01:00
|
|
|
.addModifier(HighlightingModifier::ClassScope);
|
2019-08-12 07:45:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-05 14:28:56 -05:00
|
|
|
bool VisitDependentTemplateSpecializationTypeLoc(
|
|
|
|
DependentTemplateSpecializationTypeLoc L) {
|
2021-01-30 01:12:36 +01:00
|
|
|
H.addToken(L.getTemplateNameLoc(), HighlightingKind::Type)
|
|
|
|
.addModifier(HighlightingModifier::DependentName)
|
2021-01-29 23:18:34 +01:00
|
|
|
.addModifier(HighlightingModifier::ClassScope);
|
2022-07-20 10:57:59 +02:00
|
|
|
H.addAngleBracketTokens(L.getLAngleLoc(), L.getRAngleLoc());
|
2019-12-05 14:28:56 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-04 23:33:41 -04:00
|
|
|
bool TraverseTemplateArgumentLoc(TemplateArgumentLoc L) {
|
2021-01-30 01:12:36 +01:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
TemplateName N = L.getArgument().getAsTemplateOrTemplatePattern();
|
|
|
|
switch (N.getKind()) {
|
|
|
|
case TemplateName::OverloadedTemplate:
|
|
|
|
// Template template params must always be class templates.
|
|
|
|
// Don't bother to try to work out the scope here.
|
|
|
|
H.addToken(L.getTemplateNameLoc(), HighlightingKind::Class);
|
|
|
|
break;
|
|
|
|
case TemplateName::DependentTemplate:
|
|
|
|
case TemplateName::AssumedTemplate:
|
|
|
|
H.addToken(L.getTemplateNameLoc(), HighlightingKind::Class)
|
|
|
|
.addModifier(HighlightingModifier::DependentName);
|
2020-08-04 23:33:41 -04:00
|
|
|
break;
|
2021-01-30 01:12:36 +01:00
|
|
|
case TemplateName::Template:
|
|
|
|
case TemplateName::QualifiedTemplate:
|
|
|
|
case TemplateName::SubstTemplateTemplateParm:
|
|
|
|
case TemplateName::SubstTemplateTemplateParmPack:
|
2022-04-12 15:51:16 +02:00
|
|
|
case TemplateName::UsingTemplate:
|
[clang] Implement CWG2398 provisional TTP matching to class templates (#94981)
This extends default argument deduction to cover class templates as
well, applying only to partial ordering, adding to the provisional
wording introduced in https://github.com/llvm/llvm-project/pull/89807.
This solves some ambuguity introduced in P0522 regarding how template
template parameters are partially ordered, and should reduce the
negative impact of enabling `-frelaxed-template-template-args` by
default.
Given the following example:
```C++
template <class T1, class T2 = float> struct A;
template <class T3> struct B;
template <template <class T4> class TT1, class T5> struct B<TT1<T5>>; // #1
template <class T6, class T7> struct B<A<T6, T7>>; // #2
template struct B<A<int>>;
```
Prior to P0522, `#2` was picked. Afterwards, this became ambiguous. This
patch restores the pre-P0522 behavior, `#2` is picked again.
2024-09-07 15:49:07 -03:00
|
|
|
case TemplateName::DeducedTemplate:
|
2021-01-30 01:12:36 +01:00
|
|
|
// Names that could be resolved to a TemplateDecl are handled elsewhere.
|
2020-08-04 23:33:41 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L);
|
|
|
|
}
|
|
|
|
|
2019-12-05 14:28:56 -05:00
|
|
|
// findExplicitReferences will walk nested-name-specifiers and
|
|
|
|
// find anything that can be resolved to a Decl. However, non-leaf
|
|
|
|
// components of nested-name-specifiers which are dependent names
|
|
|
|
// (kind "Identifier") cannot be resolved to a decl, so we visit
|
|
|
|
// them here.
|
|
|
|
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Q) {
|
|
|
|
if (NestedNameSpecifier *NNS = Q.getNestedNameSpecifier()) {
|
|
|
|
if (NNS->getKind() == NestedNameSpecifier::Identifier)
|
2021-01-30 01:12:36 +01:00
|
|
|
H.addToken(Q.getLocalBeginLoc(), HighlightingKind::Type)
|
|
|
|
.addModifier(HighlightingModifier::DependentName)
|
2021-01-29 23:18:34 +01:00
|
|
|
.addModifier(HighlightingModifier::ClassScope);
|
2019-12-05 14:28:56 -05:00
|
|
|
}
|
|
|
|
return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(Q);
|
|
|
|
}
|
|
|
|
|
2019-08-12 07:45:12 +00:00
|
|
|
private:
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
HighlightingsBuilder &H;
|
2019-06-26 13:08:36 +00:00
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
2022-04-05 03:19:15 -04:00
|
|
|
std::vector<HighlightingToken>
|
|
|
|
getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens) {
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
auto &C = AST.getASTContext();
|
2023-05-26 03:15:10 -04:00
|
|
|
HighlightingFilter Filter = HighlightingFilter::fromCurrentConfig();
|
|
|
|
if (!IncludeInactiveRegionTokens)
|
|
|
|
Filter.disableKind(HighlightingKind::InactiveCode);
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
// Add highlightings for AST nodes.
|
2023-05-26 03:15:10 -04:00
|
|
|
HighlightingsBuilder Builder(AST, Filter);
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
// Highlight 'decltype' and 'auto' as their underlying types.
|
|
|
|
CollectExtraHighlightings(Builder).TraverseAST(C);
|
|
|
|
// Highlight all decls and references coming from the AST.
|
2021-01-18 02:58:43 -05:00
|
|
|
findExplicitReferences(
|
|
|
|
C,
|
|
|
|
[&](ReferenceLoc R) {
|
|
|
|
for (const NamedDecl *Decl : R.Targets) {
|
|
|
|
if (!canHighlightName(Decl->getDeclName()))
|
|
|
|
continue;
|
2021-03-22 02:43:41 -04:00
|
|
|
auto Kind = kindForDecl(Decl, AST.getHeuristicResolver());
|
2021-01-18 02:58:43 -05:00
|
|
|
if (!Kind)
|
|
|
|
continue;
|
|
|
|
auto &Tok = Builder.addToken(R.NameLoc, *Kind);
|
|
|
|
|
|
|
|
// The attribute tests don't want to look at the template.
|
|
|
|
if (auto *TD = dyn_cast<TemplateDecl>(Decl)) {
|
|
|
|
if (auto *Templated = TD->getTemplatedDecl())
|
|
|
|
Decl = Templated;
|
|
|
|
}
|
|
|
|
if (auto Mod = scopeModifier(Decl))
|
|
|
|
Tok.addModifier(*Mod);
|
|
|
|
if (isConst(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::Readonly);
|
|
|
|
if (isStatic(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::Static);
|
|
|
|
if (isAbstract(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::Abstract);
|
2021-08-03 19:53:01 -07:00
|
|
|
if (isVirtual(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::Virtual);
|
2021-03-22 02:13:53 -04:00
|
|
|
if (isDependent(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::DependentName);
|
2021-04-29 13:03:10 -04:00
|
|
|
if (isDefaultLibrary(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::DefaultLibrary);
|
2021-01-18 02:58:43 -05:00
|
|
|
if (Decl->isDeprecated())
|
|
|
|
Tok.addModifier(HighlightingModifier::Deprecated);
|
2022-08-04 13:33:33 +02:00
|
|
|
if (isa<CXXConstructorDecl>(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::ConstructorOrDestructor);
|
2022-10-17 17:12:39 -04:00
|
|
|
if (R.IsDecl) {
|
|
|
|
// Do not treat an UnresolvedUsingValueDecl as a declaration.
|
|
|
|
// It's more common to think of it as a reference to the
|
|
|
|
// underlying declaration.
|
|
|
|
if (!isa<UnresolvedUsingValueDecl>(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::Declaration);
|
|
|
|
if (isUniqueDefinition(Decl))
|
|
|
|
Tok.addModifier(HighlightingModifier::Definition);
|
|
|
|
}
|
2021-01-18 02:58:43 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
AST.getHeuristicResolver());
|
2019-11-07 12:14:38 +01:00
|
|
|
// Add highlightings for macro references.
|
2021-01-27 09:47:17 +01:00
|
|
|
auto AddMacro = [&](const MacroOccurrence &M) {
|
2023-06-23 09:08:35 +02:00
|
|
|
auto &T = Builder.addToken(M.toRange(C.getSourceManager()),
|
|
|
|
HighlightingKind::Macro);
|
2021-01-29 23:18:34 +01:00
|
|
|
T.addModifier(HighlightingModifier::GlobalScope);
|
2021-01-27 09:47:17 +01:00
|
|
|
if (M.IsDefinition)
|
|
|
|
T.addModifier(HighlightingModifier::Declaration);
|
|
|
|
};
|
|
|
|
for (const auto &SIDToRefs : AST.getMacros().MacroRefs)
|
2019-11-07 12:14:38 +01:00
|
|
|
for (const auto &M : SIDToRefs.second)
|
2021-01-27 09:47:17 +01:00
|
|
|
AddMacro(M);
|
2019-11-07 12:14:38 +01:00
|
|
|
for (const auto &M : AST.getMacros().UnknownMacros)
|
2021-01-27 09:47:17 +01:00
|
|
|
AddMacro(M);
|
2019-11-07 12:14:38 +01:00
|
|
|
|
2019-09-24 18:17:55 -04:00
|
|
|
return std::move(Builder).collect(AST);
|
[clangd] Implement semantic highlightings via findExplicitReferences
Summary:
To keep the logic of finding locations of interesting AST nodes in one
place.
The advantage is better coverage of various AST nodes, both now and in
the future: as new nodes get added to `findExplicitReferences`, semantic
highlighting will automatically pick them up.
The drawback of this change is that we have to traverse declarations
inside our file twice in order to highlight dependent names, 'auto'
and 'decltype'. Hopefully, this should not affect the actual latency
too much, most time should be spent in building the AST and not
traversing it.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nridge, merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D69673
2019-11-05 19:06:12 +01:00
|
|
|
}
|
|
|
|
|
2019-09-09 08:47:05 +00:00
|
|
|
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) {
|
|
|
|
switch (K) {
|
|
|
|
case HighlightingKind::Variable:
|
|
|
|
return OS << "Variable";
|
|
|
|
case HighlightingKind::LocalVariable:
|
|
|
|
return OS << "LocalVariable";
|
|
|
|
case HighlightingKind::Parameter:
|
|
|
|
return OS << "Parameter";
|
|
|
|
case HighlightingKind::Function:
|
|
|
|
return OS << "Function";
|
|
|
|
case HighlightingKind::Method:
|
|
|
|
return OS << "Method";
|
|
|
|
case HighlightingKind::StaticMethod:
|
|
|
|
return OS << "StaticMethod";
|
|
|
|
case HighlightingKind::Field:
|
|
|
|
return OS << "Field";
|
|
|
|
case HighlightingKind::StaticField:
|
|
|
|
return OS << "StaticField";
|
|
|
|
case HighlightingKind::Class:
|
|
|
|
return OS << "Class";
|
2021-02-27 22:08:07 +01:00
|
|
|
case HighlightingKind::Interface:
|
|
|
|
return OS << "Interface";
|
2019-09-09 08:47:05 +00:00
|
|
|
case HighlightingKind::Enum:
|
|
|
|
return OS << "Enum";
|
|
|
|
case HighlightingKind::EnumConstant:
|
|
|
|
return OS << "EnumConstant";
|
2019-09-09 14:33:10 +00:00
|
|
|
case HighlightingKind::Typedef:
|
|
|
|
return OS << "Typedef";
|
2021-01-30 01:12:36 +01:00
|
|
|
case HighlightingKind::Type:
|
|
|
|
return OS << "Type";
|
|
|
|
case HighlightingKind::Unknown:
|
|
|
|
return OS << "Unknown";
|
2019-09-09 08:47:05 +00:00
|
|
|
case HighlightingKind::Namespace:
|
|
|
|
return OS << "Namespace";
|
|
|
|
case HighlightingKind::TemplateParameter:
|
|
|
|
return OS << "TemplateParameter";
|
2020-01-21 13:21:08 -05:00
|
|
|
case HighlightingKind::Concept:
|
|
|
|
return OS << "Concept";
|
2019-09-09 08:47:05 +00:00
|
|
|
case HighlightingKind::Primitive:
|
|
|
|
return OS << "Primitive";
|
|
|
|
case HighlightingKind::Macro:
|
|
|
|
return OS << "Macro";
|
2022-11-21 22:00:23 +01:00
|
|
|
case HighlightingKind::Modifier:
|
|
|
|
return OS << "Modifier";
|
2022-07-20 18:05:44 +02:00
|
|
|
case HighlightingKind::Operator:
|
|
|
|
return OS << "Operator";
|
2022-07-20 10:57:59 +02:00
|
|
|
case HighlightingKind::Bracket:
|
|
|
|
return OS << "Bracket";
|
2023-02-03 12:37:58 +01:00
|
|
|
case HighlightingKind::Label:
|
|
|
|
return OS << "Label";
|
2019-09-24 18:17:55 -04:00
|
|
|
case HighlightingKind::InactiveCode:
|
|
|
|
return OS << "InactiveCode";
|
2019-09-09 08:47:05 +00:00
|
|
|
}
|
2019-09-09 08:57:17 +00:00
|
|
|
llvm_unreachable("invalid HighlightingKind");
|
2019-09-09 08:47:05 +00:00
|
|
|
}
|
2023-05-26 03:15:10 -04:00
|
|
|
std::optional<HighlightingKind>
|
|
|
|
highlightingKindFromString(llvm::StringRef Name) {
|
|
|
|
static llvm::StringMap<HighlightingKind> Lookup = {
|
|
|
|
{"Variable", HighlightingKind::Variable},
|
|
|
|
{"LocalVariable", HighlightingKind::LocalVariable},
|
|
|
|
{"Parameter", HighlightingKind::Parameter},
|
|
|
|
{"Function", HighlightingKind::Function},
|
|
|
|
{"Method", HighlightingKind::Method},
|
|
|
|
{"StaticMethod", HighlightingKind::StaticMethod},
|
|
|
|
{"Field", HighlightingKind::Field},
|
|
|
|
{"StaticField", HighlightingKind::StaticField},
|
|
|
|
{"Class", HighlightingKind::Class},
|
|
|
|
{"Interface", HighlightingKind::Interface},
|
|
|
|
{"Enum", HighlightingKind::Enum},
|
|
|
|
{"EnumConstant", HighlightingKind::EnumConstant},
|
|
|
|
{"Typedef", HighlightingKind::Typedef},
|
|
|
|
{"Type", HighlightingKind::Type},
|
|
|
|
{"Unknown", HighlightingKind::Unknown},
|
|
|
|
{"Namespace", HighlightingKind::Namespace},
|
|
|
|
{"TemplateParameter", HighlightingKind::TemplateParameter},
|
|
|
|
{"Concept", HighlightingKind::Concept},
|
|
|
|
{"Primitive", HighlightingKind::Primitive},
|
|
|
|
{"Macro", HighlightingKind::Macro},
|
|
|
|
{"Modifier", HighlightingKind::Modifier},
|
|
|
|
{"Operator", HighlightingKind::Operator},
|
|
|
|
{"Bracket", HighlightingKind::Bracket},
|
|
|
|
{"InactiveCode", HighlightingKind::InactiveCode},
|
|
|
|
};
|
|
|
|
|
|
|
|
auto It = Lookup.find(Name);
|
|
|
|
return It != Lookup.end() ? std::make_optional(It->getValue()) : std::nullopt;
|
|
|
|
}
|
2021-01-27 09:47:17 +01:00
|
|
|
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K) {
|
|
|
|
switch (K) {
|
|
|
|
case HighlightingModifier::Declaration:
|
2022-08-01 15:31:59 +02:00
|
|
|
return OS << "decl"; // abbreviation for common case
|
2022-10-17 17:12:39 -04:00
|
|
|
case HighlightingModifier::Definition:
|
|
|
|
return OS << "def"; // abbrevation for common case
|
2022-08-04 13:33:33 +02:00
|
|
|
case HighlightingModifier::ConstructorOrDestructor:
|
|
|
|
return OS << "constrDestr";
|
2021-01-27 09:47:17 +01:00
|
|
|
default:
|
|
|
|
return OS << toSemanticTokenModifier(K);
|
|
|
|
}
|
|
|
|
}
|
2023-05-26 03:15:10 -04:00
|
|
|
std::optional<HighlightingModifier>
|
|
|
|
highlightingModifierFromString(llvm::StringRef Name) {
|
|
|
|
static llvm::StringMap<HighlightingModifier> Lookup = {
|
|
|
|
{"Declaration", HighlightingModifier::Declaration},
|
|
|
|
{"Definition", HighlightingModifier::Definition},
|
|
|
|
{"Deprecated", HighlightingModifier::Deprecated},
|
|
|
|
{"Deduced", HighlightingModifier::Deduced},
|
|
|
|
{"Readonly", HighlightingModifier::Readonly},
|
|
|
|
{"Static", HighlightingModifier::Static},
|
|
|
|
{"Abstract", HighlightingModifier::Abstract},
|
|
|
|
{"Virtual", HighlightingModifier::Virtual},
|
|
|
|
{"DependentName", HighlightingModifier::DependentName},
|
|
|
|
{"DefaultLibrary", HighlightingModifier::DefaultLibrary},
|
|
|
|
{"UsedAsMutableReference", HighlightingModifier::UsedAsMutableReference},
|
|
|
|
{"UsedAsMutablePointer", HighlightingModifier::UsedAsMutablePointer},
|
|
|
|
{"ConstructorOrDestructor",
|
|
|
|
HighlightingModifier::ConstructorOrDestructor},
|
|
|
|
{"UserDefined", HighlightingModifier::UserDefined},
|
|
|
|
{"FunctionScope", HighlightingModifier::FunctionScope},
|
|
|
|
{"ClassScope", HighlightingModifier::ClassScope},
|
|
|
|
{"FileScope", HighlightingModifier::FileScope},
|
|
|
|
{"GlobalScope", HighlightingModifier::GlobalScope},
|
|
|
|
};
|
|
|
|
|
|
|
|
auto It = Lookup.find(Name);
|
|
|
|
return It != Lookup.end() ? std::make_optional(It->getValue()) : std::nullopt;
|
|
|
|
}
|
2019-09-09 08:47:05 +00:00
|
|
|
|
2019-08-01 08:08:44 +00:00
|
|
|
bool operator==(const HighlightingToken &L, const HighlightingToken &R) {
|
2021-01-27 09:47:17 +01:00
|
|
|
return std::tie(L.R, L.Kind, L.Modifiers) ==
|
|
|
|
std::tie(R.R, R.Kind, R.Modifiers);
|
2019-08-01 08:08:44 +00:00
|
|
|
}
|
|
|
|
bool operator<(const HighlightingToken &L, const HighlightingToken &R) {
|
2022-04-13 02:54:12 -04:00
|
|
|
return std::tie(L.R, L.Kind, L.Modifiers) <
|
2021-01-27 09:47:17 +01:00
|
|
|
std::tie(R.R, R.Kind, R.Modifiers);
|
2019-08-01 08:08:44 +00:00
|
|
|
}
|
2019-06-26 13:08:36 +00:00
|
|
|
|
2020-03-24 02:24:47 +01:00
|
|
|
std::vector<SemanticToken>
|
2022-06-15 15:44:09 +02:00
|
|
|
toSemanticTokens(llvm::ArrayRef<HighlightingToken> Tokens,
|
|
|
|
llvm::StringRef Code) {
|
2022-07-29 21:18:42 -07:00
|
|
|
assert(llvm::is_sorted(Tokens));
|
2020-03-24 02:24:47 +01:00
|
|
|
std::vector<SemanticToken> Result;
|
2022-06-15 15:44:09 +02:00
|
|
|
// In case we split a HighlightingToken into multiple tokens (e.g. because it
|
|
|
|
// was spanning multiple lines), this tracks the last one. This prevents
|
|
|
|
// having a copy all the time.
|
|
|
|
HighlightingToken Scratch;
|
2020-03-24 02:24:47 +01:00
|
|
|
const HighlightingToken *Last = nullptr;
|
|
|
|
for (const HighlightingToken &Tok : Tokens) {
|
|
|
|
Result.emplace_back();
|
2022-06-15 15:44:09 +02:00
|
|
|
SemanticToken *Out = &Result.back();
|
2020-03-24 02:24:47 +01:00
|
|
|
// deltaStart/deltaLine are relative if possible.
|
|
|
|
if (Last) {
|
2022-06-15 15:44:09 +02:00
|
|
|
assert(Tok.R.start.line >= Last->R.end.line);
|
|
|
|
Out->deltaLine = Tok.R.start.line - Last->R.end.line;
|
|
|
|
if (Out->deltaLine == 0) {
|
2020-03-24 02:24:47 +01:00
|
|
|
assert(Tok.R.start.character >= Last->R.start.character);
|
2022-06-15 15:44:09 +02:00
|
|
|
Out->deltaStart = Tok.R.start.character - Last->R.start.character;
|
2020-03-24 02:24:47 +01:00
|
|
|
} else {
|
2022-06-15 15:44:09 +02:00
|
|
|
Out->deltaStart = Tok.R.start.character;
|
2020-03-24 02:24:47 +01:00
|
|
|
}
|
|
|
|
} else {
|
2022-06-15 15:44:09 +02:00
|
|
|
Out->deltaLine = Tok.R.start.line;
|
|
|
|
Out->deltaStart = Tok.R.start.character;
|
2020-03-24 02:24:47 +01:00
|
|
|
}
|
2022-06-15 15:44:09 +02:00
|
|
|
Out->tokenType = static_cast<unsigned>(Tok.Kind);
|
|
|
|
Out->tokenModifiers = Tok.Modifiers;
|
2020-03-24 02:24:47 +01:00
|
|
|
Last = &Tok;
|
2022-06-15 15:44:09 +02:00
|
|
|
|
|
|
|
if (Tok.R.end.line == Tok.R.start.line) {
|
|
|
|
Out->length = Tok.R.end.character - Tok.R.start.character;
|
|
|
|
} else {
|
|
|
|
// If the token spans a line break, split it into multiple pieces for each
|
|
|
|
// line.
|
|
|
|
// This is slow, but multiline tokens are rare.
|
|
|
|
// FIXME: There's a client capability for supporting multiline tokens,
|
|
|
|
// respect that.
|
|
|
|
auto TokStartOffset = llvm::cantFail(positionToOffset(Code, Tok.R.start));
|
|
|
|
// Note that the loop doesn't cover the last line, which has a special
|
|
|
|
// length.
|
|
|
|
for (int I = Tok.R.start.line; I < Tok.R.end.line; ++I) {
|
|
|
|
auto LineEnd = Code.find('\n', TokStartOffset);
|
|
|
|
assert(LineEnd != Code.npos);
|
|
|
|
Out->length = LineEnd - TokStartOffset;
|
|
|
|
// Token continues on next line, right after the line break.
|
|
|
|
TokStartOffset = LineEnd + 1;
|
|
|
|
Result.emplace_back();
|
|
|
|
Out = &Result.back();
|
|
|
|
*Out = Result[Result.size() - 2];
|
|
|
|
// New token starts at the first column of the next line.
|
|
|
|
Out->deltaLine = 1;
|
|
|
|
Out->deltaStart = 0;
|
|
|
|
}
|
|
|
|
// This is the token on last line.
|
|
|
|
Out->length = Tok.R.end.character;
|
|
|
|
// Update the start location for last token, as that's used in the
|
|
|
|
// relative delta calculation for following tokens.
|
|
|
|
Scratch = *Last;
|
|
|
|
Scratch.R.start.line = Tok.R.end.line;
|
|
|
|
Scratch.R.start.character = 0;
|
|
|
|
Last = &Scratch;
|
|
|
|
}
|
2020-03-24 02:24:47 +01:00
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
llvm::StringRef toSemanticTokenType(HighlightingKind Kind) {
|
|
|
|
switch (Kind) {
|
|
|
|
case HighlightingKind::Variable:
|
|
|
|
case HighlightingKind::LocalVariable:
|
|
|
|
case HighlightingKind::StaticField:
|
|
|
|
return "variable";
|
|
|
|
case HighlightingKind::Parameter:
|
|
|
|
return "parameter";
|
|
|
|
case HighlightingKind::Function:
|
|
|
|
return "function";
|
|
|
|
case HighlightingKind::Method:
|
2020-11-25 21:31:18 +01:00
|
|
|
return "method";
|
2020-03-24 02:24:47 +01:00
|
|
|
case HighlightingKind::StaticMethod:
|
2020-11-25 21:31:18 +01:00
|
|
|
// FIXME: better method with static modifier?
|
2020-03-24 02:24:47 +01:00
|
|
|
return "function";
|
|
|
|
case HighlightingKind::Field:
|
2020-11-20 20:51:58 +01:00
|
|
|
return "property";
|
2020-03-24 02:24:47 +01:00
|
|
|
case HighlightingKind::Class:
|
|
|
|
return "class";
|
2021-02-27 22:08:07 +01:00
|
|
|
case HighlightingKind::Interface:
|
|
|
|
return "interface";
|
2020-03-24 02:24:47 +01:00
|
|
|
case HighlightingKind::Enum:
|
|
|
|
return "enum";
|
|
|
|
case HighlightingKind::EnumConstant:
|
2020-12-11 14:46:12 +00:00
|
|
|
return "enumMember";
|
2020-03-24 02:24:47 +01:00
|
|
|
case HighlightingKind::Typedef:
|
2021-01-30 01:12:36 +01:00
|
|
|
case HighlightingKind::Type:
|
2020-03-24 02:24:47 +01:00
|
|
|
return "type";
|
2021-01-30 01:12:36 +01:00
|
|
|
case HighlightingKind::Unknown:
|
|
|
|
return "unknown"; // nonstandard
|
2020-03-24 02:24:47 +01:00
|
|
|
case HighlightingKind::Namespace:
|
|
|
|
return "namespace";
|
|
|
|
case HighlightingKind::TemplateParameter:
|
|
|
|
return "typeParameter";
|
|
|
|
case HighlightingKind::Concept:
|
|
|
|
return "concept"; // nonstandard
|
|
|
|
case HighlightingKind::Primitive:
|
|
|
|
return "type";
|
|
|
|
case HighlightingKind::Macro:
|
|
|
|
return "macro";
|
2022-11-21 22:00:23 +01:00
|
|
|
case HighlightingKind::Modifier:
|
|
|
|
return "modifier";
|
2022-07-20 18:05:44 +02:00
|
|
|
case HighlightingKind::Operator:
|
|
|
|
return "operator";
|
2022-07-20 10:57:59 +02:00
|
|
|
case HighlightingKind::Bracket:
|
|
|
|
return "bracket";
|
2023-02-03 12:37:58 +01:00
|
|
|
case HighlightingKind::Label:
|
|
|
|
return "label";
|
2020-03-24 02:24:47 +01:00
|
|
|
case HighlightingKind::InactiveCode:
|
|
|
|
return "comment";
|
|
|
|
}
|
2020-04-01 14:08:25 +01:00
|
|
|
llvm_unreachable("unhandled HighlightingKind");
|
2020-03-24 02:24:47 +01:00
|
|
|
}
|
|
|
|
|
2021-01-27 09:47:17 +01:00
|
|
|
llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier) {
|
|
|
|
switch (Modifier) {
|
|
|
|
case HighlightingModifier::Declaration:
|
|
|
|
return "declaration";
|
2022-10-17 17:12:39 -04:00
|
|
|
case HighlightingModifier::Definition:
|
|
|
|
return "definition";
|
2021-01-27 09:47:17 +01:00
|
|
|
case HighlightingModifier::Deprecated:
|
|
|
|
return "deprecated";
|
|
|
|
case HighlightingModifier::Readonly:
|
|
|
|
return "readonly";
|
|
|
|
case HighlightingModifier::Static:
|
|
|
|
return "static";
|
|
|
|
case HighlightingModifier::Deduced:
|
|
|
|
return "deduced"; // nonstandard
|
|
|
|
case HighlightingModifier::Abstract:
|
|
|
|
return "abstract";
|
2021-08-03 19:53:01 -07:00
|
|
|
case HighlightingModifier::Virtual:
|
|
|
|
return "virtual";
|
2021-01-30 01:12:36 +01:00
|
|
|
case HighlightingModifier::DependentName:
|
|
|
|
return "dependentName"; // nonstandard
|
2021-04-29 13:03:10 -04:00
|
|
|
case HighlightingModifier::DefaultLibrary:
|
|
|
|
return "defaultLibrary";
|
2021-09-13 00:34:02 -04:00
|
|
|
case HighlightingModifier::UsedAsMutableReference:
|
|
|
|
return "usedAsMutableReference"; // nonstandard
|
2022-06-21 15:56:21 +02:00
|
|
|
case HighlightingModifier::UsedAsMutablePointer:
|
|
|
|
return "usedAsMutablePointer"; // nonstandard
|
2022-08-04 13:33:33 +02:00
|
|
|
case HighlightingModifier::ConstructorOrDestructor:
|
|
|
|
return "constructorOrDestructor"; // nonstandard
|
2022-07-20 18:05:44 +02:00
|
|
|
case HighlightingModifier::UserDefined:
|
|
|
|
return "userDefined"; // nonstandard
|
2021-01-29 23:18:34 +01:00
|
|
|
case HighlightingModifier::FunctionScope:
|
|
|
|
return "functionScope"; // nonstandard
|
|
|
|
case HighlightingModifier::ClassScope:
|
|
|
|
return "classScope"; // nonstandard
|
|
|
|
case HighlightingModifier::FileScope:
|
|
|
|
return "fileScope"; // nonstandard
|
|
|
|
case HighlightingModifier::GlobalScope:
|
|
|
|
return "globalScope"; // nonstandard
|
2021-01-27 09:47:17 +01:00
|
|
|
}
|
2021-01-29 23:18:34 +01:00
|
|
|
llvm_unreachable("unhandled HighlightingModifier");
|
2021-01-27 09:47:17 +01:00
|
|
|
}
|
|
|
|
|
[clangd] Support textDocument/semanticTokens/edits
Summary:
This returns incremental highlights as a set of edits against the
previous highlights.
Server-side, we compute the full set of highlights, this just saves
wire-format size.
For now, the diff used is trivial: everything from the first change to
the last change is sent as a single edit.
The wire format is grungy - the replacement offset/length refer to
positions in the encoded array instead of the logical list of tokens.
We use token-oriented structs and translating to LSP forms when serializing.
This departs from LSP (but is consistent with semanticTokens today).
Tested in VSCode insiders (with a patched client to enable experimental
features).
Reviewers: hokein
Subscribers: ilya-biryukov, MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D77225
2020-04-01 16:21:44 +02:00
|
|
|
std::vector<SemanticTokensEdit>
|
|
|
|
diffTokens(llvm::ArrayRef<SemanticToken> Old,
|
|
|
|
llvm::ArrayRef<SemanticToken> New) {
|
|
|
|
// For now, just replace everything from the first-last modification.
|
|
|
|
// FIXME: use a real diff instead, this is bad with include-insertion.
|
|
|
|
|
|
|
|
unsigned Offset = 0;
|
|
|
|
while (!Old.empty() && !New.empty() && Old.front() == New.front()) {
|
|
|
|
++Offset;
|
|
|
|
Old = Old.drop_front();
|
|
|
|
New = New.drop_front();
|
|
|
|
}
|
|
|
|
while (!Old.empty() && !New.empty() && Old.back() == New.back()) {
|
|
|
|
Old = Old.drop_back();
|
|
|
|
New = New.drop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Old.empty() && New.empty())
|
|
|
|
return {};
|
|
|
|
SemanticTokensEdit Edit;
|
|
|
|
Edit.startToken = Offset;
|
|
|
|
Edit.deleteTokens = Old.size();
|
|
|
|
Edit.tokens = New;
|
|
|
|
return {std::move(Edit)};
|
|
|
|
}
|
|
|
|
|
2023-05-23 04:17:09 -04:00
|
|
|
std::vector<Range> getInactiveRegions(ParsedAST &AST) {
|
|
|
|
std::vector<Range> SkippedRanges(std::move(AST.getMacros().SkippedRanges));
|
|
|
|
const auto &SM = AST.getSourceManager();
|
|
|
|
StringRef MainCode = SM.getBufferOrFake(SM.getMainFileID()).getBuffer();
|
|
|
|
std::vector<Range> InactiveRegions;
|
|
|
|
for (const Range &Skipped : SkippedRanges) {
|
|
|
|
Range Inactive = Skipped;
|
|
|
|
// Sometimes, SkippedRanges contains a range ending at position 0
|
|
|
|
// of a line. Clients that apply whole-line styles will treat that
|
|
|
|
// line as inactive which is not desirable, so adjust the ending
|
|
|
|
// position to be the end of the previous line.
|
|
|
|
if (Inactive.end.character == 0 && Inactive.end.line > 0) {
|
|
|
|
--Inactive.end.line;
|
|
|
|
}
|
|
|
|
// Exclude the directive lines themselves from the range.
|
|
|
|
if (Inactive.end.line >= Inactive.start.line + 2) {
|
|
|
|
++Inactive.start.line;
|
|
|
|
--Inactive.end.line;
|
|
|
|
} else {
|
|
|
|
// range would be empty, e.g. #endif on next line after #ifdef
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Since we've adjusted the ending line, we need to recompute the
|
|
|
|
// column to reflect the end of that line.
|
|
|
|
if (auto EndOfLine = endOfLine(MainCode, Inactive.end.line)) {
|
|
|
|
Inactive.end = *EndOfLine;
|
|
|
|
} else {
|
|
|
|
elog("Failed to determine end of line: {0}", EndOfLine.takeError());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
InactiveRegions.push_back(Inactive);
|
|
|
|
}
|
|
|
|
return InactiveRegions;
|
|
|
|
}
|
|
|
|
|
2019-06-26 13:08:36 +00:00
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|