mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-27 10:17:15 +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 vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completion_item_hash,
|
|
||||||
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
|
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
|
||||||
diagnostics::convert_diagnostic,
|
diagnostics::convert_diagnostic,
|
||||||
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
|
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
|
||||||
hack_recover_crate_name,
|
hack_recover_crate_name,
|
||||||
line_index::LineEndings,
|
line_index::LineEndings,
|
||||||
lsp::{
|
lsp::{
|
||||||
|
completion_item_hash,
|
||||||
ext::{
|
ext::{
|
||||||
InternalTestingFetchConfigOption, InternalTestingFetchConfigParams,
|
InternalTestingFetchConfigOption, InternalTestingFetchConfigParams,
|
||||||
InternalTestingFetchConfigResponse,
|
InternalTestingFetchConfigResponse,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,15 @@
|
||||||
//! The `cli` submodule implements some batch-processing analysis, primarily as
|
//! The `cli` submodule implements some batch-processing analysis, primarily as
|
||||||
//! a debugging aid.
|
//! 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;
|
pub mod cli;
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
|
|
@ -47,10 +56,7 @@ use self::lsp::ext as lsp_ext;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod integrated_benchmarks;
|
mod integrated_benchmarks;
|
||||||
|
|
||||||
use hir::Mutability;
|
|
||||||
use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use tenthash::TentHash;
|
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
|
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}"))
|
.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)]
|
#[doc(hidden)]
|
||||||
macro_rules! try_default_ {
|
macro_rules! try_default_ {
|
||||||
($it:expr $(,)?) => {
|
($it:expr $(,)?) => {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
|
use hir::Mutability;
|
||||||
|
use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
|
||||||
|
use tenthash::TentHash;
|
||||||
|
|
||||||
pub mod ext;
|
pub mod ext;
|
||||||
|
|
||||||
pub(crate) mod capabilities;
|
pub(crate) mod capabilities;
|
||||||
|
|
@ -29,3 +33,112 @@ impl fmt::Display for LspError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error 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 vfs::AbsPath;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completion_item_hash,
|
|
||||||
config::{CallInfoConfig, Config},
|
config::{CallInfoConfig, Config},
|
||||||
global_state::GlobalStateSnapshot,
|
global_state::GlobalStateSnapshot,
|
||||||
line_index::{LineEndings, LineIndex, PositionEncoding},
|
line_index::{LineEndings, LineIndex, PositionEncoding},
|
||||||
lsp::{
|
lsp::{
|
||||||
|
completion_item_hash,
|
||||||
ext::ShellRunnableArgs,
|
ext::ShellRunnableArgs,
|
||||||
semantic_tokens::{self, standard_fallback_type},
|
semantic_tokens::{self, standard_fallback_type},
|
||||||
utils::invalid_params_error,
|
utils::invalid_params_error,
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,22 @@ impl GlobalState {
|
||||||
self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None));
|
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) {
|
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), .. }
|
if let ProjectWorkspaceKind::Cargo { error: Some(error), .. }
|
||||||
| ProjectWorkspaceKind::DetachedFile {
|
| ProjectWorkspaceKind::DetachedFile {
|
||||||
cargo: Some((_, _, Some(error))), ..
|
cargo: Some((_, _, Some(error))), ..
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue