Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-diagnostics/src/lib.rs')
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 194 |
1 files changed, 55 insertions, 139 deletions
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 1530e64652..0c6953419f 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -23,6 +23,11 @@ //! There are also a couple of ad-hoc diagnostics implemented directly here, we //! don't yet have a great pattern for how to do them properly. +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; + mod handlers { pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; @@ -83,29 +88,26 @@ mod handlers { #[cfg(test)] mod tests; -use std::{iter, sync::LazyLock}; +use std::sync::LazyLock; -use either::Either; use hir::{ Crate, DisplayTarget, InFile, Semantics, db::ExpandDatabase, diagnostics::AnyDiagnostic, }; use ide_db::{ - EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, + FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, base_db::{ReleaseChannel, RootQueryDb as _}, generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup}, imports::insert_use::InsertUseConfig, label::Label, + rename::RenameConfig, source_change::SourceChange, - syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, }; -use itertools::Itertools; use syntax::{ - AstPtr, Edition, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, T, TextRange, - ast::{self, AstNode, HasAttrs}, + AstPtr, Edition, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, + ast::{self, AstNode}, }; -// FIXME: Make this an enum #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum DiagnosticCode { RustcHardError(&'static str), @@ -236,6 +238,7 @@ pub struct DiagnosticsConfig { pub prefer_absolute: bool, pub term_search_fuel: u64, pub term_search_borrowck: bool, + pub show_rename_conflicts: bool, } impl DiagnosticsConfig { @@ -264,8 +267,13 @@ impl DiagnosticsConfig { prefer_absolute: false, term_search_fuel: 400, term_search_borrowck: true, + show_rename_conflicts: true, } } + + pub fn rename_config(&self) -> RenameConfig { + RenameConfig { show_conflicts: self.show_rename_conflicts } + } } struct DiagnosticsContext<'a> { @@ -277,31 +285,6 @@ struct DiagnosticsContext<'a> { is_nightly: bool, } -impl DiagnosticsContext<'_> { - fn resolve_precise_location( - &self, - node: &InFile<SyntaxNodePtr>, - precise_location: Option<TextRange>, - ) -> FileRange { - let sema = &self.sema; - (|| { - let precise_location = precise_location?; - let root = sema.parse_or_expand(node.file_id); - match root.covering_element(precise_location) { - syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)), - syntax::NodeOrToken::Token(it) => { - node.with_value(it).original_file_range_opt(sema.db) - } - } - })() - .map(|frange| ide_db::FileRange { - file_id: frange.file_id.file_id(self.sema.db), - range: frange.range, - }) - .unwrap_or_else(|| sema.diagnostics_display_range(*node)) - } -} - /// Request parser level diagnostics for the given [`FileId`]. pub fn syntax_diagnostics( db: &RootDatabase, @@ -315,9 +298,7 @@ pub fn syntax_diagnostics( } let sema = Semantics::new(db); - let editioned_file_id = sema - .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + let editioned_file_id = sema.attach_first_edition(file_id); let (file_id, _) = editioned_file_id.unpack(db); @@ -346,9 +327,7 @@ pub fn semantic_diagnostics( ) -> Vec<Diagnostic> { let _p = tracing::info_span!("semantic_diagnostics").entered(); let sema = Semantics::new(db); - let editioned_file_id = sema - .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + let editioned_file_id = sema.attach_first_edition(file_id); let (file_id, edition) = editioned_file_id.unpack(db); let mut res = Vec::new(); @@ -374,12 +353,12 @@ pub fn semantic_diagnostics( let module = sema.file_to_module_def(file_id); let is_nightly = matches!( - module.and_then(|m| db.toolchain_channel(m.krate().into())), + module.and_then(|m| db.toolchain_channel(m.krate(db).into())), Some(ReleaseChannel::Nightly) | None ); let krate = match module { - Some(module) => module.krate(), + Some(module) => module.krate(db), None => { match db.all_crates().last() { Some(last) => (*last).into(), @@ -426,7 +405,7 @@ pub fn semantic_diagnostics( Diagnostic::new( DiagnosticCode::SyntaxError, format!("Syntax Error in Expansion: {err}"), - ctx.resolve_precise_location(&d.node.clone(), d.precise_location), + ctx.sema.diagnostics_display_range_for_range(d.range), ) })); continue; @@ -512,7 +491,7 @@ pub fn semantic_diagnostics( // The edition isn't accurate (each diagnostics may have its own edition due to macros), // but it's okay as it's only being used for error recovery. - handle_lints(&ctx.sema, &mut lints, editioned_file_id.edition(db)); + handle_lints(&ctx.sema, file_id, krate, &mut lints, editioned_file_id.edition(db)); res.retain(|d| d.severity != Severity::Allow); @@ -620,6 +599,8 @@ fn build_lints_map( fn handle_lints( sema: &Semantics<'_, RootDatabase>, + file_id: FileId, + krate: hir::Crate, diagnostics: &mut [(InFile<SyntaxNode>, &mut Diagnostic)], edition: Edition, ) { @@ -635,10 +616,10 @@ fn handle_lints( } let mut diag_severity = - lint_severity_at(sema, node, &lint_groups(&diag.code, edition), edition); + lint_severity_at(sema, file_id, krate, node, &lint_groups(&diag.code, edition)); if let outline_diag_severity @ Some(_) = - find_outline_mod_lint_severity(sema, node, diag, edition) + find_outline_mod_lint_severity(sema, file_id, krate, node, diag, edition) { diag_severity = outline_diag_severity; } @@ -661,6 +642,8 @@ fn default_lint_severity(lint: &Lint, edition: Edition) -> Severity { fn find_outline_mod_lint_severity( sema: &Semantics<'_, RootDatabase>, + file_id: FileId, + krate: hir::Crate, node: &InFile<SyntaxNode>, diag: &Diagnostic, edition: Edition, @@ -673,123 +656,56 @@ fn find_outline_mod_lint_severity( let mod_def = sema.to_module_def(&mod_node)?; let module_source_file = sema.module_definition_node(mod_def); - let mut result = None; let lint_groups = lint_groups(&diag.code, edition); lint_attrs( sema, + file_id, + krate, ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"), - edition, ) - .for_each(|(lint, severity)| { - if lint_groups.contains(&lint) { - result = Some(severity); - } - }); - result + .find_map(|(lint, severity)| lint_groups.contains(&lint).then_some(severity)) } fn lint_severity_at( sema: &Semantics<'_, RootDatabase>, + file_id: FileId, + krate: hir::Crate, node: &InFile<SyntaxNode>, lint_groups: &LintGroups, - edition: Edition, ) -> Option<Severity> { node.value .ancestors() .filter_map(ast::AnyHasAttrs::cast) .find_map(|ancestor| { - lint_attrs(sema, ancestor, edition) + lint_attrs(sema, file_id, krate, ancestor) .find_map(|(lint, severity)| lint_groups.contains(&lint).then_some(severity)) }) .or_else(|| { - lint_severity_at(sema, &sema.find_parent_file(node.file_id)?, lint_groups, edition) - }) -} - -fn lint_attrs<'a>( - sema: &'a Semantics<'a, RootDatabase>, - ancestor: ast::AnyHasAttrs, - edition: Edition, -) -> impl Iterator<Item = (SmolStr, Severity)> + 'a { - ancestor - .attrs_including_inner() - .filter_map(|attr| { - attr.as_simple_call().and_then(|(name, value)| match &*name { - "allow" | "expect" => Some(Either::Left(iter::once((Severity::Allow, value)))), - "warn" => Some(Either::Left(iter::once((Severity::Warning, value)))), - "forbid" | "deny" => Some(Either::Left(iter::once((Severity::Error, value)))), - "cfg_attr" => { - let mut lint_attrs = Vec::new(); - cfg_attr_lint_attrs(sema, &value, &mut lint_attrs); - Some(Either::Right(lint_attrs.into_iter())) - } - _ => None, - }) - }) - .flatten() - .flat_map(move |(severity, lints)| { - parse_tt_as_comma_sep_paths(lints, edition).into_iter().flat_map(move |lints| { - // Rejoin the idents with `::`, so we have no spaces in between. - lints.into_iter().map(move |lint| { - ( - lint.segments().filter_map(|segment| segment.name_ref()).join("::").into(), - severity, - ) - }) - }) + lint_severity_at( + sema, + file_id, + krate, + &sema.find_parent_file(node.file_id)?, + lint_groups, + ) }) } -fn cfg_attr_lint_attrs( +// FIXME: Switch this to analysis' `expand_cfg_attr`. +fn lint_attrs( sema: &Semantics<'_, RootDatabase>, - value: &ast::TokenTree, - lint_attrs: &mut Vec<(Severity, ast::TokenTree)>, -) { - let prev_len = lint_attrs.len(); - - let mut iter = value.token_trees_and_tokens().filter(|it| match it { - NodeOrToken::Node(_) => true, - NodeOrToken::Token(it) => !it.kind().is_trivia(), - }); - - // Skip the condition. - for value in &mut iter { - if value.as_token().is_some_and(|it| it.kind() == T![,]) { - break; - } - } - - while let Some(value) = iter.next() { - if let Some(token) = value.as_token() - && token.kind() == SyntaxKind::IDENT - { - let severity = match token.text() { - "allow" | "expect" => Some(Severity::Allow), - "warn" => Some(Severity::Warning), - "forbid" | "deny" => Some(Severity::Error), - "cfg_attr" => { - if let Some(NodeOrToken::Node(value)) = iter.next() { - cfg_attr_lint_attrs(sema, &value, lint_attrs); - } - None - } - _ => None, - }; - if let Some(severity) = severity { - let lints = iter.next(); - if let Some(NodeOrToken::Node(lints)) = lints { - lint_attrs.push((severity, lints)); - } - } - } - } - - if prev_len != lint_attrs.len() - && let Some(false) | None = sema.check_cfg_attr(value) - { - // Discard the attributes when the condition is false. - lint_attrs.truncate(prev_len); - } + file_id: FileId, + krate: hir::Crate, + ancestor: ast::AnyHasAttrs, +) -> impl Iterator<Item = (SmolStr, Severity)> { + sema.lint_attrs(file_id, krate, ancestor).rev().map(|(lint_attr, lint)| { + let severity = match lint_attr { + hir::LintAttr::Allow | hir::LintAttr::Expect => Severity::Allow, + hir::LintAttr::Warn => Severity::Warning, + hir::LintAttr::Deny | hir::LintAttr::Forbid => Severity::Error, + }; + (lint, severity) + }) } #[derive(Debug)] |