mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-27 02:06:57 +00:00
Warn when the used toolchain looks too old for rust-analyzer
This commit is contained in:
parent
505b52da5f
commit
7ab7633e91
5 changed files with 140 additions and 114 deletions
|
|
@ -32,13 +32,13 @@ use triomphe::Arc;
|
|||
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
|
||||
|
||||
use crate::{
|
||||
completion_item_hash,
|
||||
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
|
||||
diagnostics::convert_diagnostic,
|
||||
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
|
||||
hack_recover_crate_name,
|
||||
line_index::LineEndings,
|
||||
lsp::{
|
||||
completion_item_hash,
|
||||
ext::{
|
||||
InternalTestingFetchConfigOption, InternalTestingFetchConfigParams,
|
||||
InternalTestingFetchConfigResponse,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,15 @@
|
|||
//! The `cli` submodule implements some batch-processing analysis, primarily as
|
||||
//! a debugging aid.
|
||||
|
||||
/// Any toolchain less than this version will likely not work with rust-analyzer built from this revision.
|
||||
pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version {
|
||||
major: 1,
|
||||
minor: 78,
|
||||
patch: 0,
|
||||
pre: semver::Prerelease::EMPTY,
|
||||
build: semver::BuildMetadata::EMPTY,
|
||||
};
|
||||
|
||||
pub mod cli;
|
||||
|
||||
mod command;
|
||||
|
|
@ -47,10 +56,7 @@ use self::lsp::ext as lsp_ext;
|
|||
#[cfg(test)]
|
||||
mod integrated_benchmarks;
|
||||
|
||||
use hir::Mutability;
|
||||
use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
|
||||
use serde::de::DeserializeOwned;
|
||||
use tenthash::TentHash;
|
||||
|
||||
pub use crate::{
|
||||
lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
|
||||
|
|
@ -65,115 +71,6 @@ pub fn from_json<T: DeserializeOwned>(
|
|||
.map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
|
||||
}
|
||||
|
||||
fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
|
||||
fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
|
||||
use ide_completion::{
|
||||
CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
|
||||
CompletionRelevanceTypeMatch,
|
||||
};
|
||||
|
||||
hasher.update([
|
||||
u8::from(relevance.exact_name_match),
|
||||
u8::from(relevance.is_local),
|
||||
u8::from(relevance.is_name_already_imported),
|
||||
u8::from(relevance.requires_import),
|
||||
u8::from(relevance.is_private_editable),
|
||||
]);
|
||||
|
||||
match relevance.type_match {
|
||||
None => hasher.update([0u8]),
|
||||
Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]),
|
||||
Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]),
|
||||
}
|
||||
|
||||
hasher.update([u8::from(relevance.trait_.is_some())]);
|
||||
if let Some(trait_) = &relevance.trait_ {
|
||||
hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
|
||||
}
|
||||
|
||||
match relevance.postfix_match {
|
||||
None => hasher.update([0u8]),
|
||||
Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]),
|
||||
Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]),
|
||||
}
|
||||
|
||||
hasher.update([u8::from(relevance.function.is_some())]);
|
||||
if let Some(function) = &relevance.function {
|
||||
hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
|
||||
let discriminant: u8 = match function.return_type {
|
||||
CompletionRelevanceReturnType::Other => 0,
|
||||
CompletionRelevanceReturnType::DirectConstructor => 1,
|
||||
CompletionRelevanceReturnType::Constructor => 2,
|
||||
CompletionRelevanceReturnType::Builder => 3,
|
||||
};
|
||||
hasher.update([discriminant]);
|
||||
}
|
||||
}
|
||||
|
||||
let mut hasher = TentHash::new();
|
||||
hasher.update([
|
||||
u8::from(is_ref_completion),
|
||||
u8::from(item.is_snippet),
|
||||
u8::from(item.deprecated),
|
||||
u8::from(item.trigger_call_info),
|
||||
]);
|
||||
|
||||
hasher.update(item.label.primary.len().to_ne_bytes());
|
||||
hasher.update(&item.label.primary);
|
||||
|
||||
hasher.update([u8::from(item.label.detail_left.is_some())]);
|
||||
if let Some(label_detail) = &item.label.detail_left {
|
||||
hasher.update(label_detail.len().to_ne_bytes());
|
||||
hasher.update(label_detail);
|
||||
}
|
||||
|
||||
hasher.update([u8::from(item.label.detail_right.is_some())]);
|
||||
if let Some(label_detail) = &item.label.detail_right {
|
||||
hasher.update(label_detail.len().to_ne_bytes());
|
||||
hasher.update(label_detail);
|
||||
}
|
||||
|
||||
// NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
|
||||
// and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
|
||||
//
|
||||
// Documentation hashing is skipped too, as it's a large blob to process,
|
||||
// while not really making completion properties more unique as they are already.
|
||||
|
||||
let kind_tag = item.kind.tag();
|
||||
hasher.update(kind_tag.len().to_ne_bytes());
|
||||
hasher.update(kind_tag);
|
||||
|
||||
hasher.update(item.lookup.len().to_ne_bytes());
|
||||
hasher.update(&item.lookup);
|
||||
|
||||
hasher.update([u8::from(item.detail.is_some())]);
|
||||
if let Some(detail) = &item.detail {
|
||||
hasher.update(detail.len().to_ne_bytes());
|
||||
hasher.update(detail);
|
||||
}
|
||||
|
||||
hash_completion_relevance(&mut hasher, &item.relevance);
|
||||
|
||||
hasher.update([u8::from(item.ref_match.is_some())]);
|
||||
if let Some((ref_mode, text_size)) = &item.ref_match {
|
||||
let discriminant = match ref_mode {
|
||||
CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
|
||||
CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
|
||||
CompletionItemRefMode::Dereference => 2u8,
|
||||
};
|
||||
hasher.update([discriminant]);
|
||||
hasher.update(u32::from(*text_size).to_ne_bytes());
|
||||
}
|
||||
|
||||
hasher.update(item.import_to_add.len().to_ne_bytes());
|
||||
for import_path in &item.import_to_add {
|
||||
hasher.update(import_path.len().to_ne_bytes());
|
||||
hasher.update(import_path);
|
||||
}
|
||||
|
||||
hasher.finalize()
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
macro_rules! try_default_ {
|
||||
($it:expr $(,)?) => {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
use core::fmt;
|
||||
|
||||
use hir::Mutability;
|
||||
use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
|
||||
use tenthash::TentHash;
|
||||
|
||||
pub mod ext;
|
||||
|
||||
pub(crate) mod capabilities;
|
||||
|
|
@ -29,3 +33,112 @@ impl fmt::Display for LspError {
|
|||
}
|
||||
|
||||
impl std::error::Error for LspError {}
|
||||
|
||||
pub(crate) fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
|
||||
fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
|
||||
use ide_completion::{
|
||||
CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
|
||||
CompletionRelevanceTypeMatch,
|
||||
};
|
||||
|
||||
hasher.update([
|
||||
u8::from(relevance.exact_name_match),
|
||||
u8::from(relevance.is_local),
|
||||
u8::from(relevance.is_name_already_imported),
|
||||
u8::from(relevance.requires_import),
|
||||
u8::from(relevance.is_private_editable),
|
||||
]);
|
||||
|
||||
match relevance.type_match {
|
||||
None => hasher.update([0u8]),
|
||||
Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]),
|
||||
Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]),
|
||||
}
|
||||
|
||||
hasher.update([u8::from(relevance.trait_.is_some())]);
|
||||
if let Some(trait_) = &relevance.trait_ {
|
||||
hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
|
||||
}
|
||||
|
||||
match relevance.postfix_match {
|
||||
None => hasher.update([0u8]),
|
||||
Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]),
|
||||
Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]),
|
||||
}
|
||||
|
||||
hasher.update([u8::from(relevance.function.is_some())]);
|
||||
if let Some(function) = &relevance.function {
|
||||
hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
|
||||
let discriminant: u8 = match function.return_type {
|
||||
CompletionRelevanceReturnType::Other => 0,
|
||||
CompletionRelevanceReturnType::DirectConstructor => 1,
|
||||
CompletionRelevanceReturnType::Constructor => 2,
|
||||
CompletionRelevanceReturnType::Builder => 3,
|
||||
};
|
||||
hasher.update([discriminant]);
|
||||
}
|
||||
}
|
||||
|
||||
let mut hasher = TentHash::new();
|
||||
hasher.update([
|
||||
u8::from(is_ref_completion),
|
||||
u8::from(item.is_snippet),
|
||||
u8::from(item.deprecated),
|
||||
u8::from(item.trigger_call_info),
|
||||
]);
|
||||
|
||||
hasher.update(item.label.primary.len().to_ne_bytes());
|
||||
hasher.update(&item.label.primary);
|
||||
|
||||
hasher.update([u8::from(item.label.detail_left.is_some())]);
|
||||
if let Some(label_detail) = &item.label.detail_left {
|
||||
hasher.update(label_detail.len().to_ne_bytes());
|
||||
hasher.update(label_detail);
|
||||
}
|
||||
|
||||
hasher.update([u8::from(item.label.detail_right.is_some())]);
|
||||
if let Some(label_detail) = &item.label.detail_right {
|
||||
hasher.update(label_detail.len().to_ne_bytes());
|
||||
hasher.update(label_detail);
|
||||
}
|
||||
|
||||
// NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
|
||||
// and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
|
||||
//
|
||||
// Documentation hashing is skipped too, as it's a large blob to process,
|
||||
// while not really making completion properties more unique as they are already.
|
||||
|
||||
let kind_tag = item.kind.tag();
|
||||
hasher.update(kind_tag.len().to_ne_bytes());
|
||||
hasher.update(kind_tag);
|
||||
|
||||
hasher.update(item.lookup.len().to_ne_bytes());
|
||||
hasher.update(&item.lookup);
|
||||
|
||||
hasher.update([u8::from(item.detail.is_some())]);
|
||||
if let Some(detail) = &item.detail {
|
||||
hasher.update(detail.len().to_ne_bytes());
|
||||
hasher.update(detail);
|
||||
}
|
||||
|
||||
hash_completion_relevance(&mut hasher, &item.relevance);
|
||||
|
||||
hasher.update([u8::from(item.ref_match.is_some())]);
|
||||
if let Some((ref_mode, text_size)) = &item.ref_match {
|
||||
let discriminant = match ref_mode {
|
||||
CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
|
||||
CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
|
||||
CompletionItemRefMode::Dereference => 2u8,
|
||||
};
|
||||
hasher.update([discriminant]);
|
||||
hasher.update(u32::from(*text_size).to_ne_bytes());
|
||||
}
|
||||
|
||||
hasher.update(item.import_to_add.len().to_ne_bytes());
|
||||
for import_path in &item.import_to_add {
|
||||
hasher.update(import_path.len().to_ne_bytes());
|
||||
hasher.update(import_path);
|
||||
}
|
||||
|
||||
hasher.finalize()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ use serde_json::to_value;
|
|||
use vfs::AbsPath;
|
||||
|
||||
use crate::{
|
||||
completion_item_hash,
|
||||
config::{CallInfoConfig, Config},
|
||||
global_state::GlobalStateSnapshot,
|
||||
line_index::{LineEndings, LineIndex, PositionEncoding},
|
||||
lsp::{
|
||||
completion_item_hash,
|
||||
ext::ShellRunnableArgs,
|
||||
semantic_tokens::{self, standard_fallback_type},
|
||||
utils::invalid_params_error,
|
||||
|
|
|
|||
|
|
@ -182,6 +182,22 @@ impl GlobalState {
|
|||
self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None));
|
||||
|
||||
for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) {
|
||||
if let Some(toolchain) = &ws.toolchain {
|
||||
if *toolchain < crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION {
|
||||
status.health |= lsp_ext::Health::Warning;
|
||||
format_to!(
|
||||
message,
|
||||
"Workspace `{}` is using an outdated toolchain version `{}` but \
|
||||
rust-analyzer only supports `{}` and higher.\n\
|
||||
Consider using the rust-analyzer rustup component for your toolchain or
|
||||
upgrade your toolchain to a supported version.\n\n",
|
||||
ws.manifest_or_root(),
|
||||
toolchain,
|
||||
crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let ProjectWorkspaceKind::Cargo { error: Some(error), .. }
|
||||
| ProjectWorkspaceKind::DetachedFile {
|
||||
cargo: Some((_, _, Some(error))), ..
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue