2018-08-15 00:03:32 +08:00
|
|
|
//===--- XRefs.cpp -----------------------------------------------*- C++-*-===//
|
2017-12-20 01:06:07 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08: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
|
2017-12-20 01:06:07 +08:00
|
|
|
//
|
2018-08-15 00:03:32 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2017-12-20 01:06:07 +08:00
|
|
|
#include "XRefs.h"
|
2018-03-09 22:00:34 +08:00
|
|
|
#include "AST.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "CodeCompletionStrings.h"
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
#include "FindSymbols.h"
|
2019-09-26 15:27:43 +08:00
|
|
|
#include "FindTarget.h"
|
2019-09-04 17:46:06 +08:00
|
|
|
#include "ParsedAST.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "Protocol.h"
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
#include "Quality.h"
|
2019-10-18 06:48:39 +08:00
|
|
|
#include "Selection.h"
|
2018-02-21 10:39:08 +08:00
|
|
|
#include "SourceCode.h"
|
2018-01-29 23:37:46 +08:00
|
|
|
#include "URI.h"
|
2019-07-10 01:59:50 +08:00
|
|
|
#include "index/Index.h"
|
2019-02-11 23:05:29 +08:00
|
|
|
#include "index/Merge.h"
|
2019-10-17 22:08:28 +08:00
|
|
|
#include "index/Relation.h"
|
2019-02-28 19:02:01 +08:00
|
|
|
#include "index/SymbolLocation.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 23:49:17 +08:00
|
|
|
#include "support/Logger.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2020-03-20 04:28:53 +08:00
|
|
|
#include "clang/AST/ASTTypeTraits.h"
|
2020-01-25 01:21:47 +08:00
|
|
|
#include "clang/AST/Attr.h"
|
2020-01-30 19:45:43 +08:00
|
|
|
#include "clang/AST/Attrs.inc"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "clang/AST/Decl.h"
|
2019-06-13 16:51:44 +08:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2020-04-19 08:19:25 +08:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
[clangd] Implement textDocument/hover
Summary: Implemention of textDocument/hover as described in LSP definition.
This patch adds a basic Hover implementation. When hovering a variable,
function, method or namespace, clangd will return a text containing the
declaration's scope, as well as the declaration of the hovered entity.
For example, for a variable:
Declared in class Foo::Bar
int hello = 2
For macros, the macro definition is returned.
This patch doesn't include:
- markdown support (the client I use doesn't support it yet)
- range support (optional in the Hover response)
- comments associated to variables/functions/classes
They are kept as future work to keep this patch simpler.
I added tests in XRefsTests.cpp. hover.test contains one simple
smoketest to make sure the feature works from a black box perspective.
Reviewers: malaperle, krasimir, bkramer, ilya-biryukov
Subscribers: sammccall, mgrang, klimek, rwols, ilya-biryukov, arphaman, cfe-commits
Differential Revision: https://reviews.llvm.org/D35894
Signed-off-by: Simon Marchi <simon.marchi@ericsson.com>
Signed-off-by: William Enright <william.enright@polymtl.ca>
llvm-svn: 325395
2018-02-17 05:38:15 +08:00
|
|
|
#include "clang/AST/DeclTemplate.h"
|
2019-06-13 16:51:44 +08:00
|
|
|
#include "clang/AST/ExprCXX.h"
|
2020-04-19 08:19:25 +08:00
|
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
|
|
#include "clang/AST/Stmt.h"
|
|
|
|
#include "clang/AST/StmtCXX.h"
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
#include "clang/AST/Type.h"
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
#include "clang/Basic/CharInfo.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "clang/Basic/LLVM.h"
|
2020-03-01 23:05:12 +08:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "clang/Basic/SourceLocation.h"
|
|
|
|
#include "clang/Basic/SourceManager.h"
|
2020-03-03 05:45:25 +08:00
|
|
|
#include "clang/Basic/TokenKinds.h"
|
2017-12-20 01:06:07 +08:00
|
|
|
#include "clang/Index/IndexDataConsumer.h"
|
2019-03-08 17:54:37 +08:00
|
|
|
#include "clang/Index/IndexSymbol.h"
|
2017-12-20 01:06:07 +08:00
|
|
|
#include "clang/Index/IndexingAction.h"
|
2019-09-07 04:08:32 +08:00
|
|
|
#include "clang/Index/IndexingOptions.h"
|
2018-04-30 23:24:17 +08:00
|
|
|
#include "clang/Index/USRGeneration.h"
|
2020-02-20 02:11:01 +08:00
|
|
|
#include "clang/Tooling/Syntax/Tokens.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
2020-11-16 11:59:10 +08:00
|
|
|
#include "llvm/ADT/MapVector.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "llvm/ADT/None.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2020-04-19 08:19:25 +08:00
|
|
|
#include "llvm/ADT/ScopeExit.h"
|
2020-04-04 03:35:30 +08:00
|
|
|
#include "llvm/ADT/SmallSet.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "llvm/Support/Casting.h"
|
2020-02-26 20:39:46 +08:00
|
|
|
#include "llvm/Support/Error.h"
|
2020-03-03 05:45:25 +08:00
|
|
|
#include "llvm/Support/MathExtras.h"
|
2018-02-14 01:47:16 +08:00
|
|
|
#include "llvm/Support/Path.h"
|
2019-05-28 18:29:58 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2018-09-05 19:53:07 +08:00
|
|
|
|
2017-12-20 01:06:07 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
namespace {
|
|
|
|
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
// Returns the single definition of the entity declared by D, if visible.
|
|
|
|
// In particular:
|
|
|
|
// - for non-redeclarable kinds (e.g. local vars), return D
|
|
|
|
// - for kinds that allow multiple definitions (e.g. namespaces), return nullptr
|
|
|
|
// Kinds of nodes that always return nullptr here will not have definitions
|
|
|
|
// reported by locateSymbolAt().
|
[clangd] targetDecl() returns only NamedDecls.
Summary:
While it's perfectly reasonable for non-named decls such as
static_assert to resolve to themselves:
- nothing else ever resolves to them
- features based on references (hover, highlight, find refs etc) tend
to be uninteresting where only trivial references are possible
- returning NamedDecl is a more convenient API (we cast to it in many places)
- this aligns closer to findExplicitReferences/explicitReferenceTargets
This fixes a crash in explicitReferenceTargets: if the target is a
non-named decl then there's an invalid unchecked cast to NamedDecl.
In practice this means when hovering over e.g. a static_assert:
- before ac3f9e4842, we would show a (boring) hover card
- after ac3f9e4842, we would crash
- after this patch, we will show nothing
Reviewers: kadircet, ilya-biryukov
Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72163
2020-01-04 00:26:33 +08:00
|
|
|
const NamedDecl *getDefinition(const NamedDecl *D) {
|
2018-01-12 22:21:10 +08:00
|
|
|
assert(D);
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
// Decl has one definition that we can find.
|
2018-01-12 22:21:10 +08:00
|
|
|
if (const auto *TD = dyn_cast<TagDecl>(D))
|
|
|
|
return TD->getDefinition();
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
if (const auto *VD = dyn_cast<VarDecl>(D))
|
2018-01-12 22:21:10 +08:00
|
|
|
return VD->getDefinition();
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
if (const auto *FD = dyn_cast<FunctionDecl>(D))
|
2018-01-12 22:21:10 +08:00
|
|
|
return FD->getDefinition();
|
2020-07-10 02:29:15 +08:00
|
|
|
// Objective-C classes can have three types of declarations:
|
|
|
|
//
|
|
|
|
// - forward declaration: @class MyClass;
|
|
|
|
// - true declaration (interface definition): @interface MyClass ... @end
|
|
|
|
// - true definition (implementation): @implementation MyClass ... @end
|
|
|
|
//
|
|
|
|
// Objective-C categories are extensions are on classes:
|
|
|
|
//
|
|
|
|
// - declaration: @interface MyClass (Ext) ... @end
|
|
|
|
// - definition: @implementation MyClass (Ext) ... @end
|
|
|
|
//
|
|
|
|
// With one special case, a class extension, which is normally used to keep
|
|
|
|
// some declarations internal to a file without exposing them in a header.
|
|
|
|
//
|
|
|
|
// - class extension declaration: @interface MyClass () ... @end
|
|
|
|
// - which really links to class definition: @implementation MyClass ... @end
|
|
|
|
if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(D))
|
|
|
|
return ID->getImplementation();
|
|
|
|
if (const auto *CD = dyn_cast<ObjCCategoryDecl>(D)) {
|
|
|
|
if (CD->IsClassExtension()) {
|
|
|
|
if (const auto *ID = CD->getClassInterface())
|
|
|
|
return ID->getImplementation();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return CD->getImplementation();
|
|
|
|
}
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
// Only a single declaration is allowed.
|
2019-02-21 17:55:00 +08:00
|
|
|
if (isa<ValueDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
|
|
|
|
isa<TemplateTemplateParmDecl>(D)) // except cases above
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
return D;
|
|
|
|
// Multiple definitions are allowed.
|
|
|
|
return nullptr; // except cases above
|
2018-01-12 22:21:10 +08:00
|
|
|
}
|
|
|
|
|
2018-10-19 16:35:24 +08:00
|
|
|
void logIfOverflow(const SymbolLocation &Loc) {
|
|
|
|
if (Loc.Start.hasOverflow() || Loc.End.hasOverflow())
|
|
|
|
log("Possible overflow in symbol location: {0}", Loc);
|
|
|
|
}
|
|
|
|
|
2018-04-30 23:24:17 +08:00
|
|
|
// Convert a SymbolLocation to LSP's Location.
|
2018-11-28 18:30:42 +08:00
|
|
|
// TUPath is used to resolve the path of URI.
|
2018-04-30 23:24:17 +08:00
|
|
|
// FIXME: figure out a good home for it, and share the implementation with
|
|
|
|
// FindSymbols.
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::Optional<Location> toLSPLocation(const SymbolLocation &Loc,
|
|
|
|
llvm::StringRef TUPath) {
|
2018-04-30 23:24:17 +08:00
|
|
|
if (!Loc)
|
2018-10-20 23:30:37 +08:00
|
|
|
return None;
|
2018-04-30 23:24:17 +08:00
|
|
|
auto Uri = URI::parse(Loc.FileURI);
|
|
|
|
if (!Uri) {
|
2018-11-28 18:30:42 +08:00
|
|
|
elog("Could not parse URI {0}: {1}", Loc.FileURI, Uri.takeError());
|
2018-10-20 23:30:37 +08:00
|
|
|
return None;
|
2018-04-30 23:24:17 +08:00
|
|
|
}
|
2018-11-28 18:30:42 +08:00
|
|
|
auto U = URIForFile::fromURI(*Uri, TUPath);
|
|
|
|
if (!U) {
|
|
|
|
elog("Could not resolve URI {0}: {1}", Loc.FileURI, U.takeError());
|
2018-10-20 23:30:37 +08:00
|
|
|
return None;
|
2018-04-30 23:24:17 +08:00
|
|
|
}
|
2018-11-28 18:30:42 +08:00
|
|
|
|
2018-04-30 23:24:17 +08:00
|
|
|
Location LSPLoc;
|
2018-11-28 18:30:42 +08:00
|
|
|
LSPLoc.uri = std::move(*U);
|
[clangd] Encode Line/Column as a 32-bits integer.
Summary:
This would buy us more memory. Using a 32-bits integer is enough for
most human-readable source code (up to 4M lines and 4K columns).
Previsouly, we used 8 bytes for a position, now 4 bytes, it would save
us 8 bytes for each Ref and each Symbol instance.
For LLVM-project binary index file, we save ~13% memory.
| Before | After |
| 412MB | 355MB |
Reviewers: sammccall
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D53363
llvm-svn: 344735
2018-10-18 18:43:50 +08:00
|
|
|
LSPLoc.range.start.line = Loc.Start.line();
|
|
|
|
LSPLoc.range.start.character = Loc.Start.column();
|
|
|
|
LSPLoc.range.end.line = Loc.End.line();
|
|
|
|
LSPLoc.range.end.character = Loc.End.column();
|
2018-10-19 16:35:24 +08:00
|
|
|
logIfOverflow(Loc);
|
2018-04-30 23:24:17 +08:00
|
|
|
return LSPLoc;
|
|
|
|
}
|
|
|
|
|
2019-02-11 23:05:29 +08:00
|
|
|
SymbolLocation toIndexLocation(const Location &Loc, std::string &URIStorage) {
|
|
|
|
SymbolLocation SymLoc;
|
|
|
|
URIStorage = Loc.uri.uri();
|
|
|
|
SymLoc.FileURI = URIStorage.c_str();
|
|
|
|
SymLoc.Start.setLine(Loc.range.start.line);
|
|
|
|
SymLoc.Start.setColumn(Loc.range.start.character);
|
|
|
|
SymLoc.End.setLine(Loc.range.end.line);
|
|
|
|
SymLoc.End.setColumn(Loc.range.end.character);
|
|
|
|
return SymLoc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the preferred location between an AST location and an index location.
|
|
|
|
SymbolLocation getPreferredLocation(const Location &ASTLoc,
|
2019-02-12 18:38:45 +08:00
|
|
|
const SymbolLocation &IdxLoc,
|
|
|
|
std::string &Scratch) {
|
2019-02-11 23:05:29 +08:00
|
|
|
// Also use a dummy symbol for the index location so that other fields (e.g.
|
2019-12-16 17:33:56 +08:00
|
|
|
// definition) are not factored into the preference.
|
2019-02-11 23:05:29 +08:00
|
|
|
Symbol ASTSym, IdxSym;
|
|
|
|
ASTSym.ID = IdxSym.ID = SymbolID("dummy_id");
|
2019-02-12 18:38:45 +08:00
|
|
|
ASTSym.CanonicalDeclaration = toIndexLocation(ASTLoc, Scratch);
|
2019-02-11 23:05:29 +08:00
|
|
|
IdxSym.CanonicalDeclaration = IdxLoc;
|
|
|
|
auto Merged = mergeSymbol(ASTSym, IdxSym);
|
|
|
|
return Merged.CanonicalDeclaration;
|
|
|
|
}
|
|
|
|
|
2020-10-12 22:07:15 +08:00
|
|
|
std::vector<std::pair<const NamedDecl *, DeclRelationSet>>
|
|
|
|
getDeclAtPositionWithRelations(ParsedAST &AST, SourceLocation Pos,
|
|
|
|
DeclRelationSet Relations,
|
|
|
|
ASTNodeKind *NodeKind = nullptr) {
|
|
|
|
unsigned Offset = AST.getSourceManager().getDecomposedSpellingLoc(Pos).second;
|
|
|
|
std::vector<std::pair<const NamedDecl *, DeclRelationSet>> Result;
|
|
|
|
auto ResultFromTree = [&](SelectionTree ST) {
|
|
|
|
if (const SelectionTree::Node *N = ST.commonAncestor()) {
|
|
|
|
if (NodeKind)
|
|
|
|
*NodeKind = N->ASTNode.getNodeKind();
|
|
|
|
llvm::copy_if(allTargetDecls(N->ASTNode), std::back_inserter(Result),
|
|
|
|
[&](auto &Entry) { return !(Entry.second & ~Relations); });
|
|
|
|
}
|
|
|
|
return !Result.empty();
|
|
|
|
};
|
|
|
|
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Offset,
|
|
|
|
Offset, ResultFromTree);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-03-20 04:28:53 +08:00
|
|
|
std::vector<const NamedDecl *>
|
|
|
|
getDeclAtPosition(ParsedAST &AST, SourceLocation Pos, DeclRelationSet Relations,
|
|
|
|
ASTNodeKind *NodeKind = nullptr) {
|
[clangd] targetDecl() returns only NamedDecls.
Summary:
While it's perfectly reasonable for non-named decls such as
static_assert to resolve to themselves:
- nothing else ever resolves to them
- features based on references (hover, highlight, find refs etc) tend
to be uninteresting where only trivial references are possible
- returning NamedDecl is a more convenient API (we cast to it in many places)
- this aligns closer to findExplicitReferences/explicitReferenceTargets
This fixes a crash in explicitReferenceTargets: if the target is a
non-named decl then there's an invalid unchecked cast to NamedDecl.
In practice this means when hovering over e.g. a static_assert:
- before ac3f9e4842, we would show a (boring) hover card
- after ac3f9e4842, we would crash
- after this patch, we will show nothing
Reviewers: kadircet, ilya-biryukov
Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72163
2020-01-04 00:26:33 +08:00
|
|
|
std::vector<const NamedDecl *> Result;
|
2020-10-12 22:07:15 +08:00
|
|
|
for (auto &Entry :
|
|
|
|
getDeclAtPositionWithRelations(AST, Pos, Relations, NodeKind))
|
|
|
|
Result.push_back(Entry.first);
|
2019-10-18 06:48:39 +08:00
|
|
|
return Result;
|
2018-04-30 23:24:17 +08:00
|
|
|
}
|
|
|
|
|
2020-03-01 23:05:12 +08:00
|
|
|
// Expects Loc to be a SpellingLocation, will bail out otherwise as it can't
|
|
|
|
// figure out a filename.
|
|
|
|
llvm::Optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::StringRef TUPath) {
|
2020-03-01 23:05:12 +08:00
|
|
|
const auto &SM = AST.getSourceManager();
|
|
|
|
const FileEntry *F = SM.getFileEntryForID(SM.getFileID(Loc));
|
2017-12-20 01:06:07 +08:00
|
|
|
if (!F)
|
2018-10-20 23:30:37 +08:00
|
|
|
return None;
|
2020-03-01 23:05:12 +08:00
|
|
|
auto FilePath = getCanonicalPath(F, SM);
|
2018-04-30 23:24:17 +08:00
|
|
|
if (!FilePath) {
|
|
|
|
log("failed to get path!");
|
2018-10-20 23:30:37 +08:00
|
|
|
return None;
|
2018-02-14 01:47:16 +08:00
|
|
|
}
|
2020-03-01 23:05:12 +08:00
|
|
|
Location L;
|
|
|
|
L.uri = URIForFile::canonicalize(*FilePath, TUPath);
|
|
|
|
// We call MeasureTokenLength here as TokenBuffer doesn't store spelled tokens
|
|
|
|
// outside the main file.
|
|
|
|
auto TokLen = Lexer::MeasureTokenLength(Loc, SM, AST.getLangOpts());
|
|
|
|
L.range = halfOpenToRange(
|
|
|
|
SM, CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(TokLen)));
|
|
|
|
return L;
|
2017-12-20 01:06:07 +08:00
|
|
|
}
|
|
|
|
|
2020-03-03 01:45:05 +08:00
|
|
|
// Treat #included files as symbols, to enable go-to-definition on them.
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
llvm::Optional<LocatedSymbol> locateFileReferent(const Position &Pos,
|
|
|
|
ParsedAST &AST,
|
|
|
|
llvm::StringRef MainFilePath) {
|
2018-07-03 16:09:29 +08:00
|
|
|
for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
|
2020-05-04 16:48:19 +08:00
|
|
|
if (!Inc.Resolved.empty() && Inc.HashLine == Pos.line) {
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
LocatedSymbol File;
|
2020-01-29 03:23:46 +08:00
|
|
|
File.Name = std::string(llvm::sys::path::filename(Inc.Resolved));
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
File.PreferredDeclaration = {
|
2020-03-03 01:45:05 +08:00
|
|
|
URIForFile::canonicalize(Inc.Resolved, MainFilePath), Range{}};
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
File.Definition = File.PreferredDeclaration;
|
|
|
|
// We're not going to find any further symbols on #include lines.
|
2020-03-03 01:45:05 +08:00
|
|
|
return File;
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
}
|
2018-03-09 00:28:12 +08:00
|
|
|
}
|
2020-03-03 01:45:05 +08:00
|
|
|
return llvm::None;
|
|
|
|
}
|
2018-03-09 00:28:12 +08:00
|
|
|
|
2020-03-03 01:45:05 +08:00
|
|
|
// Macros are simple: there's no declaration/definition distinction.
|
|
|
|
// As a consequence, there's no need to look them up in the index either.
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
llvm::Optional<LocatedSymbol>
|
2020-03-03 01:45:05 +08:00
|
|
|
locateMacroReferent(const syntax::Token &TouchedIdentifier, ParsedAST &AST,
|
|
|
|
llvm::StringRef MainFilePath) {
|
|
|
|
if (auto M = locateMacroAt(TouchedIdentifier, AST.getPreprocessor())) {
|
[clangd] locateMacroAt handles patched macros
Summary: Depends on D79992.
This patch changes locateMacroAt to perform #line directive substitution
for macro identifier locations.
We first check whether a location is inside a file included through
built-in header. If so we check whether line directive maps it back to
the main file, and afterwards use TokenBuffers to find exact location of
the identifier on the line.
Instead of performing the mapping in locateMacroAt, we could also store
a mapping inside the ParsedAST whenever we use a patched preamble. But
that would imply adding more responsibility to ParsedAST and paying for
the mapping even when it is not going to be used.
====
Go-To-Definition:
Later on these locations are used for serving go-to-definition requests,
this enables jumping to definition inside the preamble section in
presence of patched macros.
=====
Go-To-Refs:
Macro references in main file are collected separetely and stored as a
map from macro's symbol id to reference ranges. Those ranges are
computed inside PPCallbacks, hence we don't have access to TokenBuffer.
In presence of preamble patch, any reference to a macro inside the
preamble section will unfortunately have the wrong range. They'll point
into the patch rather than the main file. Hence during findReferences,
we won't get any ranges reported for those.
Fixing those requires:
- Lexing the preamble section to figure out "real range" of a patched
macro definition
- Postponing range/location calculations until a later step in which we
have access to tokenbuffers.
This patch trades some accuracy in favor of code complexity. We don't do
any patching for references inside the preamble patch but get any
reference inside the main file for free.
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D80198
2020-05-14 18:26:47 +08:00
|
|
|
if (auto Loc =
|
|
|
|
makeLocation(AST.getASTContext(), M->NameLoc, MainFilePath)) {
|
2020-03-03 01:45:05 +08:00
|
|
|
LocatedSymbol Macro;
|
|
|
|
Macro.Name = std::string(M->Name);
|
|
|
|
Macro.PreferredDeclaration = *Loc;
|
|
|
|
Macro.Definition = Loc;
|
|
|
|
return Macro;
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
}
|
2017-12-20 01:06:07 +08:00
|
|
|
}
|
2020-03-03 01:45:05 +08:00
|
|
|
return llvm::None;
|
|
|
|
}
|
2017-12-20 01:06:07 +08:00
|
|
|
|
2020-07-10 02:29:15 +08:00
|
|
|
// A wrapper around `Decl::getCanonicalDecl` to support cases where Clang's
|
|
|
|
// definition of a canonical declaration doesn't match up to what a programmer
|
|
|
|
// would expect. For example, Objective-C classes can have three types of
|
|
|
|
// declarations:
|
|
|
|
//
|
|
|
|
// - forward declaration(s): @class MyClass;
|
|
|
|
// - true declaration (interface definition): @interface MyClass ... @end
|
|
|
|
// - true definition (implementation): @implementation MyClass ... @end
|
|
|
|
//
|
|
|
|
// Clang will consider the forward declaration to be the canonical declaration
|
|
|
|
// because it is first. We actually want the class definition if it is
|
|
|
|
// available since that is what a programmer would consider the primary
|
|
|
|
// declaration to be.
|
|
|
|
const NamedDecl *getPreferredDecl(const NamedDecl *D) {
|
|
|
|
// FIXME: Canonical declarations of some symbols might refer to built-in
|
|
|
|
// decls with possibly-invalid source locations (e.g. global new operator).
|
|
|
|
// In such cases we should pick up a redecl with valid source location
|
|
|
|
// instead of failing.
|
|
|
|
D = llvm::cast<NamedDecl>(D->getCanonicalDecl());
|
|
|
|
|
|
|
|
// Prefer Objective-C class/protocol definitions over the forward declaration.
|
|
|
|
if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(D))
|
|
|
|
if (const auto *DefinitionID = ID->getDefinition())
|
|
|
|
return DefinitionID;
|
|
|
|
if (const auto *PD = dyn_cast<ObjCProtocolDecl>(D))
|
|
|
|
if (const auto *DefinitionID = PD->getDefinition())
|
|
|
|
return DefinitionID;
|
|
|
|
|
|
|
|
return D;
|
|
|
|
}
|
|
|
|
|
2020-12-03 16:42:46 +08:00
|
|
|
std::vector<LocatedSymbol> findImplementors(llvm::DenseSet<SymbolID> IDs,
|
|
|
|
RelationKind Predicate,
|
|
|
|
const SymbolIndex *Index,
|
|
|
|
llvm::StringRef MainFilePath) {
|
2020-12-14 15:55:47 +08:00
|
|
|
if (IDs.empty())
|
|
|
|
return {};
|
2021-01-11 16:55:24 +08:00
|
|
|
static constexpr trace::Metric FindImplementorsMetric(
|
|
|
|
"find_implementors", trace::Metric::Counter, "case");
|
|
|
|
switch (Predicate) {
|
|
|
|
case RelationKind::BaseOf:
|
|
|
|
FindImplementorsMetric.record(1, "find-base");
|
|
|
|
break;
|
|
|
|
case RelationKind::OverriddenBy:
|
|
|
|
FindImplementorsMetric.record(1, "find-override");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-12-14 15:55:47 +08:00
|
|
|
RelationsRequest Req;
|
2020-12-03 16:42:46 +08:00
|
|
|
Req.Predicate = Predicate;
|
2020-12-14 15:55:47 +08:00
|
|
|
Req.Subjects = std::move(IDs);
|
|
|
|
std::vector<LocatedSymbol> Results;
|
|
|
|
Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
|
|
|
|
auto DeclLoc =
|
|
|
|
indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
|
|
|
|
if (!DeclLoc) {
|
|
|
|
elog("Find overrides: {0}", DeclLoc.takeError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Results.emplace_back();
|
|
|
|
Results.back().Name = Object.Name.str();
|
|
|
|
Results.back().PreferredDeclaration = *DeclLoc;
|
|
|
|
auto DefLoc = indexToLSPLocation(Object.Definition, MainFilePath);
|
|
|
|
if (!DefLoc) {
|
|
|
|
elog("Failed to convert location: {0}", DefLoc.takeError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Results.back().Definition = *DefLoc;
|
|
|
|
});
|
|
|
|
return Results;
|
|
|
|
}
|
|
|
|
|
2020-03-03 01:45:05 +08:00
|
|
|
// Decls are more complicated.
|
|
|
|
// The AST contains at least a declaration, maybe a definition.
|
|
|
|
// These are up-to-date, and so generally preferred over index results.
|
|
|
|
// We perform a single batch index lookup to find additional definitions.
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
std::vector<LocatedSymbol>
|
2020-03-03 01:45:05 +08:00
|
|
|
locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
|
|
|
|
ParsedAST &AST, llvm::StringRef MainFilePath,
|
2020-03-20 04:28:53 +08:00
|
|
|
const SymbolIndex *Index, ASTNodeKind *NodeKind) {
|
2020-03-03 01:45:05 +08:00
|
|
|
const SourceManager &SM = AST.getSourceManager();
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
// Results follow the order of Symbols.Decls.
|
2020-03-03 01:45:05 +08:00
|
|
|
std::vector<LocatedSymbol> Result;
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
// Keep track of SymbolID -> index mapping, to fill in index data later.
|
|
|
|
llvm::DenseMap<SymbolID, size_t> ResultIndex;
|
2018-04-30 23:24:17 +08:00
|
|
|
|
2021-01-05 17:26:20 +08:00
|
|
|
static constexpr trace::Metric LocateASTReferentMetric(
|
|
|
|
"locate_ast_referent", trace::Metric::Counter, "case");
|
2020-01-25 01:21:47 +08:00
|
|
|
auto AddResultDecl = [&](const NamedDecl *D) {
|
2020-07-10 02:29:15 +08:00
|
|
|
D = getPreferredDecl(D);
|
2020-01-25 01:31:37 +08:00
|
|
|
auto Loc =
|
|
|
|
makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath);
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
if (!Loc)
|
2020-01-25 01:21:47 +08:00
|
|
|
return;
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
|
|
|
|
Result.emplace_back();
|
2020-01-25 01:31:37 +08:00
|
|
|
Result.back().Name = printName(AST.getASTContext(), *D);
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
Result.back().PreferredDeclaration = *Loc;
|
2020-01-25 01:31:37 +08:00
|
|
|
if (const NamedDecl *Def = getDefinition(D))
|
|
|
|
Result.back().Definition = makeLocation(
|
|
|
|
AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
|
|
|
|
// Record SymbolID for index lookup later.
|
2020-01-25 01:31:37 +08:00
|
|
|
if (auto ID = getSymbolID(D))
|
2020-10-29 23:04:53 +08:00
|
|
|
ResultIndex[ID] = Result.size() - 1;
|
2020-01-25 01:21:47 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Emit all symbol locations (declaration or definition) from AST.
|
|
|
|
DeclRelationSet Relations =
|
|
|
|
DeclRelation::TemplatePattern | DeclRelation::Alias;
|
2020-10-12 22:07:15 +08:00
|
|
|
auto Candidates =
|
|
|
|
getDeclAtPositionWithRelations(AST, CurLoc, Relations, NodeKind);
|
2020-12-14 15:55:47 +08:00
|
|
|
llvm::DenseSet<SymbolID> VirtualMethods;
|
2020-10-12 22:07:15 +08:00
|
|
|
for (const auto &E : Candidates) {
|
|
|
|
const NamedDecl *D = E.first;
|
2020-01-25 01:21:47 +08:00
|
|
|
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
|
2020-12-14 15:55:47 +08:00
|
|
|
// Special case: virtual void ^method() = 0: jump to all overrides.
|
|
|
|
// FIXME: extend it to ^virtual, unfortunately, virtual location is not
|
|
|
|
// saved in the AST.
|
|
|
|
if (CMD->isPure()) {
|
|
|
|
if (TouchedIdentifier && SM.getSpellingLoc(CMD->getLocation()) ==
|
2021-01-05 17:26:20 +08:00
|
|
|
TouchedIdentifier->location()) {
|
2020-12-14 15:55:47 +08:00
|
|
|
VirtualMethods.insert(getSymbolID(CMD));
|
2021-01-05 17:26:20 +08:00
|
|
|
LocateASTReferentMetric.record(1, "method-to-override");
|
|
|
|
}
|
2020-12-14 15:55:47 +08:00
|
|
|
}
|
|
|
|
// Special case: void foo() ^override: jump to the overridden method.
|
2020-02-26 20:39:46 +08:00
|
|
|
const InheritableAttr *Attr = D->getAttr<OverrideAttr>();
|
2020-01-30 19:45:43 +08:00
|
|
|
if (!Attr)
|
|
|
|
Attr = D->getAttr<FinalAttr>();
|
2020-02-26 20:39:46 +08:00
|
|
|
if (Attr && TouchedIdentifier &&
|
|
|
|
SM.getSpellingLoc(Attr->getLocation()) ==
|
|
|
|
TouchedIdentifier->location()) {
|
2021-01-05 17:26:20 +08:00
|
|
|
LocateASTReferentMetric.record(1, "method-to-base");
|
2020-01-25 01:21:47 +08:00
|
|
|
// We may be overridding multiple methods - offer them all.
|
|
|
|
for (const NamedDecl *ND : CMD->overridden_methods())
|
|
|
|
AddResultDecl(ND);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 22:07:15 +08:00
|
|
|
// Special case: the cursor is on an alias, prefer other results.
|
|
|
|
// This targets "using ns::^Foo", where the target is more interesting.
|
|
|
|
// This does not trigger on renaming aliases:
|
|
|
|
// `using Foo = ^Bar` already targets Bar via a TypeLoc
|
|
|
|
// `using ^Foo = Bar` has no other results, as Underlying is filtered.
|
|
|
|
if (E.second & DeclRelation::Alias && Candidates.size() > 1 &&
|
|
|
|
// beginLoc/endLoc are a token range, so rewind the identifier we're in.
|
|
|
|
SM.isPointWithin(TouchedIdentifier ? TouchedIdentifier->location()
|
|
|
|
: CurLoc,
|
|
|
|
D->getBeginLoc(), D->getEndLoc()))
|
|
|
|
continue;
|
|
|
|
|
2020-01-25 01:21:47 +08:00
|
|
|
// Special case: the point of declaration of a template specialization,
|
|
|
|
// it's more useful to navigate to the template declaration.
|
2020-02-26 20:39:46 +08:00
|
|
|
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
|
|
|
|
if (TouchedIdentifier &&
|
|
|
|
D->getLocation() == TouchedIdentifier->location()) {
|
2021-01-05 17:26:20 +08:00
|
|
|
LocateASTReferentMetric.record(1, "template-specialization-to-primary");
|
2020-01-25 01:21:47 +08:00
|
|
|
AddResultDecl(CTSD->getSpecializedTemplate());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-10 02:29:15 +08:00
|
|
|
// Special case: if the class name is selected, also map Objective-C
|
|
|
|
// categories and category implementations back to their class interface.
|
|
|
|
//
|
|
|
|
// Since `TouchedIdentifier` might refer to the `ObjCCategoryImplDecl`
|
|
|
|
// instead of the `ObjCCategoryDecl` we intentionally check the contents
|
|
|
|
// of the locs when checking for class name equivalence.
|
|
|
|
if (const auto *CD = dyn_cast<ObjCCategoryDecl>(D))
|
|
|
|
if (const auto *ID = CD->getClassInterface())
|
|
|
|
if (TouchedIdentifier &&
|
|
|
|
(CD->getLocation() == TouchedIdentifier->location() ||
|
2021-01-05 17:26:20 +08:00
|
|
|
ID->getName() == TouchedIdentifier->text(SM))) {
|
|
|
|
LocateASTReferentMetric.record(1, "objc-category-to-class");
|
2020-07-10 02:29:15 +08:00
|
|
|
AddResultDecl(ID);
|
2021-01-05 17:26:20 +08:00
|
|
|
}
|
2020-07-10 02:29:15 +08:00
|
|
|
|
2021-01-05 17:26:20 +08:00
|
|
|
LocateASTReferentMetric.record(1, "regular");
|
2020-01-25 01:21:47 +08:00
|
|
|
// Otherwise the target declaration is the right one.
|
|
|
|
AddResultDecl(D);
|
2018-04-30 23:24:17 +08:00
|
|
|
}
|
|
|
|
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
// Now query the index for all Symbol IDs we found in the AST.
|
|
|
|
if (Index && !ResultIndex.empty()) {
|
2018-04-30 23:24:17 +08:00
|
|
|
LookupRequest QueryRequest;
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
for (auto It : ResultIndex)
|
2018-04-30 23:24:17 +08:00
|
|
|
QueryRequest.IDs.insert(It.first);
|
2019-02-12 18:38:45 +08:00
|
|
|
std::string Scratch;
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
Index->lookup(QueryRequest, [&](const Symbol &Sym) {
|
|
|
|
auto &R = Result[ResultIndex.lookup(Sym.ID)];
|
|
|
|
|
2019-02-11 23:05:29 +08:00
|
|
|
if (R.Definition) { // from AST
|
2019-05-03 20:11:14 +08:00
|
|
|
// Special case: if the AST yielded a definition, then it may not be
|
|
|
|
// the right *declaration*. Prefer the one from the index.
|
2020-03-03 01:45:05 +08:00
|
|
|
if (auto Loc = toLSPLocation(Sym.CanonicalDeclaration, MainFilePath))
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
R.PreferredDeclaration = *Loc;
|
2019-05-03 20:11:14 +08:00
|
|
|
|
|
|
|
// We might still prefer the definition from the index, e.g. for
|
|
|
|
// generated symbols.
|
|
|
|
if (auto Loc = toLSPLocation(
|
|
|
|
getPreferredLocation(*R.Definition, Sym.Definition, Scratch),
|
2020-03-03 01:45:05 +08:00
|
|
|
MainFilePath))
|
2019-05-03 20:11:14 +08:00
|
|
|
R.Definition = *Loc;
|
2019-02-11 23:05:29 +08:00
|
|
|
} else {
|
2020-03-03 01:45:05 +08:00
|
|
|
R.Definition = toLSPLocation(Sym.Definition, MainFilePath);
|
2019-02-11 23:05:29 +08:00
|
|
|
|
2019-05-03 20:11:14 +08:00
|
|
|
// Use merge logic to choose AST or index declaration.
|
|
|
|
if (auto Loc = toLSPLocation(
|
|
|
|
getPreferredLocation(R.PreferredDeclaration,
|
|
|
|
Sym.CanonicalDeclaration, Scratch),
|
2020-03-03 01:45:05 +08:00
|
|
|
MainFilePath))
|
2019-05-03 20:11:14 +08:00
|
|
|
R.PreferredDeclaration = *Loc;
|
2019-02-11 23:05:29 +08:00
|
|
|
}
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
});
|
2017-12-20 01:06:07 +08:00
|
|
|
}
|
|
|
|
|
2020-12-03 16:42:46 +08:00
|
|
|
auto Overrides = findImplementors(VirtualMethods, RelationKind::OverriddenBy,
|
|
|
|
Index, MainFilePath);
|
2020-12-14 15:55:47 +08:00
|
|
|
Result.insert(Result.end(), Overrides.begin(), Overrides.end());
|
2017-12-20 01:06:07 +08:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-12-15 23:31:25 +08:00
|
|
|
std::vector<LocatedSymbol> locateSymbolForType(const ParsedAST &AST,
|
|
|
|
const QualType &Type) {
|
|
|
|
const auto &SM = AST.getSourceManager();
|
|
|
|
auto MainFilePath =
|
|
|
|
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
|
|
|
|
if (!MainFilePath) {
|
|
|
|
elog("Failed to get a path for the main file, so no symbol.");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()),
|
|
|
|
DeclRelation::TemplatePattern | DeclRelation::Alias);
|
|
|
|
if (Decls.empty())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
std::vector<LocatedSymbol> Results;
|
|
|
|
const auto &ASTContext = AST.getASTContext();
|
|
|
|
|
|
|
|
for (const NamedDecl *D : Decls) {
|
|
|
|
D = getPreferredDecl(D);
|
|
|
|
|
|
|
|
auto Loc = makeLocation(ASTContext, nameLocation(*D, SM), *MainFilePath);
|
|
|
|
if (!Loc)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Results.emplace_back();
|
|
|
|
Results.back().Name = printName(ASTContext, *D);
|
|
|
|
Results.back().PreferredDeclaration = *Loc;
|
|
|
|
if (const NamedDecl *Def = getDefinition(D))
|
|
|
|
Results.back().Definition =
|
|
|
|
makeLocation(ASTContext, nameLocation(*Def, SM), *MainFilePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Results;
|
|
|
|
}
|
|
|
|
|
2020-03-03 05:45:25 +08:00
|
|
|
bool tokenSpelledAt(SourceLocation SpellingLoc, const syntax::TokenBuffer &TB) {
|
|
|
|
auto ExpandedTokens = TB.expandedTokens(
|
|
|
|
TB.sourceManager().getMacroArgExpandedLocation(SpellingLoc));
|
|
|
|
return !ExpandedTokens.empty();
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
}
|
|
|
|
|
2020-03-03 05:45:25 +08:00
|
|
|
llvm::StringRef sourcePrefix(SourceLocation Loc, const SourceManager &SM) {
|
|
|
|
auto D = SM.getDecomposedLoc(Loc);
|
|
|
|
bool Invalid = false;
|
|
|
|
llvm::StringRef Buf = SM.getBufferData(D.first, &Invalid);
|
|
|
|
if (Invalid || D.second > Buf.size())
|
|
|
|
return "";
|
|
|
|
return Buf.substr(0, D.second);
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
}
|
|
|
|
|
2020-03-20 04:28:53 +08:00
|
|
|
bool isDependentName(ASTNodeKind NodeKind) {
|
|
|
|
return NodeKind.isSame(ASTNodeKind::getFromNodeKind<OverloadExpr>()) ||
|
|
|
|
NodeKind.isSame(
|
|
|
|
ASTNodeKind::getFromNodeKind<CXXDependentScopeMemberExpr>()) ||
|
|
|
|
NodeKind.isSame(
|
|
|
|
ASTNodeKind::getFromNodeKind<DependentScopeDeclRefExpr>());
|
|
|
|
}
|
|
|
|
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
std::vector<LocatedSymbol>
|
2020-03-03 05:45:25 +08:00
|
|
|
locateSymbolTextually(const SpelledWord &Word, ParsedAST &AST,
|
2020-03-20 04:28:53 +08:00
|
|
|
const SymbolIndex *Index, const std::string &MainFilePath,
|
|
|
|
ASTNodeKind NodeKind) {
|
|
|
|
// Don't use heuristics if this is a real identifier, or not an
|
|
|
|
// identifier.
|
|
|
|
// Exception: dependent names, because those may have useful textual
|
|
|
|
// matches that AST-based heuristics cannot find.
|
|
|
|
if ((Word.ExpandedToken && !isDependentName(NodeKind)) ||
|
|
|
|
!Word.LikelyIdentifier || !Index)
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
return {};
|
2020-09-24 16:49:32 +08:00
|
|
|
// We don't want to handle words in string literals. (It'd be nice to list
|
|
|
|
// *allowed* token kinds explicitly, but comment Tokens aren't retained).
|
2020-03-03 05:45:25 +08:00
|
|
|
if (Word.PartOfSpelledToken &&
|
|
|
|
isStringLiteral(Word.PartOfSpelledToken->kind()))
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
return {};
|
|
|
|
|
2020-03-03 05:45:25 +08:00
|
|
|
const auto &SM = AST.getSourceManager();
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
// Look up the selected word in the index.
|
|
|
|
FuzzyFindRequest Req;
|
2020-03-03 05:45:25 +08:00
|
|
|
Req.Query = Word.Text.str();
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
Req.ProximityPaths = {MainFilePath};
|
2020-03-03 05:45:25 +08:00
|
|
|
// Find the namespaces to query by lexing the file.
|
|
|
|
Req.Scopes =
|
|
|
|
visibleNamespaces(sourcePrefix(Word.Location, SM), AST.getLangOpts());
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
// FIXME: For extra strictness, consider AnyScope=false.
|
|
|
|
Req.AnyScope = true;
|
|
|
|
// We limit the results to 3 further below. This limit is to avoid fetching
|
|
|
|
// too much data, while still likely having enough for 3 results to remain
|
|
|
|
// after additional filtering.
|
|
|
|
Req.Limit = 10;
|
|
|
|
bool TooMany = false;
|
|
|
|
using ScoredLocatedSymbol = std::pair<float, LocatedSymbol>;
|
|
|
|
std::vector<ScoredLocatedSymbol> ScoredResults;
|
|
|
|
Index->fuzzyFind(Req, [&](const Symbol &Sym) {
|
|
|
|
// Only consider exact name matches, including case.
|
|
|
|
// This is to avoid too many false positives.
|
|
|
|
// We could relax this in the future (e.g. to allow for typos) if we make
|
|
|
|
// the query more accurate by other means.
|
2020-03-03 05:45:25 +08:00
|
|
|
if (Sym.Name != Word.Text)
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Exclude constructor results. They have the same name as the class,
|
|
|
|
// but we don't have enough context to prefer them over the class.
|
|
|
|
if (Sym.SymInfo.Kind == index::SymbolKind::Constructor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto MaybeDeclLoc =
|
|
|
|
indexToLSPLocation(Sym.CanonicalDeclaration, MainFilePath);
|
|
|
|
if (!MaybeDeclLoc) {
|
|
|
|
log("locateSymbolNamedTextuallyAt: {0}", MaybeDeclLoc.takeError());
|
|
|
|
return;
|
|
|
|
}
|
2020-07-31 20:32:18 +08:00
|
|
|
LocatedSymbol Located;
|
|
|
|
Located.PreferredDeclaration = *MaybeDeclLoc;
|
|
|
|
Located.Name = (Sym.Name + Sym.TemplateSpecializationArgs).str();
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
if (Sym.Definition) {
|
|
|
|
auto MaybeDefLoc = indexToLSPLocation(Sym.Definition, MainFilePath);
|
|
|
|
if (!MaybeDefLoc) {
|
|
|
|
log("locateSymbolNamedTextuallyAt: {0}", MaybeDefLoc.takeError());
|
|
|
|
return;
|
|
|
|
}
|
2020-07-31 20:32:18 +08:00
|
|
|
Located.PreferredDeclaration = *MaybeDefLoc;
|
|
|
|
Located.Definition = *MaybeDefLoc;
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
}
|
|
|
|
|
2020-04-26 12:45:51 +08:00
|
|
|
if (ScoredResults.size() >= 5) {
|
|
|
|
// If we have more than 5 results, don't return anything,
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
// as confidence is too low.
|
|
|
|
// FIXME: Alternatively, try a stricter query?
|
|
|
|
TooMany = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SymbolQualitySignals Quality;
|
|
|
|
Quality.merge(Sym);
|
|
|
|
SymbolRelevanceSignals Relevance;
|
|
|
|
Relevance.Name = Sym.Name;
|
|
|
|
Relevance.Query = SymbolRelevanceSignals::Generic;
|
|
|
|
Relevance.merge(Sym);
|
2020-09-29 01:19:51 +08:00
|
|
|
auto Score = evaluateSymbolAndRelevance(Quality.evaluateHeuristics(),
|
|
|
|
Relevance.evaluateHeuristics());
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
dlog("locateSymbolNamedTextuallyAt: {0}{1} = {2}\n{3}{4}\n", Sym.Scope,
|
|
|
|
Sym.Name, Score, Quality, Relevance);
|
|
|
|
|
|
|
|
ScoredResults.push_back({Score, std::move(Located)});
|
|
|
|
});
|
|
|
|
|
2020-05-25 16:19:34 +08:00
|
|
|
if (TooMany) {
|
|
|
|
vlog("Heuristic index lookup for {0} returned too many candidates, ignored",
|
|
|
|
Word.Text);
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
return {};
|
2020-05-25 16:19:34 +08:00
|
|
|
}
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
|
|
|
|
llvm::sort(ScoredResults,
|
|
|
|
[](const ScoredLocatedSymbol &A, const ScoredLocatedSymbol &B) {
|
|
|
|
return A.first > B.first;
|
|
|
|
});
|
|
|
|
std::vector<LocatedSymbol> Results;
|
|
|
|
for (auto &Res : std::move(ScoredResults))
|
|
|
|
Results.push_back(std::move(Res.second));
|
2020-05-25 16:19:34 +08:00
|
|
|
if (Results.empty())
|
|
|
|
vlog("No heuristic index definition for {0}", Word.Text);
|
|
|
|
else
|
|
|
|
log("Found definition heuristically in index for {0}", Word.Text);
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
return Results;
|
|
|
|
}
|
|
|
|
|
2020-03-03 05:45:25 +08:00
|
|
|
const syntax::Token *findNearbyIdentifier(const SpelledWord &Word,
|
|
|
|
const syntax::TokenBuffer &TB) {
|
|
|
|
// Don't use heuristics if this is a real identifier.
|
|
|
|
// Unlikely identifiers are OK if they were used as identifiers nearby.
|
|
|
|
if (Word.ExpandedToken)
|
|
|
|
return nullptr;
|
2020-09-24 16:49:32 +08:00
|
|
|
// We don't want to handle words in string literals. (It'd be nice to list
|
|
|
|
// *allowed* token kinds explicitly, but comment Tokens aren't retained).
|
2020-03-03 05:45:25 +08:00
|
|
|
if (Word.PartOfSpelledToken &&
|
|
|
|
isStringLiteral(Word.PartOfSpelledToken->kind()))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
const SourceManager &SM = TB.sourceManager();
|
|
|
|
// We prefer the closest possible token, line-wise. Backwards is penalized.
|
|
|
|
// Ties are implicitly broken by traversal order (first-one-wins).
|
|
|
|
auto File = SM.getFileID(Word.Location);
|
|
|
|
unsigned WordLine = SM.getSpellingLineNumber(Word.Location);
|
|
|
|
auto Cost = [&](SourceLocation Loc) -> unsigned {
|
|
|
|
assert(SM.getFileID(Loc) == File && "spelled token in wrong file?");
|
|
|
|
unsigned Line = SM.getSpellingLineNumber(Loc);
|
2020-09-30 00:54:33 +08:00
|
|
|
return Line >= WordLine ? Line - WordLine : 2 * (WordLine - Line);
|
2020-03-03 05:45:25 +08:00
|
|
|
};
|
|
|
|
const syntax::Token *BestTok = nullptr;
|
2020-09-30 00:54:33 +08:00
|
|
|
unsigned BestCost = -1;
|
|
|
|
// Search bounds are based on word length:
|
|
|
|
// - forward: 2^N lines
|
|
|
|
// - backward: 2^(N-1) lines.
|
|
|
|
unsigned MaxDistance =
|
|
|
|
1U << std::min<unsigned>(Word.Text.size(),
|
|
|
|
std::numeric_limits<unsigned>::digits - 1);
|
|
|
|
// Line number for SM.translateLineCol() should be one-based, also
|
|
|
|
// SM.translateLineCol() can handle line number greater than
|
|
|
|
// number of lines in the file.
|
|
|
|
// - LineMin = max(1, WordLine + 1 - 2^(N-1))
|
|
|
|
// - LineMax = WordLine + 1 + 2^N
|
|
|
|
unsigned LineMin =
|
|
|
|
WordLine + 1 <= MaxDistance / 2 ? 1 : WordLine + 1 - MaxDistance / 2;
|
|
|
|
unsigned LineMax = WordLine + 1 + MaxDistance;
|
|
|
|
SourceLocation LocMin = SM.translateLineCol(File, LineMin, 1);
|
|
|
|
assert(LocMin.isValid());
|
|
|
|
SourceLocation LocMax = SM.translateLineCol(File, LineMax, 1);
|
|
|
|
assert(LocMax.isValid());
|
2020-03-03 05:45:25 +08:00
|
|
|
|
|
|
|
// Updates BestTok and BestCost if Tok is a good candidate.
|
|
|
|
// May return true if the cost is too high for this token.
|
|
|
|
auto Consider = [&](const syntax::Token &Tok) {
|
2020-09-30 00:54:33 +08:00
|
|
|
if (Tok.location() < LocMin || Tok.location() > LocMax)
|
|
|
|
return true; // we are too far from the word, break the outer loop.
|
2020-03-03 05:45:25 +08:00
|
|
|
if (!(Tok.kind() == tok::identifier && Tok.text(SM) == Word.Text))
|
|
|
|
return false;
|
|
|
|
// No point guessing the same location we started with.
|
|
|
|
if (Tok.location() == Word.Location)
|
|
|
|
return false;
|
|
|
|
// We've done cheap checks, compute cost so we can break the caller's loop.
|
|
|
|
unsigned TokCost = Cost(Tok.location());
|
|
|
|
if (TokCost >= BestCost)
|
|
|
|
return true; // causes the outer loop to break.
|
|
|
|
// Allow locations that might be part of the AST, and macros (even if empty)
|
|
|
|
// but not things like disabled preprocessor sections.
|
|
|
|
if (!(tokenSpelledAt(Tok.location(), TB) || TB.expansionStartingAt(&Tok)))
|
|
|
|
return false;
|
|
|
|
// We already verified this token is an improvement.
|
|
|
|
BestCost = TokCost;
|
|
|
|
BestTok = &Tok;
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
auto SpelledTokens = TB.spelledTokens(File);
|
|
|
|
// Find where the word occurred in the token stream, to search forward & back.
|
|
|
|
auto *I = llvm::partition_point(SpelledTokens, [&](const syntax::Token &T) {
|
|
|
|
assert(SM.getFileID(T.location()) == SM.getFileID(Word.Location));
|
2020-07-30 17:45:07 +08:00
|
|
|
return T.location() < Word.Location; // Comparison OK: same file.
|
2020-03-03 05:45:25 +08:00
|
|
|
});
|
|
|
|
// Search for matches after the cursor.
|
|
|
|
for (const syntax::Token &Tok : llvm::makeArrayRef(I, SpelledTokens.end()))
|
|
|
|
if (Consider(Tok))
|
|
|
|
break; // costs of later tokens are greater...
|
|
|
|
// Search for matches before the cursor.
|
|
|
|
for (const syntax::Token &Tok :
|
|
|
|
llvm::reverse(llvm::makeArrayRef(SpelledTokens.begin(), I)))
|
|
|
|
if (Consider(Tok))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (BestTok)
|
|
|
|
vlog(
|
|
|
|
"Word {0} under cursor {1} isn't a token (after PP), trying nearby {2}",
|
|
|
|
Word.Text, Word.Location.printToString(SM),
|
|
|
|
BestTok->location().printToString(SM));
|
|
|
|
|
|
|
|
return BestTok;
|
|
|
|
}
|
|
|
|
|
2020-03-03 01:45:05 +08:00
|
|
|
std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
|
|
|
|
const SymbolIndex *Index) {
|
|
|
|
const auto &SM = AST.getSourceManager();
|
|
|
|
auto MainFilePath =
|
|
|
|
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
|
|
|
|
if (!MainFilePath) {
|
|
|
|
elog("Failed to get a path for the main file, so no references");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto File = locateFileReferent(Pos, AST, *MainFilePath))
|
|
|
|
return {std::move(*File)};
|
|
|
|
|
|
|
|
auto CurLoc = sourceLocationInMainFile(SM, Pos);
|
|
|
|
if (!CurLoc) {
|
|
|
|
elog("locateSymbolAt failed to convert position to source location: {0}",
|
|
|
|
CurLoc.takeError());
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-12-15 23:31:25 +08:00
|
|
|
const syntax::Token *TouchedIdentifier = nullptr;
|
|
|
|
auto TokensTouchingCursor =
|
|
|
|
syntax::spelledTokensTouching(*CurLoc, AST.getTokens());
|
|
|
|
for (const syntax::Token &Tok : TokensTouchingCursor) {
|
|
|
|
if (Tok.kind() == tok::identifier) {
|
|
|
|
if (auto Macro = locateMacroReferent(Tok, AST, *MainFilePath))
|
|
|
|
// Don't look at the AST or index if we have a macro result.
|
|
|
|
// (We'd just return declarations referenced from the macro's
|
|
|
|
// expansion.)
|
|
|
|
return {*std::move(Macro)};
|
|
|
|
|
|
|
|
TouchedIdentifier = &Tok;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {
|
|
|
|
// go-to-definition on auto should find the definition of the deduced
|
|
|
|
// type, if possible
|
|
|
|
if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) {
|
|
|
|
auto LocSym = locateSymbolForType(AST, *Deduced);
|
|
|
|
if (!LocSym.empty())
|
|
|
|
return LocSym;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-03 01:45:05 +08:00
|
|
|
|
2020-03-20 04:28:53 +08:00
|
|
|
ASTNodeKind NodeKind;
|
|
|
|
auto ASTResults = locateASTReferent(*CurLoc, TouchedIdentifier, AST,
|
|
|
|
*MainFilePath, Index, &NodeKind);
|
[clangd] Add a textual fallback for go-to-definition
Summary:
This facilitates performing go-to-definition in contexts where AST-based
resolution does not work, such as comments, string literals, preprocessor
disabled regions, and macro definitions, based on textual lookup in the index.
Partially fixes https://github.com/clangd/clangd/issues/241
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72874
2020-03-06 05:47:32 +08:00
|
|
|
if (!ASTResults.empty())
|
|
|
|
return ASTResults;
|
|
|
|
|
2020-03-03 05:45:25 +08:00
|
|
|
// If the cursor can't be resolved directly, try fallback strategies.
|
|
|
|
auto Word =
|
|
|
|
SpelledWord::touching(*CurLoc, AST.getTokens(), AST.getLangOpts());
|
|
|
|
if (Word) {
|
|
|
|
// Is the same word nearby a real identifier that might refer to something?
|
|
|
|
if (const syntax::Token *NearbyIdent =
|
|
|
|
findNearbyIdentifier(*Word, AST.getTokens())) {
|
2020-05-25 16:19:34 +08:00
|
|
|
if (auto Macro = locateMacroReferent(*NearbyIdent, AST, *MainFilePath)) {
|
|
|
|
log("Found macro definition heuristically using nearby identifier {0}",
|
|
|
|
Word->Text);
|
2020-03-03 05:45:25 +08:00
|
|
|
return {*std::move(Macro)};
|
2020-05-25 16:19:34 +08:00
|
|
|
}
|
2020-03-20 04:28:53 +08:00
|
|
|
ASTResults =
|
|
|
|
locateASTReferent(NearbyIdent->location(), NearbyIdent, AST,
|
|
|
|
*MainFilePath, Index, /*NodeKind=*/nullptr);
|
2020-05-25 16:19:34 +08:00
|
|
|
if (!ASTResults.empty()) {
|
|
|
|
log("Found definition heuristically using nearby identifier {0}",
|
|
|
|
NearbyIdent->text(SM));
|
2020-03-03 05:45:25 +08:00
|
|
|
return ASTResults;
|
2020-05-25 16:19:34 +08:00
|
|
|
} else {
|
|
|
|
vlog("No definition found using nearby identifier {0} at {1}",
|
|
|
|
Word->Text, Word->Location.printToString(SM));
|
|
|
|
}
|
2020-03-03 05:45:25 +08:00
|
|
|
}
|
|
|
|
// No nearby word, or it didn't refer to anything either. Try the index.
|
|
|
|
auto TextualResults =
|
2020-03-20 04:28:53 +08:00
|
|
|
locateSymbolTextually(*Word, AST, Index, *MainFilePath, NodeKind);
|
2020-03-03 05:45:25 +08:00
|
|
|
if (!TextualResults.empty())
|
|
|
|
return TextualResults;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
2020-03-03 01:45:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) {
|
|
|
|
const auto &SM = AST.getSourceManager();
|
|
|
|
auto MainFilePath =
|
|
|
|
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
|
|
|
|
if (!MainFilePath) {
|
|
|
|
elog("Failed to get a path for the main file, so no links");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<DocumentLink> Result;
|
|
|
|
for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
|
2020-05-04 16:48:19 +08:00
|
|
|
if (Inc.Resolved.empty())
|
|
|
|
continue;
|
|
|
|
auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
|
|
|
|
const auto *HashTok = AST.getTokens().spelledTokenAt(HashLoc);
|
|
|
|
assert(HashTok && "got inclusion at wrong offset");
|
|
|
|
const auto *IncludeTok = std::next(HashTok);
|
|
|
|
const auto *FileTok = std::next(IncludeTok);
|
|
|
|
// FileTok->range is not sufficient here, as raw lexing wouldn't yield
|
|
|
|
// correct tokens for angled filenames. Hence we explicitly use
|
|
|
|
// Inc.Written's length.
|
|
|
|
auto FileRange =
|
|
|
|
syntax::FileRange(SM, FileTok->location(), Inc.Written.length())
|
|
|
|
.toCharRange(SM);
|
|
|
|
|
|
|
|
Result.push_back(
|
|
|
|
DocumentLink({halfOpenToRange(SM, FileRange),
|
|
|
|
URIForFile::canonicalize(Inc.Resolved, *MainFilePath)}));
|
2020-03-03 01:45:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2017-12-20 01:06:07 +08:00
|
|
|
namespace {
|
|
|
|
|
2018-09-05 18:33:36 +08:00
|
|
|
/// Collects references to symbols within the main file.
|
|
|
|
class ReferenceFinder : public index::IndexDataConsumer {
|
2017-12-20 01:06:07 +08:00
|
|
|
public:
|
2018-09-05 18:33:36 +08:00
|
|
|
struct Reference {
|
2020-03-01 23:05:12 +08:00
|
|
|
syntax::Token SpelledTok;
|
2018-09-05 18:33:36 +08:00
|
|
|
index::SymbolRoleSet Role;
|
2020-03-01 23:05:12 +08:00
|
|
|
|
|
|
|
Range range(const SourceManager &SM) const {
|
|
|
|
return halfOpenToRange(SM, SpelledTok.range(SM).toCharRange(SM));
|
|
|
|
}
|
2018-09-05 18:33:36 +08:00
|
|
|
};
|
|
|
|
|
2020-03-01 23:05:12 +08:00
|
|
|
ReferenceFinder(const ParsedAST &AST,
|
[clangd] targetDecl() returns only NamedDecls.
Summary:
While it's perfectly reasonable for non-named decls such as
static_assert to resolve to themselves:
- nothing else ever resolves to them
- features based on references (hover, highlight, find refs etc) tend
to be uninteresting where only trivial references are possible
- returning NamedDecl is a more convenient API (we cast to it in many places)
- this aligns closer to findExplicitReferences/explicitReferenceTargets
This fixes a crash in explicitReferenceTargets: if the target is a
non-named decl then there's an invalid unchecked cast to NamedDecl.
In practice this means when hovering over e.g. a static_assert:
- before ac3f9e4842, we would show a (boring) hover card
- after ac3f9e4842, we would crash
- after this patch, we will show nothing
Reviewers: kadircet, ilya-biryukov
Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72163
2020-01-04 00:26:33 +08:00
|
|
|
const std::vector<const NamedDecl *> &TargetDecls)
|
2018-09-05 18:33:36 +08:00
|
|
|
: AST(AST) {
|
[clangd] targetDecl() returns only NamedDecls.
Summary:
While it's perfectly reasonable for non-named decls such as
static_assert to resolve to themselves:
- nothing else ever resolves to them
- features based on references (hover, highlight, find refs etc) tend
to be uninteresting where only trivial references are possible
- returning NamedDecl is a more convenient API (we cast to it in many places)
- this aligns closer to findExplicitReferences/explicitReferenceTargets
This fixes a crash in explicitReferenceTargets: if the target is a
non-named decl then there's an invalid unchecked cast to NamedDecl.
In practice this means when hovering over e.g. a static_assert:
- before ac3f9e4842, we would show a (boring) hover card
- after ac3f9e4842, we would crash
- after this patch, we will show nothing
Reviewers: kadircet, ilya-biryukov
Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72163
2020-01-04 00:26:33 +08:00
|
|
|
for (const NamedDecl *D : TargetDecls)
|
2018-10-04 17:56:08 +08:00
|
|
|
CanonicalTargets.insert(D->getCanonicalDecl());
|
2018-09-05 18:33:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Reference> take() && {
|
2018-10-08 01:21:08 +08:00
|
|
|
llvm::sort(References, [](const Reference &L, const Reference &R) {
|
2020-03-01 23:05:12 +08:00
|
|
|
auto LTok = L.SpelledTok.location();
|
|
|
|
auto RTok = R.SpelledTok.location();
|
|
|
|
return std::tie(LTok, L.Role) < std::tie(RTok, R.Role);
|
2018-10-08 01:21:08 +08:00
|
|
|
});
|
2018-09-05 18:33:36 +08:00
|
|
|
// We sometimes see duplicates when parts of the AST get traversed twice.
|
2019-08-20 22:07:27 +08:00
|
|
|
References.erase(std::unique(References.begin(), References.end(),
|
|
|
|
[](const Reference &L, const Reference &R) {
|
2020-03-01 23:05:12 +08:00
|
|
|
auto LTok = L.SpelledTok.location();
|
|
|
|
auto RTok = R.SpelledTok.location();
|
|
|
|
return std::tie(LTok, L.Role) ==
|
|
|
|
std::tie(RTok, R.Role);
|
2019-08-20 22:07:27 +08:00
|
|
|
}),
|
|
|
|
References.end());
|
2018-09-05 18:33:36 +08:00
|
|
|
return std::move(References);
|
2017-12-20 01:06:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-12-16 17:33:56 +08:00
|
|
|
handleDeclOccurrence(const Decl *D, index::SymbolRoleSet Roles,
|
|
|
|
llvm::ArrayRef<index::SymbolRelation> Relations,
|
|
|
|
SourceLocation Loc,
|
|
|
|
index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
|
2018-10-04 17:56:08 +08:00
|
|
|
assert(D->isCanonicalDecl() && "expect D to be a canonical declaration");
|
2020-04-02 15:29:16 +08:00
|
|
|
const SourceManager &SM = AST.getSourceManager();
|
|
|
|
if (!CanonicalTargets.count(D) || !isInsideMainFile(Loc, SM))
|
2020-03-01 23:05:12 +08:00
|
|
|
return true;
|
|
|
|
const auto &TB = AST.getTokens();
|
2018-09-05 18:33:36 +08:00
|
|
|
Loc = SM.getFileLoc(Loc);
|
2020-03-01 23:05:12 +08:00
|
|
|
if (const auto *Tok = TB.spelledTokenAt(Loc))
|
|
|
|
References.push_back({*Tok, Roles});
|
2017-12-20 01:06:07 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::SmallSet<const Decl *, 4> CanonicalTargets;
|
2018-09-05 18:33:36 +08:00
|
|
|
std::vector<Reference> References;
|
2020-03-01 23:05:12 +08:00
|
|
|
const ParsedAST &AST;
|
2017-12-20 01:06:07 +08:00
|
|
|
};
|
|
|
|
|
2018-09-05 18:33:36 +08:00
|
|
|
std::vector<ReferenceFinder::Reference>
|
[clangd] targetDecl() returns only NamedDecls.
Summary:
While it's perfectly reasonable for non-named decls such as
static_assert to resolve to themselves:
- nothing else ever resolves to them
- features based on references (hover, highlight, find refs etc) tend
to be uninteresting where only trivial references are possible
- returning NamedDecl is a more convenient API (we cast to it in many places)
- this aligns closer to findExplicitReferences/explicitReferenceTargets
This fixes a crash in explicitReferenceTargets: if the target is a
non-named decl then there's an invalid unchecked cast to NamedDecl.
In practice this means when hovering over e.g. a static_assert:
- before ac3f9e4842, we would show a (boring) hover card
- after ac3f9e4842, we would crash
- after this patch, we will show nothing
Reviewers: kadircet, ilya-biryukov
Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72163
2020-01-04 00:26:33 +08:00
|
|
|
findRefs(const std::vector<const NamedDecl *> &Decls, ParsedAST &AST) {
|
2020-03-01 23:05:12 +08:00
|
|
|
ReferenceFinder RefFinder(AST, Decls);
|
2018-04-30 23:24:17 +08:00
|
|
|
index::IndexingOptions IndexOpts;
|
|
|
|
IndexOpts.SystemSymbolFilter =
|
|
|
|
index::IndexingOptions::SystemSymbolFilterKind::All;
|
|
|
|
IndexOpts.IndexFunctionLocals = true;
|
2019-02-11 21:03:08 +08:00
|
|
|
IndexOpts.IndexParametersInDeclarations = true;
|
2019-02-21 17:55:00 +08:00
|
|
|
IndexOpts.IndexTemplateParameters = true;
|
2018-09-18 16:52:14 +08:00
|
|
|
indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
|
|
|
|
AST.getLocalTopLevelDecls(), RefFinder, IndexOpts);
|
2018-09-05 18:33:36 +08:00
|
|
|
return std::move(RefFinder).take();
|
|
|
|
}
|
|
|
|
|
2020-04-19 08:19:25 +08:00
|
|
|
const Stmt *getFunctionBody(DynTypedNode N) {
|
|
|
|
if (const auto *FD = N.get<FunctionDecl>())
|
|
|
|
return FD->getBody();
|
|
|
|
if (const auto *FD = N.get<BlockDecl>())
|
|
|
|
return FD->getBody();
|
|
|
|
if (const auto *FD = N.get<LambdaExpr>())
|
|
|
|
return FD->getBody();
|
|
|
|
if (const auto *FD = N.get<ObjCMethodDecl>())
|
|
|
|
return FD->getBody();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Stmt *getLoopBody(DynTypedNode N) {
|
|
|
|
if (const auto *LS = N.get<ForStmt>())
|
|
|
|
return LS->getBody();
|
|
|
|
if (const auto *LS = N.get<CXXForRangeStmt>())
|
|
|
|
return LS->getBody();
|
|
|
|
if (const auto *LS = N.get<WhileStmt>())
|
|
|
|
return LS->getBody();
|
|
|
|
if (const auto *LS = N.get<DoStmt>())
|
|
|
|
return LS->getBody();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AST traversal to highlight control flow statements under some root.
|
|
|
|
// Once we hit further control flow we prune the tree (or at least restrict
|
|
|
|
// what we highlight) so we capture e.g. breaks from the outer loop only.
|
|
|
|
class FindControlFlow : public RecursiveASTVisitor<FindControlFlow> {
|
|
|
|
// Types of control-flow statements we might highlight.
|
|
|
|
enum Target {
|
|
|
|
Break = 1,
|
|
|
|
Continue = 2,
|
|
|
|
Return = 4,
|
|
|
|
Case = 8,
|
|
|
|
Throw = 16,
|
|
|
|
Goto = 32,
|
|
|
|
All = Break | Continue | Return | Case | Throw | Goto,
|
|
|
|
};
|
|
|
|
int Ignore = 0; // bitmask of Target - what are we *not* highlighting?
|
|
|
|
SourceRange Bounds; // Half-open, restricts reported targets.
|
|
|
|
std::vector<SourceLocation> &Result;
|
|
|
|
const SourceManager &SM;
|
|
|
|
|
|
|
|
// Masks out targets for a traversal into D.
|
|
|
|
// Traverses the subtree using Delegate() if any targets remain.
|
|
|
|
template <typename Func>
|
|
|
|
bool filterAndTraverse(DynTypedNode D, const Func &Delegate) {
|
|
|
|
auto RestoreIgnore = llvm::make_scope_exit(
|
|
|
|
[OldIgnore(Ignore), this] { Ignore = OldIgnore; });
|
|
|
|
if (getFunctionBody(D))
|
|
|
|
Ignore = All;
|
|
|
|
else if (getLoopBody(D))
|
|
|
|
Ignore |= Continue | Break;
|
|
|
|
else if (D.get<SwitchStmt>())
|
|
|
|
Ignore |= Break | Case;
|
|
|
|
// Prune tree if we're not looking for anything.
|
|
|
|
return (Ignore == All) ? true : Delegate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void found(Target T, SourceLocation Loc) {
|
|
|
|
if (T & Ignore)
|
|
|
|
return;
|
|
|
|
if (SM.isBeforeInTranslationUnit(Loc, Bounds.getBegin()) ||
|
|
|
|
SM.isBeforeInTranslationUnit(Bounds.getEnd(), Loc))
|
|
|
|
return;
|
|
|
|
Result.push_back(Loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
FindControlFlow(SourceRange Bounds, std::vector<SourceLocation> &Result,
|
|
|
|
const SourceManager &SM)
|
|
|
|
: Bounds(Bounds), Result(Result), SM(SM) {}
|
|
|
|
|
|
|
|
// When traversing function or loops, limit targets to those that still
|
|
|
|
// refer to the original root.
|
|
|
|
bool TraverseDecl(Decl *D) {
|
|
|
|
return !D || filterAndTraverse(DynTypedNode::create(*D), [&] {
|
|
|
|
return RecursiveASTVisitor::TraverseDecl(D);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
bool TraverseStmt(Stmt *S) {
|
|
|
|
return !S || filterAndTraverse(DynTypedNode::create(*S), [&] {
|
|
|
|
return RecursiveASTVisitor::TraverseStmt(S);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add leaves that we found and want.
|
|
|
|
bool VisitReturnStmt(ReturnStmt *R) {
|
|
|
|
found(Return, R->getReturnLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool VisitBreakStmt(BreakStmt *B) {
|
|
|
|
found(Break, B->getBreakLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool VisitContinueStmt(ContinueStmt *C) {
|
|
|
|
found(Continue, C->getContinueLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool VisitSwitchCase(SwitchCase *C) {
|
|
|
|
found(Case, C->getKeywordLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool VisitCXXThrowExpr(CXXThrowExpr *T) {
|
|
|
|
found(Throw, T->getThrowLoc());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool VisitGotoStmt(GotoStmt *G) {
|
|
|
|
// Goto is interesting if its target is outside the root.
|
|
|
|
if (const auto *LD = G->getLabel()) {
|
|
|
|
if (SM.isBeforeInTranslationUnit(LD->getLocation(), Bounds.getBegin()) ||
|
|
|
|
SM.isBeforeInTranslationUnit(Bounds.getEnd(), LD->getLocation()))
|
|
|
|
found(Goto, G->getGotoLoc());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Given a location within a switch statement, return the half-open range that
|
|
|
|
// covers the case it's contained in.
|
|
|
|
// We treat `case X: case Y: ...` as one case, and assume no other fallthrough.
|
|
|
|
SourceRange findCaseBounds(const SwitchStmt &Switch, SourceLocation Loc,
|
|
|
|
const SourceManager &SM) {
|
|
|
|
// Cases are not stored in order, sort them first.
|
|
|
|
// (In fact they seem to be stored in reverse order, don't rely on this)
|
|
|
|
std::vector<const SwitchCase *> Cases;
|
|
|
|
for (const SwitchCase *Case = Switch.getSwitchCaseList(); Case;
|
|
|
|
Case = Case->getNextSwitchCase())
|
|
|
|
Cases.push_back(Case);
|
|
|
|
llvm::sort(Cases, [&](const SwitchCase *L, const SwitchCase *R) {
|
|
|
|
return SM.isBeforeInTranslationUnit(L->getKeywordLoc(), R->getKeywordLoc());
|
|
|
|
});
|
|
|
|
|
|
|
|
// Find the first case after the target location, the end of our range.
|
|
|
|
auto CaseAfter = llvm::partition_point(Cases, [&](const SwitchCase *C) {
|
|
|
|
return !SM.isBeforeInTranslationUnit(Loc, C->getKeywordLoc());
|
|
|
|
});
|
|
|
|
SourceLocation End = CaseAfter == Cases.end() ? Switch.getEndLoc()
|
|
|
|
: (*CaseAfter)->getKeywordLoc();
|
|
|
|
|
|
|
|
// Our target can be before the first case - cases are optional!
|
|
|
|
if (CaseAfter == Cases.begin())
|
|
|
|
return SourceRange(Switch.getBeginLoc(), End);
|
|
|
|
// The start of our range is usually the previous case, but...
|
|
|
|
auto CaseBefore = std::prev(CaseAfter);
|
|
|
|
// ... rewind CaseBefore to the first in a `case A: case B: ...` sequence.
|
|
|
|
while (CaseBefore != Cases.begin() &&
|
|
|
|
(*std::prev(CaseBefore))->getSubStmt() == *CaseBefore)
|
|
|
|
--CaseBefore;
|
|
|
|
return SourceRange((*CaseBefore)->getKeywordLoc(), End);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the locations of control flow statements related to N. e.g.:
|
|
|
|
// for => branches: break/continue/return/throw
|
|
|
|
// break => controlling loop (forwhile/do), and its related control flow
|
|
|
|
// return => all returns/throws from the same function
|
|
|
|
// When an inner block is selected, we include branches bound to outer blocks
|
|
|
|
// as these are exits from the inner block. e.g. return in a for loop.
|
|
|
|
// FIXME: We don't analyze catch blocks, throw is treated the same as return.
|
|
|
|
std::vector<SourceLocation> relatedControlFlow(const SelectionTree::Node &N) {
|
|
|
|
const SourceManager &SM =
|
|
|
|
N.getDeclContext().getParentASTContext().getSourceManager();
|
|
|
|
std::vector<SourceLocation> Result;
|
|
|
|
|
|
|
|
// First, check if we're at a node that can resolve to a root.
|
|
|
|
enum class Cur { None, Break, Continue, Return, Case, Throw } Cursor;
|
|
|
|
if (N.ASTNode.get<BreakStmt>()) {
|
|
|
|
Cursor = Cur::Break;
|
|
|
|
} else if (N.ASTNode.get<ContinueStmt>()) {
|
|
|
|
Cursor = Cur::Continue;
|
|
|
|
} else if (N.ASTNode.get<ReturnStmt>()) {
|
|
|
|
Cursor = Cur::Return;
|
|
|
|
} else if (N.ASTNode.get<CXXThrowExpr>()) {
|
|
|
|
Cursor = Cur::Throw;
|
|
|
|
} else if (N.ASTNode.get<SwitchCase>()) {
|
|
|
|
Cursor = Cur::Case;
|
|
|
|
} else if (const GotoStmt *GS = N.ASTNode.get<GotoStmt>()) {
|
|
|
|
// We don't know what root to associate with, but highlight the goto/label.
|
|
|
|
Result.push_back(GS->getGotoLoc());
|
|
|
|
if (const auto *LD = GS->getLabel())
|
|
|
|
Result.push_back(LD->getLocation());
|
|
|
|
Cursor = Cur::None;
|
|
|
|
} else {
|
|
|
|
Cursor = Cur::None;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Stmt *Root = nullptr; // Loop or function body to traverse.
|
|
|
|
SourceRange Bounds;
|
|
|
|
// Look up the tree for a root (or just at this node if we didn't find a leaf)
|
|
|
|
for (const auto *P = &N; P; P = P->Parent) {
|
|
|
|
// return associates with enclosing function
|
|
|
|
if (const Stmt *FunctionBody = getFunctionBody(P->ASTNode)) {
|
|
|
|
if (Cursor == Cur::Return || Cursor == Cur::Throw) {
|
|
|
|
Root = FunctionBody;
|
|
|
|
}
|
|
|
|
break; // other leaves don't cross functions.
|
|
|
|
}
|
|
|
|
// break/continue associate with enclosing loop.
|
|
|
|
if (const Stmt *LoopBody = getLoopBody(P->ASTNode)) {
|
|
|
|
if (Cursor == Cur::None || Cursor == Cur::Break ||
|
|
|
|
Cursor == Cur::Continue) {
|
|
|
|
Root = LoopBody;
|
|
|
|
// Highlight the loop keyword itself.
|
|
|
|
// FIXME: for do-while, this only covers the `do`..
|
|
|
|
Result.push_back(P->ASTNode.getSourceRange().getBegin());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// For switches, users think of case statements as control flow blocks.
|
|
|
|
// We highlight only occurrences surrounded by the same case.
|
|
|
|
// We don't detect fallthrough (other than 'case X, case Y').
|
|
|
|
if (const auto *SS = P->ASTNode.get<SwitchStmt>()) {
|
|
|
|
if (Cursor == Cur::Break || Cursor == Cur::Case) {
|
|
|
|
Result.push_back(SS->getSwitchLoc()); // Highlight the switch.
|
|
|
|
Root = SS->getBody();
|
|
|
|
// Limit to enclosing case, if there is one.
|
|
|
|
Bounds = findCaseBounds(*SS, N.ASTNode.getSourceRange().getBegin(), SM);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we didn't start at some interesting node, we're done.
|
|
|
|
if (Cursor == Cur::None)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (Root) {
|
|
|
|
if (!Bounds.isValid())
|
|
|
|
Bounds = Root->getSourceRange();
|
|
|
|
FindControlFlow(Bounds, Result, SM).TraverseStmt(const_cast<Stmt *>(Root));
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
DocumentHighlight toHighlight(const ReferenceFinder::Reference &Ref,
|
|
|
|
const SourceManager &SM) {
|
|
|
|
DocumentHighlight DH;
|
|
|
|
DH.range = Ref.range(SM);
|
|
|
|
if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
|
|
|
|
DH.kind = DocumentHighlightKind::Write;
|
|
|
|
else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
|
|
|
|
DH.kind = DocumentHighlightKind::Read;
|
|
|
|
else
|
|
|
|
DH.kind = DocumentHighlightKind::Text;
|
|
|
|
return DH;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Optional<DocumentHighlight> toHighlight(SourceLocation Loc,
|
|
|
|
const syntax::TokenBuffer &TB) {
|
|
|
|
Loc = TB.sourceManager().getFileLoc(Loc);
|
|
|
|
if (const auto *Tok = TB.spelledTokenAt(Loc)) {
|
|
|
|
DocumentHighlight Result;
|
|
|
|
Result.range = halfOpenToRange(
|
|
|
|
TB.sourceManager(),
|
|
|
|
CharSourceRange::getCharRange(Tok->location(), Tok->endLocation()));
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
return llvm::None;
|
|
|
|
}
|
|
|
|
|
2018-09-05 18:33:36 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
|
|
|
|
Position Pos) {
|
2019-05-29 05:52:34 +08:00
|
|
|
const SourceManager &SM = AST.getSourceManager();
|
2019-08-02 18:39:46 +08:00
|
|
|
// FIXME: show references to macro within file?
|
2020-02-26 20:39:46 +08:00
|
|
|
auto CurLoc = sourceLocationInMainFile(SM, Pos);
|
|
|
|
if (!CurLoc) {
|
|
|
|
llvm::consumeError(CurLoc.takeError());
|
|
|
|
return {};
|
|
|
|
}
|
2018-09-05 18:33:36 +08:00
|
|
|
std::vector<DocumentHighlight> Result;
|
2020-04-19 08:19:25 +08:00
|
|
|
auto TryTree = [&](SelectionTree ST) {
|
|
|
|
if (const SelectionTree::Node *N = ST.commonAncestor()) {
|
|
|
|
DeclRelationSet Relations =
|
|
|
|
DeclRelation::TemplatePattern | DeclRelation::Alias;
|
|
|
|
auto Decls = targetDecl(N->ASTNode, Relations);
|
|
|
|
if (!Decls.empty()) {
|
|
|
|
// FIXME: we may get multiple DocumentHighlights with the same location
|
|
|
|
// and different kinds, deduplicate them.
|
|
|
|
for (const auto &Ref : findRefs({Decls.begin(), Decls.end()}, AST))
|
|
|
|
Result.push_back(toHighlight(Ref, SM));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
auto ControlFlow = relatedControlFlow(*N);
|
|
|
|
if (!ControlFlow.empty()) {
|
|
|
|
for (SourceLocation Loc : ControlFlow)
|
|
|
|
if (auto Highlight = toHighlight(Loc, AST.getTokens()))
|
|
|
|
Result.push_back(std::move(*Highlight));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
unsigned Offset =
|
|
|
|
AST.getSourceManager().getDecomposedSpellingLoc(*CurLoc).second;
|
|
|
|
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Offset,
|
|
|
|
Offset, TryTree);
|
2018-09-05 18:33:36 +08:00
|
|
|
return Result;
|
2017-12-20 01:06:07 +08:00
|
|
|
}
|
|
|
|
|
2020-11-19 01:13:11 +08:00
|
|
|
std::vector<LocatedSymbol> findImplementations(ParsedAST &AST, Position Pos,
|
|
|
|
const SymbolIndex *Index) {
|
|
|
|
// We rely on index to find the implementations in subclasses.
|
|
|
|
// FIXME: Index can be stale, so we may loose some latest results from the
|
|
|
|
// main file.
|
|
|
|
if (!Index)
|
|
|
|
return {};
|
|
|
|
const SourceManager &SM = AST.getSourceManager();
|
|
|
|
auto MainFilePath =
|
|
|
|
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
|
|
|
|
if (!MainFilePath) {
|
|
|
|
elog("Failed to get a path for the main file, so no implementations.");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
auto CurLoc = sourceLocationInMainFile(SM, Pos);
|
|
|
|
if (!CurLoc) {
|
|
|
|
elog("Failed to convert position to source location: {0}",
|
|
|
|
CurLoc.takeError());
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
DeclRelationSet Relations =
|
|
|
|
DeclRelation::TemplatePattern | DeclRelation::Alias;
|
2020-12-03 16:42:46 +08:00
|
|
|
llvm::DenseSet<SymbolID> IDs;
|
|
|
|
RelationKind QueryKind;
|
|
|
|
for (const NamedDecl *ND : getDeclAtPosition(AST, *CurLoc, Relations)) {
|
|
|
|
if (const auto *CXXMD = llvm::dyn_cast<CXXMethodDecl>(ND)) {
|
|
|
|
if (CXXMD->isVirtual()) {
|
|
|
|
IDs.insert(getSymbolID(ND));
|
|
|
|
QueryKind = RelationKind::OverriddenBy;
|
|
|
|
}
|
|
|
|
} else if (const auto *RD = dyn_cast<CXXRecordDecl>(ND)) {
|
|
|
|
IDs.insert(getSymbolID(RD));
|
|
|
|
QueryKind = RelationKind::BaseOf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return findImplementors(std::move(IDs), QueryKind, Index, *MainFilePath);
|
2020-11-19 01:13:11 +08:00
|
|
|
}
|
|
|
|
|
2019-11-18 18:35:00 +08:00
|
|
|
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
|
|
|
|
const SymbolIndex *Index) {
|
2019-01-15 02:11:09 +08:00
|
|
|
if (!Limit)
|
|
|
|
Limit = std::numeric_limits<uint32_t>::max();
|
2019-11-18 18:35:00 +08:00
|
|
|
ReferencesResult Results;
|
2019-05-29 05:52:34 +08:00
|
|
|
const SourceManager &SM = AST.getSourceManager();
|
2018-12-19 18:46:21 +08:00
|
|
|
auto MainFilePath =
|
|
|
|
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
|
2018-09-05 18:33:36 +08:00
|
|
|
if (!MainFilePath) {
|
|
|
|
elog("Failed to get a path for the main file, so no references");
|
|
|
|
return Results;
|
|
|
|
}
|
2020-01-08 20:57:00 +08:00
|
|
|
auto URIMainFile = URIForFile::canonicalize(*MainFilePath, *MainFilePath);
|
2020-02-26 20:39:46 +08:00
|
|
|
auto CurLoc = sourceLocationInMainFile(SM, Pos);
|
|
|
|
if (!CurLoc) {
|
|
|
|
llvm::consumeError(CurLoc.takeError());
|
|
|
|
return {};
|
|
|
|
}
|
2020-02-28 16:25:40 +08:00
|
|
|
llvm::Optional<DefinedMacro> Macro;
|
2020-02-26 20:39:46 +08:00
|
|
|
if (const auto *IdentifierAtCursor =
|
2020-02-28 16:25:40 +08:00
|
|
|
syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens())) {
|
|
|
|
Macro = locateMacroAt(*IdentifierAtCursor, AST.getPreprocessor());
|
|
|
|
}
|
2020-01-08 20:57:00 +08:00
|
|
|
|
2020-02-28 16:25:40 +08:00
|
|
|
RefsRequest Req;
|
|
|
|
if (Macro) {
|
2020-01-08 20:57:00 +08:00
|
|
|
// Handle references to macro.
|
|
|
|
if (auto MacroSID = getSymbolID(Macro->Name, Macro->Info, SM)) {
|
|
|
|
// Collect macro references from main file.
|
|
|
|
const auto &IDToRefs = AST.getMacros().MacroRefs;
|
2020-10-29 23:04:53 +08:00
|
|
|
auto Refs = IDToRefs.find(MacroSID);
|
2020-01-08 20:57:00 +08:00
|
|
|
if (Refs != IDToRefs.end()) {
|
2020-07-05 08:41:27 +08:00
|
|
|
for (const auto &Ref : Refs->second) {
|
2020-01-08 20:57:00 +08:00
|
|
|
Location Result;
|
|
|
|
Result.range = Ref;
|
|
|
|
Result.uri = URIMainFile;
|
|
|
|
Results.References.push_back(std::move(Result));
|
|
|
|
}
|
|
|
|
}
|
2020-10-29 23:04:53 +08:00
|
|
|
Req.IDs.insert(MacroSID);
|
2020-01-08 20:57:00 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Handle references to Decls.
|
|
|
|
|
2020-09-07 14:28:46 +08:00
|
|
|
DeclRelationSet Relations =
|
|
|
|
DeclRelation::TemplatePattern | DeclRelation::Alias;
|
|
|
|
std::vector<const NamedDecl *> Decls =
|
|
|
|
getDeclAtPosition(AST, *CurLoc, Relations);
|
2020-01-08 20:57:00 +08:00
|
|
|
|
|
|
|
// We traverse the AST to find references in the main file.
|
|
|
|
auto MainFileRefs = findRefs(Decls, AST);
|
|
|
|
// We may get multiple refs with the same location and different Roles, as
|
|
|
|
// cross-reference is only interested in locations, we deduplicate them
|
|
|
|
// by the location to avoid emitting duplicated locations.
|
|
|
|
MainFileRefs.erase(std::unique(MainFileRefs.begin(), MainFileRefs.end(),
|
|
|
|
[](const ReferenceFinder::Reference &L,
|
|
|
|
const ReferenceFinder::Reference &R) {
|
2020-03-01 23:05:12 +08:00
|
|
|
return L.SpelledTok.location() ==
|
|
|
|
R.SpelledTok.location();
|
2020-01-08 20:57:00 +08:00
|
|
|
}),
|
|
|
|
MainFileRefs.end());
|
|
|
|
for (const auto &Ref : MainFileRefs) {
|
2020-03-01 23:05:12 +08:00
|
|
|
Location Result;
|
|
|
|
Result.range = Ref.range(SM);
|
|
|
|
Result.uri = URIMainFile;
|
|
|
|
Results.References.push_back(std::move(Result));
|
2020-01-08 20:57:00 +08:00
|
|
|
}
|
|
|
|
if (Index && Results.References.size() <= Limit) {
|
|
|
|
for (const Decl *D : Decls) {
|
|
|
|
// Not all symbols can be referenced from outside (e.g.
|
|
|
|
// function-locals).
|
|
|
|
// TODO: we could skip TU-scoped symbols here (e.g. static functions) if
|
|
|
|
// we know this file isn't a header. The details might be tricky.
|
|
|
|
if (D->getParentFunctionOrMethod())
|
|
|
|
continue;
|
|
|
|
if (auto ID = getSymbolID(D))
|
2020-10-29 23:04:53 +08:00
|
|
|
Req.IDs.insert(ID);
|
2020-01-08 20:57:00 +08:00
|
|
|
}
|
2019-06-25 16:01:46 +08:00
|
|
|
}
|
2018-09-05 18:33:36 +08:00
|
|
|
}
|
|
|
|
// Now query the index for references from other files.
|
2020-01-08 20:57:00 +08:00
|
|
|
if (!Req.IDs.empty() && Index && Results.References.size() <= Limit) {
|
2019-01-15 02:11:09 +08:00
|
|
|
Req.Limit = Limit;
|
2019-11-18 18:35:00 +08:00
|
|
|
Results.HasMore |= Index->refs(Req, [&](const Ref &R) {
|
2020-01-08 20:57:00 +08:00
|
|
|
// No need to continue process if we reach the limit.
|
2019-11-18 18:35:00 +08:00
|
|
|
if (Results.References.size() > Limit)
|
|
|
|
return;
|
2019-01-15 02:11:09 +08:00
|
|
|
auto LSPLoc = toLSPLocation(R.Location, *MainFilePath);
|
|
|
|
// Avoid indexed results for the main file - the AST is authoritative.
|
2019-11-18 18:35:00 +08:00
|
|
|
if (!LSPLoc || LSPLoc->uri.file() == *MainFilePath)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Results.References.push_back(std::move(*LSPLoc));
|
2019-01-15 02:11:09 +08:00
|
|
|
});
|
2018-09-05 18:33:36 +08:00
|
|
|
}
|
2019-11-18 18:35:00 +08:00
|
|
|
if (Results.References.size() > Limit) {
|
|
|
|
Results.HasMore = true;
|
|
|
|
Results.References.resize(Limit);
|
|
|
|
}
|
2018-09-05 18:33:36 +08:00
|
|
|
return Results;
|
|
|
|
}
|
|
|
|
|
2018-11-28 00:40:46 +08:00
|
|
|
std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
|
2019-05-29 05:52:34 +08:00
|
|
|
const SourceManager &SM = AST.getSourceManager();
|
2020-02-26 20:39:46 +08:00
|
|
|
auto CurLoc = sourceLocationInMainFile(SM, Pos);
|
|
|
|
if (!CurLoc) {
|
|
|
|
llvm::consumeError(CurLoc.takeError());
|
|
|
|
return {};
|
|
|
|
}
|
2018-11-28 00:40:46 +08:00
|
|
|
|
|
|
|
std::vector<SymbolDetails> Results;
|
|
|
|
|
2019-10-18 06:48:39 +08:00
|
|
|
// We also want the targets of using-decls, so we include
|
|
|
|
// DeclRelation::Underlying.
|
|
|
|
DeclRelationSet Relations = DeclRelation::TemplatePattern |
|
|
|
|
DeclRelation::Alias | DeclRelation::Underlying;
|
2020-02-26 20:39:46 +08:00
|
|
|
for (const NamedDecl *D : getDeclAtPosition(AST, *CurLoc, Relations)) {
|
2018-11-28 00:40:46 +08:00
|
|
|
SymbolDetails NewSymbol;
|
[clangd] targetDecl() returns only NamedDecls.
Summary:
While it's perfectly reasonable for non-named decls such as
static_assert to resolve to themselves:
- nothing else ever resolves to them
- features based on references (hover, highlight, find refs etc) tend
to be uninteresting where only trivial references are possible
- returning NamedDecl is a more convenient API (we cast to it in many places)
- this aligns closer to findExplicitReferences/explicitReferenceTargets
This fixes a crash in explicitReferenceTargets: if the target is a
non-named decl then there's an invalid unchecked cast to NamedDecl.
In practice this means when hovering over e.g. a static_assert:
- before ac3f9e4842, we would show a (boring) hover card
- after ac3f9e4842, we would crash
- after this patch, we will show nothing
Reviewers: kadircet, ilya-biryukov
Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72163
2020-01-04 00:26:33 +08:00
|
|
|
std::string QName = printQualifiedName(*D);
|
2020-01-29 03:23:46 +08:00
|
|
|
auto SplitQName = splitQualifiedName(QName);
|
|
|
|
NewSymbol.containerName = std::string(SplitQName.first);
|
|
|
|
NewSymbol.name = std::string(SplitQName.second);
|
[clangd] targetDecl() returns only NamedDecls.
Summary:
While it's perfectly reasonable for non-named decls such as
static_assert to resolve to themselves:
- nothing else ever resolves to them
- features based on references (hover, highlight, find refs etc) tend
to be uninteresting where only trivial references are possible
- returning NamedDecl is a more convenient API (we cast to it in many places)
- this aligns closer to findExplicitReferences/explicitReferenceTargets
This fixes a crash in explicitReferenceTargets: if the target is a
non-named decl then there's an invalid unchecked cast to NamedDecl.
In practice this means when hovering over e.g. a static_assert:
- before ac3f9e4842, we would show a (boring) hover card
- after ac3f9e4842, we would crash
- after this patch, we will show nothing
Reviewers: kadircet, ilya-biryukov
Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72163
2020-01-04 00:26:33 +08:00
|
|
|
|
|
|
|
if (NewSymbol.containerName.empty()) {
|
|
|
|
if (const auto *ParentND =
|
|
|
|
dyn_cast_or_null<NamedDecl>(D->getDeclContext()))
|
|
|
|
NewSymbol.containerName = printQualifiedName(*ParentND);
|
2018-11-28 00:40:46 +08:00
|
|
|
}
|
|
|
|
llvm::SmallString<32> USR;
|
2019-02-21 22:48:33 +08:00
|
|
|
if (!index::generateUSRForDecl(D, USR)) {
|
2020-01-29 03:23:46 +08:00
|
|
|
NewSymbol.USR = std::string(USR.str());
|
2018-11-28 00:40:46 +08:00
|
|
|
NewSymbol.ID = SymbolID(NewSymbol.USR);
|
|
|
|
}
|
|
|
|
Results.push_back(std::move(NewSymbol));
|
|
|
|
}
|
|
|
|
|
2020-02-26 20:39:46 +08:00
|
|
|
const auto *IdentifierAtCursor =
|
|
|
|
syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens());
|
|
|
|
if (!IdentifierAtCursor)
|
|
|
|
return Results;
|
|
|
|
|
2020-02-28 16:25:40 +08:00
|
|
|
if (auto M = locateMacroAt(*IdentifierAtCursor, AST.getPreprocessor())) {
|
2018-11-28 00:40:46 +08:00
|
|
|
SymbolDetails NewMacro;
|
2020-01-29 03:23:46 +08:00
|
|
|
NewMacro.name = std::string(M->Name);
|
2018-11-28 00:40:46 +08:00
|
|
|
llvm::SmallString<32> USR;
|
2019-09-03 22:12:48 +08:00
|
|
|
if (!index::generateUSRForMacro(NewMacro.name, M->Info->getDefinitionLoc(),
|
|
|
|
SM, USR)) {
|
2020-01-29 03:23:46 +08:00
|
|
|
NewMacro.USR = std::string(USR.str());
|
2018-11-28 00:40:46 +08:00
|
|
|
NewMacro.ID = SymbolID(NewMacro.USR);
|
|
|
|
}
|
|
|
|
Results.push_back(std::move(NewMacro));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Results;
|
|
|
|
}
|
|
|
|
|
[clangd] Implement textDocument/declaration from LSP 3.14
Summary:
LSP now reflects the declaration/definition distinction.
Language server changes:
- textDocument/definition now returns a definition if one is found, otherwise
the declaration. It no longer returns declaration + definition if they are
distinct.
- textDocument/declaration returns the best declaration we can find.
- For macros, the active macro definition is returned for both methods.
- For include directive, the top of the target file is returned for both.
There doesn't appear to be a discovery mechanism (we can't return everything to
clients that only know about definition), so this changes existing behavior.
In practice, it should greatly reduce the fraction of the time we need to show
the user a menu of options.
C++ API changes:
- findDefinitions is replaced by locateSymbolAt, which returns a
vector<LocatedSymbol> - one for each symbol under the cursor.
- this contains the preferred declaration, the definition (if found), and
the symbol name
This API enables some potentially-neat extensions, like swapping between decl
and def, and exposing the symbol name to the UI in the case of multiple symbols.
Reviewers: hokein
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D57388
llvm-svn: 352864
2019-02-01 19:26:13 +08:00
|
|
|
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LocatedSymbol &S) {
|
|
|
|
OS << S.Name << ": " << S.PreferredDeclaration;
|
|
|
|
if (S.Definition)
|
|
|
|
OS << " def=" << *S.Definition;
|
|
|
|
return OS;
|
|
|
|
}
|
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
template <typename HierarchyItem>
|
|
|
|
static llvm::Optional<HierarchyItem> declToHierarchyItem(const NamedDecl &ND) {
|
|
|
|
ASTContext &Ctx = ND.getASTContext();
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
auto &SM = Ctx.getSourceManager();
|
2019-12-10 17:08:39 +08:00
|
|
|
SourceLocation NameLoc = nameLocation(ND, Ctx.getSourceManager());
|
2020-07-20 19:46:09 +08:00
|
|
|
SourceLocation BeginLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getBeginLoc()));
|
|
|
|
SourceLocation EndLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getEndLoc()));
|
|
|
|
const auto DeclRange =
|
|
|
|
toHalfOpenFileRange(SM, Ctx.getLangOpts(), {BeginLoc, EndLoc});
|
|
|
|
if (!DeclRange)
|
|
|
|
return llvm::None;
|
2020-02-20 02:11:01 +08:00
|
|
|
auto FilePath =
|
|
|
|
getCanonicalPath(SM.getFileEntryForID(SM.getFileID(NameLoc)), SM);
|
|
|
|
auto TUPath = getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
|
|
|
|
if (!FilePath || !TUPath)
|
|
|
|
return llvm::None; // Not useful without a uri.
|
|
|
|
|
2020-07-20 19:46:09 +08:00
|
|
|
Position NameBegin = sourceLocToPosition(SM, NameLoc);
|
|
|
|
Position NameEnd = sourceLocToPosition(
|
|
|
|
SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
|
|
|
|
index::SymbolInfo SymInfo = index::getSymbolInfo(&ND);
|
2020-10-26 22:31:29 +08:00
|
|
|
// FIXME: This is not classifying constructors, destructors and operators
|
|
|
|
// correctly.
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
|
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
HierarchyItem HI;
|
|
|
|
HI.name = printName(Ctx, ND);
|
|
|
|
HI.kind = SK;
|
|
|
|
HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
|
|
|
|
sourceLocToPosition(SM, DeclRange->getEnd())};
|
|
|
|
HI.selectionRange = Range{NameBegin, NameEnd};
|
|
|
|
if (!HI.range.contains(HI.selectionRange)) {
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
// 'selectionRange' must be contained in 'range', so in cases where clang
|
|
|
|
// reports unrelated ranges we need to reconcile somehow.
|
2020-11-16 11:59:10 +08:00
|
|
|
HI.range = HI.selectionRange;
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
}
|
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
HI.uri = URIForFile::canonicalize(*FilePath, *TUPath);
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
|
[clangd] Support typeHierarchy/resolve for children of parents as well
Summary:
The initial implementation of typeHierarchy/resolve only supported
cases where an initial request was made for children, and then
typeHierarchy/resolve was used to get additional levels of children.
However, a client may also want to make an initial request for
parents, and then show other children of those parents, so support
typeHierarchy/resolve for items returned in response to a request
for parents as well.
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D81845
2020-06-15 08:17:15 +08:00
|
|
|
// Compute the SymbolID and store it in the 'data' field.
|
|
|
|
// This allows typeHierarchy/resolve to be used to
|
|
|
|
// resolve children of items returned in a previous request
|
|
|
|
// for parents.
|
2020-10-29 23:04:53 +08:00
|
|
|
if (auto ID = getSymbolID(&ND))
|
2020-11-16 11:59:10 +08:00
|
|
|
HI.data = ID.str();
|
|
|
|
|
|
|
|
return HI;
|
|
|
|
}
|
[clangd] Support typeHierarchy/resolve for children of parents as well
Summary:
The initial implementation of typeHierarchy/resolve only supported
cases where an initial request was made for children, and then
typeHierarchy/resolve was used to get additional levels of children.
However, a client may also want to make an initial request for
parents, and then show other children of those parents, so support
typeHierarchy/resolve for items returned in response to a request
for parents as well.
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D81845
2020-06-15 08:17:15 +08:00
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
static llvm::Optional<TypeHierarchyItem>
|
|
|
|
declToTypeHierarchyItem(const NamedDecl &ND) {
|
|
|
|
auto Result = declToHierarchyItem<TypeHierarchyItem>(ND);
|
|
|
|
if (Result)
|
|
|
|
Result->deprecated = ND.isDeprecated();
|
|
|
|
return Result;
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
}
|
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
static llvm::Optional<CallHierarchyItem>
|
|
|
|
declToCallHierarchyItem(const NamedDecl &ND) {
|
|
|
|
auto Result = declToHierarchyItem<CallHierarchyItem>(ND);
|
|
|
|
if (Result && ND.isDeprecated())
|
|
|
|
Result->tags.push_back(SymbolTag::Deprecated);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename HierarchyItem>
|
|
|
|
static llvm::Optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
|
|
|
|
PathRef TUPath) {
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
auto Loc = symbolToLocation(S, TUPath);
|
|
|
|
if (!Loc) {
|
2020-11-16 11:59:10 +08:00
|
|
|
elog("Failed to convert symbol to hierarchy item: {0}", Loc.takeError());
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
return llvm::None;
|
|
|
|
}
|
2020-11-16 11:59:10 +08:00
|
|
|
HierarchyItem HI;
|
|
|
|
HI.name = std::string(S.Name);
|
|
|
|
HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
|
|
|
|
HI.selectionRange = Loc->range;
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
// FIXME: Populate 'range' correctly
|
|
|
|
// (https://github.com/clangd/clangd/issues/59).
|
2020-11-16 11:59:10 +08:00
|
|
|
HI.range = HI.selectionRange;
|
|
|
|
HI.uri = Loc->uri;
|
2019-07-13 11:24:48 +08:00
|
|
|
// Store the SymbolID in the 'data' field. The client will
|
2020-11-16 11:59:10 +08:00
|
|
|
// send this back in requests to resolve additional levels
|
|
|
|
// of the hierarchy.
|
|
|
|
HI.data = S.ID.str();
|
|
|
|
|
|
|
|
return HI;
|
|
|
|
}
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
static llvm::Optional<TypeHierarchyItem>
|
|
|
|
symbolToTypeHierarchyItem(const Symbol &S, PathRef TUPath) {
|
|
|
|
auto Result = symbolToHierarchyItem<TypeHierarchyItem>(S, TUPath);
|
|
|
|
if (Result)
|
|
|
|
Result->deprecated = (S.Flags & Symbol::Deprecated);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static llvm::Optional<CallHierarchyItem>
|
|
|
|
symbolToCallHierarchyItem(const Symbol &S, PathRef TUPath) {
|
|
|
|
auto Result = symbolToHierarchyItem<CallHierarchyItem>(S, TUPath);
|
|
|
|
if (Result && (S.Flags & Symbol::Deprecated))
|
|
|
|
Result->tags.push_back(SymbolTag::Deprecated);
|
|
|
|
return Result;
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fillSubTypes(const SymbolID &ID,
|
|
|
|
std::vector<TypeHierarchyItem> &SubTypes,
|
|
|
|
const SymbolIndex *Index, int Levels, PathRef TUPath) {
|
|
|
|
RelationsRequest Req;
|
|
|
|
Req.Subjects.insert(ID);
|
2019-10-17 22:08:28 +08:00
|
|
|
Req.Predicate = RelationKind::BaseOf;
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
|
|
|
|
if (Optional<TypeHierarchyItem> ChildSym =
|
2020-11-16 11:59:10 +08:00
|
|
|
symbolToTypeHierarchyItem(Object, TUPath)) {
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
if (Levels > 1) {
|
|
|
|
ChildSym->children.emplace();
|
|
|
|
fillSubTypes(Object.ID, *ChildSym->children, Index, Levels - 1, TUPath);
|
|
|
|
}
|
|
|
|
SubTypes.emplace_back(std::move(*ChildSym));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-22 09:38:53 +08:00
|
|
|
using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
|
|
|
|
|
2019-07-17 23:26:49 +08:00
|
|
|
static void fillSuperTypes(const CXXRecordDecl &CXXRD, ASTContext &ASTCtx,
|
|
|
|
std::vector<TypeHierarchyItem> &SuperTypes,
|
2020-07-20 19:46:09 +08:00
|
|
|
RecursionProtectionSet &RPSet) {
|
2019-04-22 09:38:53 +08:00
|
|
|
// typeParents() will replace dependent template specializations
|
|
|
|
// with their class template, so to avoid infinite recursion for
|
|
|
|
// certain types of hierarchies, keep the templates encountered
|
|
|
|
// along the parent chain in a set, and stop the recursion if one
|
|
|
|
// starts to repeat.
|
|
|
|
auto *Pattern = CXXRD.getDescribedTemplate() ? &CXXRD : nullptr;
|
|
|
|
if (Pattern) {
|
|
|
|
if (!RPSet.insert(Pattern).second) {
|
2019-07-17 23:26:49 +08:00
|
|
|
return;
|
2019-04-22 09:38:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) {
|
|
|
|
if (Optional<TypeHierarchyItem> ParentSym =
|
2020-11-16 11:59:10 +08:00
|
|
|
declToTypeHierarchyItem(*ParentDecl)) {
|
2019-07-17 23:26:49 +08:00
|
|
|
ParentSym->parents.emplace();
|
2020-07-20 19:46:09 +08:00
|
|
|
fillSuperTypes(*ParentDecl, ASTCtx, *ParentSym->parents, RPSet);
|
2019-07-17 23:26:49 +08:00
|
|
|
SuperTypes.emplace_back(std::move(*ParentSym));
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-22 09:38:53 +08:00
|
|
|
if (Pattern) {
|
|
|
|
RPSet.erase(Pattern);
|
|
|
|
}
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
|
2020-02-24 03:03:00 +08:00
|
|
|
auto RecordFromNode =
|
|
|
|
[](const SelectionTree::Node *N) -> const CXXRecordDecl * {
|
|
|
|
if (!N)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Note: explicitReferenceTargets() will search for both template
|
|
|
|
// instantiations and template patterns, and prefer the former if available
|
|
|
|
// (generally, one will be available for non-dependent specializations of a
|
|
|
|
// class template).
|
|
|
|
auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying);
|
|
|
|
if (Decls.empty())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
const NamedDecl *D = Decls[0];
|
|
|
|
|
|
|
|
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
|
|
|
// If this is a variable, use the type of the variable.
|
|
|
|
return VD->getType().getTypePtr()->getAsCXXRecordDecl();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
|
|
|
|
// If this is a method, use the type of the class.
|
|
|
|
return Method->getParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't handle FieldDecl because it's not clear what behaviour
|
|
|
|
// the user would expect: the enclosing class type (as with a
|
|
|
|
// method), or the field's type (as with a variable).
|
|
|
|
|
|
|
|
return dyn_cast<CXXRecordDecl>(D);
|
|
|
|
};
|
|
|
|
|
2019-09-04 18:15:27 +08:00
|
|
|
const SourceManager &SM = AST.getSourceManager();
|
2020-02-24 03:03:00 +08:00
|
|
|
const CXXRecordDecl *Result = nullptr;
|
2020-02-26 20:39:46 +08:00
|
|
|
auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
|
|
|
|
if (!Offset) {
|
|
|
|
llvm::consumeError(Offset.takeError());
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
|
|
|
|
*Offset, [&](SelectionTree ST) {
|
2020-02-24 03:03:00 +08:00
|
|
|
Result = RecordFromNode(ST.commonAncestor());
|
|
|
|
return Result != nullptr;
|
|
|
|
});
|
|
|
|
return Result;
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD) {
|
|
|
|
std::vector<const CXXRecordDecl *> Result;
|
|
|
|
|
2019-12-20 05:22:23 +08:00
|
|
|
// If this is an invalid instantiation, instantiation of the bases
|
|
|
|
// may not have succeeded, so fall back to the template pattern.
|
|
|
|
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
|
|
|
|
if (CTSD->isInvalidDecl())
|
|
|
|
CXXRD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
|
|
|
|
}
|
|
|
|
|
2020-11-25 16:11:54 +08:00
|
|
|
// Can't query bases without a definition.
|
|
|
|
if (!CXXRD->hasDefinition())
|
|
|
|
return Result;
|
|
|
|
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
for (auto Base : CXXRD->bases()) {
|
|
|
|
const CXXRecordDecl *ParentDecl = nullptr;
|
|
|
|
|
|
|
|
const Type *Type = Base.getType().getTypePtr();
|
|
|
|
if (const RecordType *RT = Type->getAs<RecordType>()) {
|
|
|
|
ParentDecl = RT->getAsCXXRecordDecl();
|
|
|
|
}
|
|
|
|
|
2019-04-22 09:38:53 +08:00
|
|
|
if (!ParentDecl) {
|
|
|
|
// Handle a dependent base such as "Base<T>" by using the primary
|
|
|
|
// template.
|
|
|
|
if (const TemplateSpecializationType *TS =
|
|
|
|
Type->getAs<TemplateSpecializationType>()) {
|
|
|
|
TemplateName TN = TS->getTemplateName();
|
|
|
|
if (TemplateDecl *TD = TN.getAsTemplateDecl()) {
|
|
|
|
ParentDecl = dyn_cast<CXXRecordDecl>(TD->getTemplatedDecl());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
|
|
|
|
if (ParentDecl)
|
|
|
|
Result.push_back(ParentDecl);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Optional<TypeHierarchyItem>
|
|
|
|
getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
TypeHierarchyDirection Direction, const SymbolIndex *Index,
|
|
|
|
PathRef TUPath) {
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
const CXXRecordDecl *CXXRD = findRecordTypeAt(AST, Pos);
|
|
|
|
if (!CXXRD)
|
|
|
|
return llvm::None;
|
|
|
|
|
2020-08-31 15:00:58 +08:00
|
|
|
bool WantParents = Direction == TypeHierarchyDirection::Parents ||
|
|
|
|
Direction == TypeHierarchyDirection::Both;
|
|
|
|
bool WantChildren = Direction == TypeHierarchyDirection::Children ||
|
|
|
|
Direction == TypeHierarchyDirection::Both;
|
|
|
|
|
|
|
|
// If we're looking for children, we're doing the lookup in the index.
|
|
|
|
// The index does not store relationships between implicit
|
|
|
|
// specializations, so if we have one, use the template pattern instead.
|
|
|
|
// Note that this needs to be done before the declToTypeHierarchyItem(),
|
|
|
|
// otherwise the type hierarchy item would misleadingly contain the
|
|
|
|
// specialization parameters, while the children would involve classes
|
|
|
|
// that derive from other specializations of the template.
|
|
|
|
if (WantChildren) {
|
|
|
|
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
|
|
|
|
CXXRD = CTSD->getTemplateInstantiationPattern();
|
|
|
|
}
|
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
Optional<TypeHierarchyItem> Result = declToTypeHierarchyItem(*CXXRD);
|
2019-07-12 08:24:45 +08:00
|
|
|
if (!Result)
|
|
|
|
return Result;
|
2019-04-22 09:38:53 +08:00
|
|
|
|
2020-08-31 15:00:58 +08:00
|
|
|
if (WantParents) {
|
2019-07-17 23:26:49 +08:00
|
|
|
Result->parents.emplace();
|
|
|
|
|
|
|
|
RecursionProtectionSet RPSet;
|
2020-07-20 19:46:09 +08:00
|
|
|
fillSuperTypes(*CXXRD, AST.getASTContext(), *Result->parents, RPSet);
|
2019-07-17 23:26:49 +08:00
|
|
|
}
|
|
|
|
|
2020-08-31 15:00:58 +08:00
|
|
|
if (WantChildren && ResolveLevels > 0) {
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
Result->children.emplace();
|
|
|
|
|
|
|
|
if (Index) {
|
2020-10-29 23:04:53 +08:00
|
|
|
if (auto ID = getSymbolID(CXXRD))
|
|
|
|
fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
|
[clangd] Type hierarchy subtypes
Summary:
This builds on the relations support added in D59407, D62459, D62471,
and D62839 to implement type hierarchy subtypes.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman,
jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58880
llvm-svn: 363506
2019-06-16 10:31:37 +08:00
|
|
|
}
|
|
|
|
}
|
2019-04-22 09:38:53 +08:00
|
|
|
|
[clangd] Add support for type hierarchy (super types only for now)
Summary:
Patch by Nathan Ridge(@nridge)!
This is an LSP extension proposed here:
https://github.com/Microsoft/vscode-languageserver-node/pull/426
An example client implementation can be found here:
https://github.com/theia-ide/theia/pull/3802
Reviewers: kadircet, sammccall
Reviewed By: kadircet
Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet
Tags: #clang
Differential Revision: https://reviews.llvm.org/D56370
llvm-svn: 356445
2019-03-19 17:27:04 +08:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2019-07-13 11:24:48 +08:00
|
|
|
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
|
|
|
|
TypeHierarchyDirection Direction,
|
|
|
|
const SymbolIndex *Index) {
|
|
|
|
// We only support typeHierarchy/resolve for children, because for parents
|
|
|
|
// we ignore ResolveLevels and return all levels of parents eagerly.
|
|
|
|
if (Direction == TypeHierarchyDirection::Parents || ResolveLevels == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Item.children.emplace();
|
|
|
|
|
|
|
|
if (Index && Item.data) {
|
|
|
|
// We store the item's SymbolID in the 'data' field, and the client
|
|
|
|
// passes it back to us in typeHierarchy/resolve.
|
|
|
|
if (Expected<SymbolID> ID = SymbolID::fromStr(*Item.data)) {
|
|
|
|
fillSubTypes(*ID, *Item.children, Index, ResolveLevels, Item.uri.file());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-16 11:59:10 +08:00
|
|
|
std::vector<CallHierarchyItem>
|
|
|
|
prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) {
|
|
|
|
std::vector<CallHierarchyItem> Result;
|
|
|
|
const auto &SM = AST.getSourceManager();
|
|
|
|
auto Loc = sourceLocationInMainFile(SM, Pos);
|
|
|
|
if (!Loc) {
|
|
|
|
elog("prepareCallHierarchy failed to convert position to source location: "
|
|
|
|
"{0}",
|
|
|
|
Loc.takeError());
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
for (const NamedDecl *Decl : getDeclAtPosition(AST, *Loc, {})) {
|
|
|
|
if (!Decl->isFunctionOrFunctionTemplate())
|
|
|
|
continue;
|
|
|
|
if (auto CHI = declToCallHierarchyItem(*Decl))
|
|
|
|
Result.emplace_back(std::move(*CHI));
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<CallHierarchyIncomingCall>
|
|
|
|
incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
|
|
|
|
std::vector<CallHierarchyIncomingCall> Results;
|
|
|
|
if (!Index || Item.data.empty())
|
|
|
|
return Results;
|
|
|
|
auto ID = SymbolID::fromStr(Item.data);
|
|
|
|
if (!ID) {
|
|
|
|
elog("incomingCalls failed to find symbol: {0}", ID.takeError());
|
|
|
|
return Results;
|
|
|
|
}
|
|
|
|
// In this function, we find incoming calls based on the index only.
|
|
|
|
// In principle, the AST could have more up-to-date information about
|
|
|
|
// occurrences within the current file. However, going from a SymbolID
|
|
|
|
// to an AST node isn't cheap, particularly when the declaration isn't
|
|
|
|
// in the main file.
|
|
|
|
// FIXME: Consider also using AST information when feasible.
|
|
|
|
RefsRequest Request;
|
|
|
|
Request.IDs.insert(*ID);
|
|
|
|
// We could restrict more specifically to calls by introducing a new RefKind,
|
|
|
|
// but non-call references (such as address-of-function) can still be
|
|
|
|
// interesting as they can indicate indirect calls.
|
|
|
|
Request.Filter = RefKind::Reference;
|
|
|
|
// Initially store the ranges in a map keyed by SymbolID of the caller.
|
|
|
|
// This allows us to group different calls with the same caller
|
|
|
|
// into the same CallHierarchyIncomingCall.
|
|
|
|
llvm::DenseMap<SymbolID, std::vector<Range>> CallsIn;
|
|
|
|
// We can populate the ranges based on a refs request only. As we do so, we
|
|
|
|
// also accumulate the container IDs into a lookup request.
|
|
|
|
LookupRequest ContainerLookup;
|
|
|
|
Index->refs(Request, [&](const Ref &R) {
|
|
|
|
auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
|
|
|
|
if (!Loc) {
|
|
|
|
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto It = CallsIn.try_emplace(R.Container, std::vector<Range>{}).first;
|
|
|
|
It->second.push_back(Loc->range);
|
|
|
|
|
|
|
|
ContainerLookup.IDs.insert(R.Container);
|
|
|
|
});
|
|
|
|
// Perform the lookup request and combine its results with CallsIn to
|
|
|
|
// get complete CallHierarchyIncomingCall objects.
|
|
|
|
Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
|
|
|
|
auto It = CallsIn.find(Caller.ID);
|
|
|
|
assert(It != CallsIn.end());
|
|
|
|
if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file()))
|
|
|
|
Results.push_back(
|
|
|
|
CallHierarchyIncomingCall{std::move(*CHI), std::move(It->second)});
|
|
|
|
});
|
2020-11-24 16:17:44 +08:00
|
|
|
// Sort results by name of container.
|
|
|
|
llvm::sort(Results, [](const CallHierarchyIncomingCall &A,
|
|
|
|
const CallHierarchyIncomingCall &B) {
|
|
|
|
return A.from.name < B.from.name;
|
|
|
|
});
|
2020-11-16 11:59:10 +08:00
|
|
|
return Results;
|
|
|
|
}
|
|
|
|
|
2019-09-26 15:27:43 +08:00
|
|
|
llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
|
|
|
|
const FunctionDecl *FD) {
|
|
|
|
if (!FD->hasBody())
|
|
|
|
return {};
|
|
|
|
llvm::DenseSet<const Decl *> DeclRefs;
|
|
|
|
findExplicitReferences(FD, [&](ReferenceLoc Ref) {
|
|
|
|
for (const Decl *D : Ref.Targets) {
|
2019-10-18 20:07:19 +08:00
|
|
|
if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() &&
|
|
|
|
!Ref.IsDecl)
|
2019-09-26 15:27:43 +08:00
|
|
|
DeclRefs.insert(D);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return DeclRefs;
|
|
|
|
}
|
2017-12-20 01:06:07 +08:00
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|