mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-09 05:38:32 +00:00
parent
302bf97bbf
commit
a8196ffe84
@ -20,6 +20,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
|
diagnostics::Diagnostic,
|
||||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||||
AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name,
|
AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name,
|
||||||
@ -126,6 +127,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||||||
original_range(self.db, node.as_ref())
|
original_range(self.db, node.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
|
||||||
|
let src = diagnostics.source();
|
||||||
|
let root = self.db.parse_or_expand(src.file_id).unwrap();
|
||||||
|
let node = src.value.to_node(&root);
|
||||||
|
original_range(self.db, src.with_value(&node))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
|
pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
|
||||||
let node = self.find_file(node);
|
let node = self.find_file(node);
|
||||||
node.ancestors_with_macros(self.db).map(|it| it.value)
|
node.ancestors_with_macros(self.db).map(|it| it.value)
|
||||||
|
@ -20,11 +20,11 @@ impl Diagnostic for UnresolvedModule {
|
|||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
"unresolved module".to_string()
|
"unresolved module".to_string()
|
||||||
}
|
}
|
||||||
fn highlight_range(&self) -> TextRange {
|
fn highlight_range(&self) -> InFile<TextRange> {
|
||||||
self.highlight_range
|
InFile::new(self.file, self.highlight_range)
|
||||||
}
|
}
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
InFile { file_id: self.file, value: self.decl.clone().into() }
|
InFile::new(self.file, self.decl.clone().into())
|
||||||
}
|
}
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
self
|
self
|
||||||
|
@ -22,7 +22,7 @@ use crate::{db::AstDatabase, InFile};
|
|||||||
|
|
||||||
pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
|
pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
|
||||||
fn message(&self) -> String;
|
fn message(&self) -> String;
|
||||||
fn highlight_range(&self) -> TextRange;
|
fn highlight_range(&self) -> InFile<TextRange>;
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr>;
|
fn source(&self) -> InFile<SyntaxNodePtr>;
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static);
|
fn as_any(&self) -> &(dyn Any + Send + 'static);
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,12 @@ impl Diagnostic for NoSuchField {
|
|||||||
"no such field".to_string()
|
"no such field".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_range(&self) -> TextRange {
|
fn highlight_range(&self) -> InFile<TextRange> {
|
||||||
self.highlight_range
|
InFile::new(self.file, self.highlight_range)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
InFile { file_id: self.file, value: self.field.clone().into() }
|
InFile::new(self.file, self.field.clone().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
@ -50,8 +50,8 @@ impl Diagnostic for MissingFields {
|
|||||||
}
|
}
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
fn highlight_range(&self) -> TextRange {
|
fn highlight_range(&self) -> InFile<TextRange> {
|
||||||
self.highlight_range
|
InFile::new(self.file, self.highlight_range)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
@ -88,8 +88,8 @@ impl Diagnostic for MissingPatFields {
|
|||||||
}
|
}
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
fn highlight_range(&self) -> TextRange {
|
fn highlight_range(&self) -> InFile<TextRange> {
|
||||||
self.highlight_range
|
InFile::new(self.file, self.highlight_range)
|
||||||
}
|
}
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
InFile { file_id: self.file, value: self.field_list.clone().into() }
|
InFile { file_id: self.file, value: self.field_list.clone().into() }
|
||||||
@ -111,8 +111,8 @@ impl Diagnostic for MissingMatchArms {
|
|||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
String::from("Missing match arm")
|
String::from("Missing match arm")
|
||||||
}
|
}
|
||||||
fn highlight_range(&self) -> TextRange {
|
fn highlight_range(&self) -> InFile<TextRange> {
|
||||||
self.highlight_range
|
InFile::new(self.file, self.highlight_range)
|
||||||
}
|
}
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
InFile { file_id: self.file, value: self.match_expr.clone().into() }
|
InFile { file_id: self.file, value: self.match_expr.clone().into() }
|
||||||
@ -133,8 +133,8 @@ impl Diagnostic for MissingOkInTailExpr {
|
|||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
"wrap return expression in Ok".to_string()
|
"wrap return expression in Ok".to_string()
|
||||||
}
|
}
|
||||||
fn highlight_range(&self) -> TextRange {
|
fn highlight_range(&self) -> InFile<TextRange> {
|
||||||
self.highlight_range
|
InFile::new(self.file, self.highlight_range)
|
||||||
}
|
}
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
InFile { file_id: self.file, value: self.expr.clone().into() }
|
InFile { file_id: self.file, value: self.expr.clone().into() }
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
//! FIXME: write short doc here
|
//! Collects diagnostics & fixits for a single file.
|
||||||
|
//!
|
||||||
|
//! The tricky bit here is that diagnostics are produced by hir in terms of
|
||||||
|
//! macro-expanded files, but we need to present them to the users in terms of
|
||||||
|
//! original files. So we need to map the ranges.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
@ -46,7 +50,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
let mut sink = DiagnosticSink::new(|d| {
|
let mut sink = DiagnosticSink::new(|d| {
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: None,
|
fix: None,
|
||||||
})
|
})
|
||||||
@ -62,7 +66,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
let create_file = FileSystemEdit::CreateFile { source_root, path };
|
let create_file = FileSystemEdit::CreateFile { source_root, path };
|
||||||
let fix = SourceChange::file_system_edit("create module", create_file);
|
let fix = SourceChange::file_system_edit("create module", create_file);
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: Some(fix),
|
fix: Some(fix),
|
||||||
@ -95,7 +99,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
};
|
};
|
||||||
|
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix,
|
fix,
|
||||||
@ -103,7 +107,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
})
|
})
|
||||||
.on::<hir::diagnostics::MissingMatchArms, _>(|d| {
|
.on::<hir::diagnostics::MissingMatchArms, _>(|d| {
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: None,
|
fix: None,
|
||||||
@ -115,7 +119,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
||||||
let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit);
|
let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit);
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: Some(fix),
|
fix: Some(fix),
|
||||||
@ -621,6 +625,62 @@ mod tests {
|
|||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range_mapping_out_of_macros() {
|
||||||
|
let (analysis, file_id) = single_file(
|
||||||
|
r"
|
||||||
|
fn some() {}
|
||||||
|
fn items() {}
|
||||||
|
fn here() {}
|
||||||
|
|
||||||
|
macro_rules! id {
|
||||||
|
($($tt:tt)*) => { $($tt)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _x = id![Foo { a: 42 }];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Foo {
|
||||||
|
pub a: i32,
|
||||||
|
pub b: i32,
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let diagnostics = analysis.diagnostics(file_id).unwrap();
|
||||||
|
assert_debug_snapshot!(diagnostics, @r###"
|
||||||
|
[
|
||||||
|
Diagnostic {
|
||||||
|
message: "Missing structure fields:\n- b",
|
||||||
|
range: [224; 233),
|
||||||
|
fix: Some(
|
||||||
|
SourceChange {
|
||||||
|
label: "fill struct fields",
|
||||||
|
source_file_edits: [
|
||||||
|
SourceFileEdit {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
edit: TextEdit {
|
||||||
|
atoms: [
|
||||||
|
AtomTextEdit {
|
||||||
|
delete: [3; 9),
|
||||||
|
insert: "{a:42, b: ()}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
file_system_edits: [],
|
||||||
|
cursor_position: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
severity: Error,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_unnecessary_braces_in_use_statement() {
|
fn test_check_unnecessary_braces_in_use_statement() {
|
||||||
check_not_applicable(
|
check_not_applicable(
|
||||||
|
Loading…
Reference in New Issue
Block a user