mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 04:44:57 +00:00
internal: check diagnostics in all files and not just the first one
This commit is contained in:
parent
7bff76d8ae
commit
a1940d8c75
6 changed files with 83 additions and 49 deletions
|
@ -341,7 +341,6 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::Expect;
|
use expect_test::Expect;
|
||||||
use hir::diagnostics::DiagnosticCode;
|
|
||||||
use ide_assists::AssistResolveStrategy;
|
use ide_assists::AssistResolveStrategy;
|
||||||
use stdx::trim_indent;
|
use stdx::trim_indent;
|
||||||
use test_utils::{assert_eq_text, extract_annotations};
|
use test_utils::{assert_eq_text, extract_annotations};
|
||||||
|
@ -442,25 +441,25 @@ mod tests {
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn check_diagnostics(ra_fixture: &str) {
|
pub(crate) fn check_diagnostics(ra_fixture: &str) {
|
||||||
check_diagnostics_with_inactive_code(ra_fixture, false)
|
let mut config = DiagnosticsConfig::default();
|
||||||
|
config.disabled.insert("inactive-code".to_string());
|
||||||
|
check_diagnostics_with_config(config, ra_fixture)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn check_diagnostics_with_inactive_code(ra_fixture: &str, with_inactive_code: bool) {
|
pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
|
||||||
let (analysis, file_id) = fixture::file(ra_fixture);
|
let (analysis, files) = fixture::files(ra_fixture);
|
||||||
let diagnostics = analysis
|
for file_id in files {
|
||||||
.diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
|
let diagnostics =
|
||||||
.unwrap();
|
analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
|
||||||
|
|
||||||
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
|
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
|
||||||
let mut actual = diagnostics
|
let mut actual =
|
||||||
.into_iter()
|
diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
|
||||||
.filter(|d| d.code != Some(DiagnosticCode("inactive-code")) || with_inactive_code)
|
|
||||||
.map(|d| (d.range, d.message))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
actual.sort_by_key(|(range, _)| range.start());
|
actual.sort_by_key(|(range, _)| range.start());
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn range_mapping_out_of_macros() {
|
fn range_mapping_out_of_macros() {
|
||||||
|
|
|
@ -37,11 +37,16 @@ pub(super) fn inactive_code(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::diagnostics::tests::check_diagnostics_with_inactive_code;
|
use crate::{diagnostics::tests::check_diagnostics_with_config, DiagnosticsConfig};
|
||||||
|
|
||||||
|
pub(crate) fn check(ra_fixture: &str) {
|
||||||
|
let config = DiagnosticsConfig::default();
|
||||||
|
check_diagnostics_with_config(config, ra_fixture)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cfg_diagnostics() {
|
fn cfg_diagnostics() {
|
||||||
check_diagnostics_with_inactive_code(
|
check(
|
||||||
r#"
|
r#"
|
||||||
fn f() {
|
fn f() {
|
||||||
// The three g̶e̶n̶d̶e̶r̶s̶ statements:
|
// The three g̶e̶n̶d̶e̶r̶s̶ statements:
|
||||||
|
@ -69,7 +74,6 @@ fn f() {
|
||||||
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +81,7 @@ fn f() {
|
||||||
fn inactive_item() {
|
fn inactive_item() {
|
||||||
// Additional tests in `cfg` crate. This only tests disabled cfgs.
|
// Additional tests in `cfg` crate. This only tests disabled cfgs.
|
||||||
|
|
||||||
check_diagnostics_with_inactive_code(
|
check(
|
||||||
r#"
|
r#"
|
||||||
#[cfg(no)] pub fn f() {}
|
#[cfg(no)] pub fn f() {}
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
//^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
||||||
|
@ -91,7 +95,6 @@ fn f() {
|
||||||
#[cfg(feature = "std")] use std;
|
#[cfg(feature = "std")] use std;
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
|
||||||
"#,
|
"#,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +102,7 @@ fn f() {
|
||||||
#[test]
|
#[test]
|
||||||
fn inactive_via_cfg_attr() {
|
fn inactive_via_cfg_attr() {
|
||||||
cov_mark::check!(cfg_attr_active);
|
cov_mark::check!(cfg_attr_active);
|
||||||
check_diagnostics_with_inactive_code(
|
check(
|
||||||
r#"
|
r#"
|
||||||
#[cfg_attr(not(never), cfg(no))] fn f() {}
|
#[cfg_attr(not(never), cfg(no))] fn f() {}
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
||||||
|
@ -111,7 +114,6 @@ fn f() {
|
||||||
#[cfg_attr(not(never), inline, cfg(no))] fn h() {}
|
#[cfg_attr(not(never), inline, cfg(no))] fn h() {}
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
||||||
"#,
|
"#,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,12 @@ pub(super) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::diagnostics::tests::{check_diagnostics, check_no_diagnostics};
|
use crate::{
|
||||||
|
diagnostics::tests::{
|
||||||
|
check_diagnostics, check_diagnostics_with_config, check_no_diagnostics,
|
||||||
|
},
|
||||||
|
DiagnosticsConfig,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builtin_macro_fails_expansion() {
|
fn builtin_macro_fails_expansion() {
|
||||||
|
@ -31,7 +36,14 @@ macro_rules! include { () => {} }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn include_macro_should_allow_empty_content() {
|
fn include_macro_should_allow_empty_content() {
|
||||||
check_diagnostics(
|
let mut config = DiagnosticsConfig::default();
|
||||||
|
|
||||||
|
// FIXME: This is a false-positive, the file is actually linked in via
|
||||||
|
// `include!` macro
|
||||||
|
config.disabled.insert("unlinked-file".to_string());
|
||||||
|
|
||||||
|
check_diagnostics_with_config(
|
||||||
|
config,
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Utilities for creating `Analysis` instances for tests.
|
//! Utilities for creating `Analysis` instances for tests.
|
||||||
use ide_db::base_db::fixture::ChangeFixture;
|
use ide_db::base_db::fixture::ChangeFixture;
|
||||||
use syntax::{TextRange, TextSize};
|
|
||||||
use test_utils::extract_annotations;
|
use test_utils::extract_annotations;
|
||||||
|
|
||||||
use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
|
use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
|
||||||
|
@ -63,15 +62,8 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
|
||||||
|
|
||||||
pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) {
|
pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) {
|
||||||
let (analysis, position, mut annotations) = annotations(ra_fixture);
|
let (analysis, position, mut annotations) = annotations(ra_fixture);
|
||||||
let (mut expected, data) = annotations.pop().unwrap();
|
let (expected, data) = annotations.pop().unwrap();
|
||||||
assert!(annotations.is_empty());
|
assert!(annotations.is_empty());
|
||||||
match data.as_str() {
|
assert_eq!(data, "");
|
||||||
"" => (),
|
|
||||||
"file" => {
|
|
||||||
expected.range =
|
|
||||||
TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
|
|
||||||
}
|
|
||||||
data => panic!("bad data: {}", data),
|
|
||||||
}
|
|
||||||
(analysis, position, expected)
|
(analysis, position, expected)
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,10 +190,21 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts `//^ some text` annotations
|
/// Extracts `//^^^ some text` annotations.
|
||||||
|
///
|
||||||
|
/// A run of `^^^` can be arbitrary long and points to the corresponding range
|
||||||
|
/// in the line above.
|
||||||
|
///
|
||||||
|
/// The `// ^file text` syntax can be used to attach `text` to the entirety of
|
||||||
|
/// the file.
|
||||||
|
///
|
||||||
|
/// Multiline string values are supported:
|
||||||
|
///
|
||||||
|
/// // ^^^ first line
|
||||||
|
/// // | second line
|
||||||
pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
|
pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut prev_line_start: Option<TextSize> = None;
|
let mut prev_line_start: Option<TextSize> = Some(0.into());
|
||||||
let mut line_start: TextSize = 0.into();
|
let mut line_start: TextSize = 0.into();
|
||||||
let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new();
|
let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new();
|
||||||
for line in text.split_inclusive('\n') {
|
for line in text.split_inclusive('\n') {
|
||||||
|
@ -202,10 +213,15 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
|
||||||
let annotation_offset = TextSize::of(&line[..idx + "//".len()]);
|
let annotation_offset = TextSize::of(&line[..idx + "//".len()]);
|
||||||
for annotation in extract_line_annotations(&line[idx + "//".len()..]) {
|
for annotation in extract_line_annotations(&line[idx + "//".len()..]) {
|
||||||
match annotation {
|
match annotation {
|
||||||
LineAnnotation::Annotation { mut range, content } => {
|
LineAnnotation::Annotation { mut range, content, file } => {
|
||||||
range += annotation_offset;
|
range += annotation_offset;
|
||||||
this_line_annotations.push((range.end(), res.len()));
|
this_line_annotations.push((range.end(), res.len()));
|
||||||
res.push((range + prev_line_start.unwrap(), content))
|
let range = if file {
|
||||||
|
TextRange::up_to(TextSize::of(text))
|
||||||
|
} else {
|
||||||
|
range + prev_line_start.unwrap()
|
||||||
|
};
|
||||||
|
res.push((range, content))
|
||||||
}
|
}
|
||||||
LineAnnotation::Continuation { mut offset, content } => {
|
LineAnnotation::Continuation { mut offset, content } => {
|
||||||
offset += annotation_offset;
|
offset += annotation_offset;
|
||||||
|
@ -226,11 +242,12 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
|
||||||
|
|
||||||
prev_line_annotations = this_line_annotations;
|
prev_line_annotations = this_line_annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LineAnnotation {
|
enum LineAnnotation {
|
||||||
Annotation { range: TextRange, content: String },
|
Annotation { range: TextRange, content: String, file: bool },
|
||||||
Continuation { offset: TextSize, content: String },
|
Continuation { offset: TextSize, content: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,12 +268,20 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
|
||||||
}
|
}
|
||||||
let range = TextRange::at(offset, len.try_into().unwrap());
|
let range = TextRange::at(offset, len.try_into().unwrap());
|
||||||
let next = line[len..].find(marker).map_or(line.len(), |it| it + len);
|
let next = line[len..].find(marker).map_or(line.len(), |it| it + len);
|
||||||
let content = line[len..][..next - len].trim().to_string();
|
let mut content = &line[len..][..next - len];
|
||||||
|
|
||||||
|
let mut file = false;
|
||||||
|
if !continuation && content.starts_with("file") {
|
||||||
|
file = true;
|
||||||
|
content = &content["file".len()..]
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = content.trim().to_string();
|
||||||
|
|
||||||
let annotation = if continuation {
|
let annotation = if continuation {
|
||||||
LineAnnotation::Continuation { offset: range.end(), content }
|
LineAnnotation::Continuation { offset: range.end(), content }
|
||||||
} else {
|
} else {
|
||||||
LineAnnotation::Annotation { range, content }
|
LineAnnotation::Annotation { range, content, file }
|
||||||
};
|
};
|
||||||
res.push(annotation);
|
res.push(annotation);
|
||||||
|
|
||||||
|
@ -277,16 +302,20 @@ fn main() {
|
||||||
zoo + 1
|
zoo + 1
|
||||||
} //^^^ type:
|
} //^^^ type:
|
||||||
// | i32
|
// | i32
|
||||||
|
|
||||||
|
// ^file
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
let res = extract_annotations(&text)
|
let res = extract_annotations(&text)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(range, ann)| (&text[range], ann))
|
.map(|(range, ann)| (&text[range], ann))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res[..3],
|
||||||
vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),]
|
[("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into())]
|
||||||
);
|
);
|
||||||
|
assert_eq!(res[3].0.len(), 115);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `false` if slow tests should not run, otherwise returns `true` and
|
/// Returns `false` if slow tests should not run, otherwise returns `true` and
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue