Skip to content

Commit 6610e60

Browse files
authored
Merge pull request #19244 from Veykril/push-nmnrnlysvyvk
Warn when the used toolchain looks too old for rust-analyzer
2 parents 015e81d + f19b205 commit 6610e60

File tree

5 files changed

+140
-114
lines changed

5 files changed

+140
-114
lines changed

src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ use triomphe::Arc;
3232
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
3333

3434
use crate::{
35-
completion_item_hash,
3635
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
3736
diagnostics::convert_diagnostic,
3837
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
3938
hack_recover_crate_name,
4039
line_index::LineEndings,
4140
lsp::{
41+
completion_item_hash,
4242
ext::{
4343
InternalTestingFetchConfigOption, InternalTestingFetchConfigParams,
4444
InternalTestingFetchConfigResponse,

src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs

Lines changed: 9 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
//! The `cli` submodule implements some batch-processing analysis, primarily as
1010
//! a debugging aid.
1111
12+
/// Any toolchain less than this version will likely not work with rust-analyzer built from this revision.
13+
pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version {
14+
major: 1,
15+
minor: 78,
16+
patch: 0,
17+
pre: semver::Prerelease::EMPTY,
18+
build: semver::BuildMetadata::EMPTY,
19+
};
20+
1221
pub mod cli;
1322

1423
mod command;
@@ -47,10 +56,7 @@ use self::lsp::ext as lsp_ext;
4756
#[cfg(test)]
4857
mod integrated_benchmarks;
4958

50-
use hir::Mutability;
51-
use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
5259
use serde::de::DeserializeOwned;
53-
use tenthash::TentHash;
5460

5561
pub use crate::{
5662
lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
@@ -65,115 +71,6 @@ pub fn from_json<T: DeserializeOwned>(
6571
.map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
6672
}
6773

68-
fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
69-
fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
70-
use ide_completion::{
71-
CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
72-
CompletionRelevanceTypeMatch,
73-
};
74-
75-
hasher.update([
76-
u8::from(relevance.exact_name_match),
77-
u8::from(relevance.is_local),
78-
u8::from(relevance.is_name_already_imported),
79-
u8::from(relevance.requires_import),
80-
u8::from(relevance.is_private_editable),
81-
]);
82-
83-
match relevance.type_match {
84-
None => hasher.update([0u8]),
85-
Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]),
86-
Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]),
87-
}
88-
89-
hasher.update([u8::from(relevance.trait_.is_some())]);
90-
if let Some(trait_) = &relevance.trait_ {
91-
hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
92-
}
93-
94-
match relevance.postfix_match {
95-
None => hasher.update([0u8]),
96-
Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]),
97-
Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]),
98-
}
99-
100-
hasher.update([u8::from(relevance.function.is_some())]);
101-
if let Some(function) = &relevance.function {
102-
hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
103-
let discriminant: u8 = match function.return_type {
104-
CompletionRelevanceReturnType::Other => 0,
105-
CompletionRelevanceReturnType::DirectConstructor => 1,
106-
CompletionRelevanceReturnType::Constructor => 2,
107-
CompletionRelevanceReturnType::Builder => 3,
108-
};
109-
hasher.update([discriminant]);
110-
}
111-
}
112-
113-
let mut hasher = TentHash::new();
114-
hasher.update([
115-
u8::from(is_ref_completion),
116-
u8::from(item.is_snippet),
117-
u8::from(item.deprecated),
118-
u8::from(item.trigger_call_info),
119-
]);
120-
121-
hasher.update(item.label.primary.len().to_ne_bytes());
122-
hasher.update(&item.label.primary);
123-
124-
hasher.update([u8::from(item.label.detail_left.is_some())]);
125-
if let Some(label_detail) = &item.label.detail_left {
126-
hasher.update(label_detail.len().to_ne_bytes());
127-
hasher.update(label_detail);
128-
}
129-
130-
hasher.update([u8::from(item.label.detail_right.is_some())]);
131-
if let Some(label_detail) = &item.label.detail_right {
132-
hasher.update(label_detail.len().to_ne_bytes());
133-
hasher.update(label_detail);
134-
}
135-
136-
// NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
137-
// and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
138-
//
139-
// Documentation hashing is skipped too, as it's a large blob to process,
140-
// while not really making completion properties more unique as they are already.
141-
142-
let kind_tag = item.kind.tag();
143-
hasher.update(kind_tag.len().to_ne_bytes());
144-
hasher.update(kind_tag);
145-
146-
hasher.update(item.lookup.len().to_ne_bytes());
147-
hasher.update(&item.lookup);
148-
149-
hasher.update([u8::from(item.detail.is_some())]);
150-
if let Some(detail) = &item.detail {
151-
hasher.update(detail.len().to_ne_bytes());
152-
hasher.update(detail);
153-
}
154-
155-
hash_completion_relevance(&mut hasher, &item.relevance);
156-
157-
hasher.update([u8::from(item.ref_match.is_some())]);
158-
if let Some((ref_mode, text_size)) = &item.ref_match {
159-
let discriminant = match ref_mode {
160-
CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
161-
CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
162-
CompletionItemRefMode::Dereference => 2u8,
163-
};
164-
hasher.update([discriminant]);
165-
hasher.update(u32::from(*text_size).to_ne_bytes());
166-
}
167-
168-
hasher.update(item.import_to_add.len().to_ne_bytes());
169-
for import_path in &item.import_to_add {
170-
hasher.update(import_path.len().to_ne_bytes());
171-
hasher.update(import_path);
172-
}
173-
174-
hasher.finalize()
175-
}
176-
17774
#[doc(hidden)]
17875
macro_rules! try_default_ {
17976
($it:expr $(,)?) => {

src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
33
use core::fmt;
44

5+
use hir::Mutability;
6+
use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
7+
use tenthash::TentHash;
8+
59
pub mod ext;
610

711
pub(crate) mod capabilities;
@@ -29,3 +33,112 @@ impl fmt::Display for LspError {
2933
}
3034

3135
impl std::error::Error for LspError {}
36+
37+
pub(crate) fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
38+
fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
39+
use ide_completion::{
40+
CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
41+
CompletionRelevanceTypeMatch,
42+
};
43+
44+
hasher.update([
45+
u8::from(relevance.exact_name_match),
46+
u8::from(relevance.is_local),
47+
u8::from(relevance.is_name_already_imported),
48+
u8::from(relevance.requires_import),
49+
u8::from(relevance.is_private_editable),
50+
]);
51+
52+
match relevance.type_match {
53+
None => hasher.update([0u8]),
54+
Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]),
55+
Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]),
56+
}
57+
58+
hasher.update([u8::from(relevance.trait_.is_some())]);
59+
if let Some(trait_) = &relevance.trait_ {
60+
hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
61+
}
62+
63+
match relevance.postfix_match {
64+
None => hasher.update([0u8]),
65+
Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]),
66+
Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]),
67+
}
68+
69+
hasher.update([u8::from(relevance.function.is_some())]);
70+
if let Some(function) = &relevance.function {
71+
hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
72+
let discriminant: u8 = match function.return_type {
73+
CompletionRelevanceReturnType::Other => 0,
74+
CompletionRelevanceReturnType::DirectConstructor => 1,
75+
CompletionRelevanceReturnType::Constructor => 2,
76+
CompletionRelevanceReturnType::Builder => 3,
77+
};
78+
hasher.update([discriminant]);
79+
}
80+
}
81+
82+
let mut hasher = TentHash::new();
83+
hasher.update([
84+
u8::from(is_ref_completion),
85+
u8::from(item.is_snippet),
86+
u8::from(item.deprecated),
87+
u8::from(item.trigger_call_info),
88+
]);
89+
90+
hasher.update(item.label.primary.len().to_ne_bytes());
91+
hasher.update(&item.label.primary);
92+
93+
hasher.update([u8::from(item.label.detail_left.is_some())]);
94+
if let Some(label_detail) = &item.label.detail_left {
95+
hasher.update(label_detail.len().to_ne_bytes());
96+
hasher.update(label_detail);
97+
}
98+
99+
hasher.update([u8::from(item.label.detail_right.is_some())]);
100+
if let Some(label_detail) = &item.label.detail_right {
101+
hasher.update(label_detail.len().to_ne_bytes());
102+
hasher.update(label_detail);
103+
}
104+
105+
// NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
106+
// and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
107+
//
108+
// Documentation hashing is skipped too, as it's a large blob to process,
109+
// while not really making completion properties more unique as they are already.
110+
111+
let kind_tag = item.kind.tag();
112+
hasher.update(kind_tag.len().to_ne_bytes());
113+
hasher.update(kind_tag);
114+
115+
hasher.update(item.lookup.len().to_ne_bytes());
116+
hasher.update(&item.lookup);
117+
118+
hasher.update([u8::from(item.detail.is_some())]);
119+
if let Some(detail) = &item.detail {
120+
hasher.update(detail.len().to_ne_bytes());
121+
hasher.update(detail);
122+
}
123+
124+
hash_completion_relevance(&mut hasher, &item.relevance);
125+
126+
hasher.update([u8::from(item.ref_match.is_some())]);
127+
if let Some((ref_mode, text_size)) = &item.ref_match {
128+
let discriminant = match ref_mode {
129+
CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
130+
CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
131+
CompletionItemRefMode::Dereference => 2u8,
132+
};
133+
hasher.update([discriminant]);
134+
hasher.update(u32::from(*text_size).to_ne_bytes());
135+
}
136+
137+
hasher.update(item.import_to_add.len().to_ne_bytes());
138+
for import_path in &item.import_to_add {
139+
hasher.update(import_path.len().to_ne_bytes());
140+
hasher.update(import_path);
141+
}
142+
143+
hasher.finalize()
144+
}

src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ use serde_json::to_value;
2424
use vfs::AbsPath;
2525

2626
use crate::{
27-
completion_item_hash,
2827
config::{CallInfoConfig, Config},
2928
global_state::GlobalStateSnapshot,
3029
line_index::{LineEndings, LineIndex, PositionEncoding},
3130
lsp::{
31+
completion_item_hash,
3232
ext::ShellRunnableArgs,
3333
semantic_tokens::{self, standard_fallback_type},
3434
utils::invalid_params_error,

src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,22 @@ impl GlobalState {
182182
self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None));
183183

184184
for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) {
185+
if let Some(toolchain) = &ws.toolchain {
186+
if *toolchain < crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION {
187+
status.health |= lsp_ext::Health::Warning;
188+
format_to!(
189+
message,
190+
"Workspace `{}` is using an outdated toolchain version `{}` but \
191+
rust-analyzer only supports `{}` and higher.\n\
192+
Consider using the rust-analyzer rustup component for your toolchain or
193+
upgrade your toolchain to a supported version.\n\n",
194+
ws.manifest_or_root(),
195+
toolchain,
196+
crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION,
197+
);
198+
}
199+
}
200+
185201
if let ProjectWorkspaceKind::Cargo { error: Some(error), .. }
186202
| ProjectWorkspaceKind::DetachedFile {
187203
cargo: Some((_, _, Some(error))), ..

0 commit comments

Comments
 (0)