Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -147,14 +148,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