Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions docs/en/features/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Go to Definition

- [x] Index-based cross-TU go-to-definition
- [ ] Go to definition on `#include` directives (navigate to the included file)
- [x] Go to definition on `#include` directives (navigate to the included file), including preamble includes compiled into the PCH
- [ ] AST-based fallback for local/unsaved symbols
- [ ] Navigate through macro wrappers to the underlying declaration

Expand Down Expand Up @@ -533,7 +533,7 @@ Navigate to the type definition of a symbol. Applicable to variables, parameters

## Module Navigation

- [ ] `import module_name` → jump to module interface unit ([clangd#2310](https://github.com/clangd/clangd/issues/2310))
- [x] `import module_name` → jump to module interface unit ([clangd#2310](https://github.com/clangd/clangd/issues/2310))

```cpp
import mylib; // go-to-def on mylib → module interface unit (export module mylib;)
Expand All @@ -545,7 +545,7 @@ Navigate to the type definition of a symbol. Applicable to variables, parameters
import :core; // go-to-def on core → partition unit (export module mylib:core;)
```

- [ ] Navigate between interface and implementation units of the same module
- [ ] Navigate between interface and implementation units of the same module (implementation → interface works via go-to-def on the `module m;` name; the reverse is not implemented)

```cpp
// interface unit: export module mylib;
Expand Down Expand Up @@ -586,6 +586,7 @@ Highlight all references to the symbol under cursor within the current file (`te

| Date | Change | PR |
| ---- | -------------------------------------------------------------------------------------------------- | --- |
| — | go-to-definition on include directives and module names | — |
| — | declaration / implementation / typeDefinition; references includeDeclaration includes declarations | — |
| — | Index-based go-to-definition and find references | — |
| — | Call hierarchy (incoming/outgoing) | — |
Expand Down
4 changes: 4 additions & 0 deletions src/compile/compilation_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ auto CompilationUnitRef::token_spelling(clang::SourceLocation location) -> llvm:
return llvm::StringRef(self->SM().getCharacterData(location), token_length(location));
}

bool CompilationUnitRef::is_named_module() {
return self->instance->getPreprocessor().isInNamedModule();
}

auto CompilationUnitRef::module_name() -> llvm::StringRef {
return self->instance->getPreprocessor().getNamedModuleName();
}
Expand Down
5 changes: 5 additions & 0 deletions src/compile/compilation_unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ class CompilationUnitRef {
auto token_spelling(clang::SourceLocation location) -> llvm::StringRef;

/// Get the C++20 named module name if any.
/// Whether this unit is a named module (interface or implementation).
/// Must be checked before module_name(): the preprocessor asserts on
/// name access for non-module units.
bool is_named_module();

auto module_name() -> llvm::StringRef;

/// Return whether this unit it module interface unit.
Expand Down
7 changes: 6 additions & 1 deletion src/compile/directive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "compile/implement.h"

#include "clang/Basic/Module.h"
#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
Expand Down Expand Up @@ -143,14 +144,18 @@ class DirectiveCollector : public clang::PPCallbacks {

void moduleImport(clang::SourceLocation import_location,
clang::ModuleIdPath names,
const clang::Module*) override {
const clang::Module* M) override {
auto fid = unit.file_id(unit.expansion_location(import_location));
auto& import = unit->directives[fid].imports.emplace_back();
import.location = import_location;
for(auto name: names) {
if(!import.name.empty())
import.name += '.';
import.name += name.getIdentifierInfo()->getName();
import.name_locations.emplace_back(name.getLoc());
}

import.full_name = M ? M->getFullModuleName() : import.name;
}

void HasInclude(clang::SourceLocation location,
Expand Down
4 changes: 4 additions & 0 deletions src/compile/directive.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ struct Import {
/// The name of imported module.
std::string name;

/// Resolved full module name (includes the enclosing module for
/// partition imports); empty when clang could not resolve the module.
std::string full_name;

/// The location of import keyword, may comes from macro expansion.
clang::SourceLocation location;

Expand Down
14 changes: 0 additions & 14 deletions src/feature/diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,6 @@ namespace clice::feature {

namespace {

auto to_uri(llvm::StringRef file) -> std::string {
const auto file_view = std::string_view(file.data(), file.size());

if(auto parsed = lsp::URI::parse(file_view)) {
return parsed->str();
}

if(auto uri = lsp::URI::from_file_path(file_view)) {
return uri->str();
}

return file.str();
}

void add_tag(protocol::Diagnostic& diagnostic, DiagnosticID id) {
if(id.is_deprecated()) {
if(!diagnostic.tags.has_value()) {
Expand Down
18 changes: 18 additions & 0 deletions src/feature/document_link.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <string>

#include "kota/ipc/lsp/protocol.h"

namespace clice::feature {

/// A resolved document link: the argument range of an include-like
/// directive and the absolute path of the target file. Plain data — it
/// serializes over the worker RPC as-is and becomes an LSP DocumentLink
/// only at the reply edge.
struct DocumentLink {
kota::ipc::protocol::Range range;
std::string target;
};

} // namespace clice::feature
48 changes: 43 additions & 5 deletions src/feature/document_links.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
namespace clice::feature {

auto document_links(CompilationUnitRef unit, PositionEncoding encoding)
-> std::vector<protocol::DocumentLink> {
std::vector<protocol::DocumentLink> links;
-> std::vector<DocumentLink> {
std::vector<DocumentLink> links;

auto interested = unit.interested_file();
auto directives_it = unit.directives().find(interested);
Expand All @@ -32,9 +32,7 @@ auto document_links(CompilationUnitRef unit, PositionEncoding encoding)
auto protocol_range = to_range(map, *range);
if(!protocol_range)
return;
protocol::DocumentLink link{.range = *protocol_range};
link.target = target.str();
links.push_back(std::move(link));
links.push_back(DocumentLink{.range = *protocol_range, .target = target.str()});
};

for(const auto& include: directives.includes) {
Expand Down Expand Up @@ -64,4 +62,44 @@ auto document_links(CompilationUnitRef unit, PositionEncoding encoding)
return links;
}

auto include_definition(CompilationUnitRef unit, std::uint32_t offset)
-> std::vector<protocol::Location> {
std::vector<protocol::Location> locations;

auto interested = unit.interested_file();
auto directives_it = unit.directives().find(interested);
if(directives_it == unit.directives().end()) {
return locations;
}

auto content = unit.interested_content();
auto* lang_opts = &unit.lang_options();

auto try_directive = [&](clang::SourceLocation loc, clang::FileID target) {
if(!locations.empty() || !target.isValid()) {
return;
}
auto [fid, directive_offset] = unit.decompose_location(loc);
if(fid != interested || directive_offset >= content.size()) {
return;
}
auto range = find_directive_argument(content, directive_offset, lang_opts);
if(!range || !range->contains(offset)) {
return;
}
locations.push_back(protocol::Location{
.uri = to_uri(unit.file_path(target)),
.range = protocol::Range{},
});
};

for(const auto& include: directives_it->second.includes) {
try_directive(include.location, include.fid);
}
for(const auto& has_include: directives_it->second.has_includes) {
try_directive(has_include.location, has_include.fid);
}
return locations;
}

} // namespace clice::feature
25 changes: 24 additions & 1 deletion src/feature/feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

#include "compile/compilation.h"
#include "compile/compilation_unit.h"
#include "feature/document_link.h"
#include "semantic/symbol_kind.h"
#include "support/anomaly.h"
#include "support/markup.h"

#include "kota/ipc/lsp/position.h"
#include "kota/ipc/lsp/protocol.h"
#include "kota/ipc/lsp/uri.h"

namespace clice::feature {

Expand All @@ -22,6 +24,21 @@ namespace protocol = kota::ipc::protocol;
using kota::ipc::lsp::LineMap;
using kota::ipc::lsp::PositionEncoding;

/// Render a file path (or an already-formed URI) as an LSP URI string.
inline auto to_uri(llvm::StringRef file) -> std::string {
const auto file_view = std::string_view(file.data(), file.size());

if(auto parsed = kota::ipc::lsp::URI::parse(file_view)) {
return parsed->str();
}

if(auto uri = kota::ipc::lsp::URI::from_file_path(file_view)) {
return uri->str();
}

return file.str();
}

inline auto to_position(const LineMap& map, std::uint32_t offset)
-> std::optional<protocol::Position> {
if(auto position = map.to_position(offset)) {
Expand Down Expand Up @@ -273,7 +290,13 @@ auto inlay_hints(CompilationUnitRef unit,
PositionEncoding encoding) -> std::vector<protocol::InlayHint>;

auto document_links(CompilationUnitRef unit, PositionEncoding encoding = PositionEncoding::UTF16)
-> std::vector<protocol::DocumentLink>;
-> std::vector<DocumentLink>;

/// Go-to-definition on an include directive: when `offset` falls on the
/// argument of an #include or __has_include in the interested file, the
/// resolved file's location (at its start). Empty otherwise.
auto include_definition(CompilationUnitRef unit, std::uint32_t offset)
-> std::vector<protocol::Location>;

auto diagnostics(CompilationUnitRef unit, PositionEncoding encoding = PositionEncoding::UTF16)
-> std::vector<protocol::Diagnostic>;
Expand Down
Loading
Loading