From 185da286d26ea7f892097c48b79a28acd7e5f172 Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Feb 2021 13:07:47 +0200 Subject: Moved CodeLens to ide crate --- crates/ide/src/annotations.rs | 142 ++++++++++++++++++++ crates/ide/src/lib.rs | 14 ++ crates/rust-analyzer/src/from_proto.rs | 40 +++++- crates/rust-analyzer/src/handlers.rs | 228 +++++---------------------------- crates/rust-analyzer/src/lsp_ext.rs | 8 ++ crates/rust-analyzer/src/to_proto.rs | 145 ++++++++++++++++++++- 6 files changed, 376 insertions(+), 201 deletions(-) create mode 100644 crates/ide/src/annotations.rs (limited to 'crates') diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs new file mode 100644 index 000000000..6d54b6b57 --- /dev/null +++ b/crates/ide/src/annotations.rs @@ -0,0 +1,142 @@ +use hir::Semantics; +use ide_db::{ + base_db::{FileId, FilePosition, FileRange, SourceDatabase}, + RootDatabase, SymbolKind, +}; +use syntax::TextRange; + +use crate::{ + file_structure::file_structure, + fn_references::find_all_methods, + goto_implementation::goto_implementation, + references::find_all_refs, + runnables::{runnables, Runnable}, + NavigationTarget, RunnableKind, +}; + +// Feature: Annotations +// +// Provides user with annotations above items for looking up references or impl blocks +// and running/debugging binaries. +pub struct Annotation { + pub range: TextRange, + pub kind: AnnotationKind, +} + +pub enum AnnotationKind { + Runnable { debug: bool, runnable: Runnable }, + HasImpls { position: FilePosition, data: Option> }, + HasReferences { position: FilePosition, data: Option> }, +} + +pub struct AnnotationConfig { + pub binary_target: bool, + pub annotate_runnables: bool, + pub annotate_impls: bool, + pub annotate_references: bool, + pub annotate_method_references: bool, + pub run: bool, + pub debug: bool, +} + +pub(crate) fn annotations( + db: &RootDatabase, + file_id: FileId, + config: AnnotationConfig, +) -> Vec { + let mut annotations = Vec::default(); + + if config.annotate_runnables { + for runnable in runnables(db, file_id) { + if !matches!(runnable.kind, RunnableKind::Bin) || !config.binary_target { + continue; + } + + let action = runnable.action(); + let range = runnable.nav.full_range; + + if config.run { + annotations.push(Annotation { + range, + // FIXME: This one allocates without reason if run is enabled, but debug is disabled + kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() }, + }); + } + + if action.debugee && config.debug { + annotations.push(Annotation { + range, + kind: AnnotationKind::Runnable { debug: true, runnable }, + }); + } + } + } + + file_structure(&db.parse(file_id).tree()) + .into_iter() + .filter(|node| { + matches!( + node.kind, + SymbolKind::Trait + | SymbolKind::Struct + | SymbolKind::Enum + | SymbolKind::Union + | SymbolKind::Const + ) + }) + .for_each(|node| { + if config.annotate_impls && node.kind != SymbolKind::Const { + annotations.push(Annotation { + range: node.node_range, + kind: AnnotationKind::HasImpls { + position: FilePosition { file_id, offset: node.navigation_range.start() }, + data: None, + }, + }); + } + + if config.annotate_references { + annotations.push(Annotation { + range: node.node_range, + kind: AnnotationKind::HasReferences { + position: FilePosition { file_id, offset: node.navigation_range.start() }, + data: None, + }, + }); + } + }); + + if config.annotate_method_references { + annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { + range: method.range, + kind: AnnotationKind::HasReferences { + position: FilePosition { file_id, offset: method.range.start() }, + data: None, + }, + })); + } + + annotations +} + +pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation { + match annotation.kind { + AnnotationKind::HasImpls { position, ref mut data } => { + *data = goto_implementation(db, position).map(|range| range.info); + } + AnnotationKind::HasReferences { position, ref mut data } => { + *data = find_all_refs(&Semantics::new(db), position, None).map(|result| { + result + .references + .into_iter() + .map(|(_, access)| access.into_iter()) + .flatten() + .map(|(range, _)| FileRange { file_id: position.file_id, range }) + .collect() + }); + } + _ => {} + }; + + annotation +} diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 592b12925..89e7bef7d 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -22,6 +22,7 @@ mod markup; mod prime_caches; mod display; +mod annotations; mod call_hierarchy; mod diagnostics; mod expand_macro; @@ -63,6 +64,7 @@ use syntax::SourceFile; use crate::display::ToNav; pub use crate::{ + annotations::{Annotation, AnnotationConfig, AnnotationKind}, call_hierarchy::CallItem, diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, display::navigation_target::NavigationTarget, @@ -555,6 +557,18 @@ impl Analysis { }) } + pub fn annotations( + &self, + file_id: FileId, + config: AnnotationConfig, + ) -> Cancelable> { + self.with_db(|db| annotations::annotations(db, file_id, config)) + } + + pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable { + self.with_db(|db| annotations::resolve_annotation(db, annotation)) + } + /// Performs an operation on that may be Canceled. fn with_db(&self, f: F) -> Cancelable where diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index aa6b808d6..6676eebf4 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs @@ -1,12 +1,12 @@ //! Conversion lsp_types types to rust-analyzer specific ones. use std::convert::TryFrom; -use ide::{AssistKind, LineCol, LineIndex}; +use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex}; use ide_db::base_db::{FileId, FilePosition, FileRange}; use syntax::{TextRange, TextSize}; use vfs::AbsPathBuf; -use crate::{global_state::GlobalStateSnapshot, Result}; +use crate::{from_json, global_state::GlobalStateSnapshot, lsp_ext, Result}; pub(crate) fn abs_path(url: &lsp_types::Url) -> Result { let path = url.to_file_path().map_err(|()| "url is not a file")?; @@ -66,3 +66,39 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option Some(assist_kind) } + +pub(crate) fn annotation( + world: &GlobalStateSnapshot, + code_lens: lsp_types::CodeLens, +) -> Result { + let data = code_lens.data.unwrap(); + let resolve = from_json::("CodeLensResolveData", data)?; + + match resolve { + lsp_ext::CodeLensResolveData::Impls(params) => { + let file_id = + world.url_to_file_id(¶ms.text_document_position_params.text_document.uri)?; + let line_index = world.analysis.file_line_index(file_id)?; + + Ok(Annotation { + range: text_range(&line_index, code_lens.range), + kind: AnnotationKind::HasImpls { + position: file_position(world, params.text_document_position_params)?, + data: None, + }, + }) + } + lsp_ext::CodeLensResolveData::References(params) => { + let file_id = world.url_to_file_id(¶ms.text_document.uri)?; + let line_index = world.analysis.file_line_index(file_id)?; + + Ok(Annotation { + range: text_range(&line_index, code_lens.range), + kind: AnnotationKind::HasReferences { + position: file_position(world, params)?, + data: None, + }, + }) + } + } +} diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 8898c12e3..b051c8f6c 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -9,8 +9,9 @@ use std::{ }; use ide::{ - FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget, - Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit, + AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, + NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, + TextEdit, }; use ide_db::SymbolKind; use itertools::Itertools; @@ -35,7 +36,7 @@ use crate::{ cargo_target_spec::CargoTargetSpec, config::RustfmtConfig, diff::diff, - from_json, from_proto, + from_proto, global_state::{GlobalState, GlobalStateSnapshot}, line_endings::LineEndings, lsp_ext::{self, InlayHint, InlayHintsParams}, @@ -1078,177 +1079,51 @@ pub(crate) fn handle_code_lens( params: lsp_types::CodeLensParams, ) -> Result>> { let _p = profile::span("handle_code_lens"); - let mut lenses: Vec = Default::default(); let lens_config = snap.config.lens(); if lens_config.none() { // early return before any db query! - return Ok(Some(lenses)); + return Ok(Some(Vec::default())); } let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; - let line_index = snap.analysis.file_line_index(file_id)?; - let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; - - if lens_config.runnable() { - // Gather runnables - for runnable in snap.analysis.runnables(file_id)? { - if should_skip_target(&runnable, cargo_spec.as_ref()) { - continue; - } - - let action = runnable.action(); - let range = to_proto::range(&line_index, runnable.nav.full_range); - let r = to_proto::runnable(&snap, file_id, runnable)?; - if lens_config.run { - let lens = CodeLens { - range, - command: Some(run_single_command(&r, action.run_title)), - data: None, - }; - lenses.push(lens); - } + let cargo_target_spec = CargoTargetSpec::for_file(&snap, file_id)?; - if action.debugee && lens_config.debug { - let debug_lens = - CodeLens { range, command: Some(debug_single_command(&r)), data: None }; - lenses.push(debug_lens); - } - } - } - - if lens_config.implementations || lens_config.refs { - snap.analysis - .file_structure(file_id)? - .into_iter() - .filter(|it| { - matches!( - it.kind, - SymbolKind::Trait | SymbolKind::Struct | SymbolKind::Enum | SymbolKind::Union - ) - }) - .for_each(|it| { - let range = to_proto::range(&line_index, it.node_range); - let position = to_proto::position(&line_index, it.navigation_range.start()); - let doc_pos = lsp_types::TextDocumentPositionParams::new( - params.text_document.clone(), - position, - ); - let goto_params = lsp_types::request::GotoImplementationParams { - text_document_position_params: doc_pos.clone(), - work_done_progress_params: Default::default(), - partial_result_params: Default::default(), - }; - - if lens_config.implementations { - lenses.push(CodeLens { - range, - command: None, - data: Some(to_value(CodeLensResolveData::Impls(goto_params)).unwrap()), - }) - } - - if lens_config.refs { - lenses.push(CodeLens { - range, - command: None, - data: Some(to_value(CodeLensResolveData::References(doc_pos)).unwrap()), + let lenses = snap + .analysis + .annotations( + file_id, + AnnotationConfig { + binary_target: cargo_target_spec + .map(|spec| { + matches!( + spec.target_kind, + TargetKind::Bin | TargetKind::Example | TargetKind::Test + ) }) - } - }); - } - - if lens_config.method_refs { - lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| { - let range = to_proto::range(&line_index, it.range); - let position = to_proto::position(&line_index, it.range.start()); - let lens_params = - lsp_types::TextDocumentPositionParams::new(params.text_document.clone(), position); - - CodeLens { - range, - command: None, - data: Some(to_value(CodeLensResolveData::References(lens_params)).unwrap()), - } - })); - } + .unwrap_or(false), + annotate_runnables: lens_config.runnable(), + annotate_impls: lens_config.implementations, + annotate_references: lens_config.refs, + annotate_method_references: lens_config.method_refs, + run: lens_config.run, + debug: lens_config.debug, + }, + )? + .into_iter() + .map(|annotation| to_proto::code_lens(&snap, annotation).unwrap()) + .collect(); Ok(Some(lenses)) } -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -enum CodeLensResolveData { - Impls(lsp_types::request::GotoImplementationParams), - References(lsp_types::TextDocumentPositionParams), -} - pub(crate) fn handle_code_lens_resolve( snap: GlobalStateSnapshot, code_lens: CodeLens, ) -> Result { - let _p = profile::span("handle_code_lens_resolve"); - let data = code_lens.data.unwrap(); - let resolve = from_json::>("CodeLensResolveData", data)?; - match resolve { - Some(CodeLensResolveData::Impls(lens_params)) => { - let locations: Vec = - match handle_goto_implementation(snap, lens_params.clone())? { - Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], - Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, - Some(lsp_types::GotoDefinitionResponse::Link(links)) => links - .into_iter() - .map(|link| Location::new(link.target_uri, link.target_selection_range)) - .collect(), - _ => vec![], - }; - - let title = implementation_title(locations.len()); - let cmd = show_references_command( - title, - &lens_params.text_document_position_params.text_document.uri, - code_lens.range.start, - locations, - ); - Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) - } - Some(CodeLensResolveData::References(doc_position)) => { - let position = from_proto::file_position(&snap, doc_position.clone())?; - let locations = snap - .analysis - .find_all_refs(position, None) - .unwrap_or(None) - .map(|r| { - r.references - .into_iter() - .flat_map(|(file_id, ranges)| { - ranges.into_iter().map(move |(range, _)| FileRange { file_id, range }) - }) - .filter_map(|frange| to_proto::location(&snap, frange).ok()) - .collect_vec() - }) - .unwrap_or_default(); - - let title = reference_title(locations.len()); - let cmd = if locations.is_empty() { - Command { title, command: "".into(), arguments: None } - } else { - show_references_command( - title, - &doc_position.text_document.uri, - code_lens.range.start, - locations, - ) - }; + let annotation = from_proto::annotation(&snap, code_lens)?; - Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) - } - None => Ok(CodeLens { - range: code_lens.range, - command: Some(Command { title: "Error".into(), ..Default::default() }), - data: None, - }), - } + Ok(to_proto::code_lens(&snap, snap.analysis.resolve_annotation(annotation)?)?) } pub(crate) fn handle_document_highlight( @@ -1547,43 +1422,6 @@ pub(crate) fn handle_open_cargo_toml( Ok(Some(res)) } -fn implementation_title(count: usize) -> String { - if count == 1 { - "1 implementation".into() - } else { - format!("{} implementations", count) - } -} - -fn reference_title(count: usize) -> String { - if count == 1 { - "1 reference".into() - } else { - format!("{} references", count) - } -} - -fn show_references_command( - title: String, - uri: &lsp_types::Url, - position: lsp_types::Position, - locations: Vec, -) -> Command { - // We cannot use the 'editor.action.showReferences' command directly - // because that command requires vscode types which we convert in the handler - // on the client side. - - Command { - title, - command: "rust-analyzer.showReferences".into(), - arguments: Some(vec![ - to_value(uri).unwrap(), - to_value(position).unwrap(), - to_value(locations).unwrap(), - ]), - } -} - fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command { Command { title: title.to_string(), @@ -1635,8 +1473,8 @@ fn show_impl_command_link( .into_iter() .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok()) .collect(); - let title = implementation_title(locations.len()); - let command = show_references_command(title, &uri, position, locations); + let title = to_proto::implementation_title(locations.len()); + let command = to_proto::show_references_command(title, &uri, position, locations); return Some(lsp_ext::CommandLinkGroup { commands: vec![to_command_link(command, "Go to implementations".into())], diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index ce5a0e822..a1ad855c3 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -377,3 +377,11 @@ impl Request for OpenCargoToml { pub struct OpenCargoTomlParams { pub text_document: TextDocumentIdentifier, } + +/// Information about CodeLens, that is to be resolved. +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) enum CodeLensResolveData { + Impls(lsp_types::request::GotoImplementationParams), + References(lsp_types::TextDocumentPositionParams), +} diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index be10ac1ae..29fac96fb 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -5,13 +5,15 @@ use std::{ }; use ide::{ - Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, - FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, - InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, - RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize, + Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, + Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, + HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, + NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, + TextRange, TextSize, }; use ide_db::SymbolKind; use itertools::Itertools; +use serde_json::to_value; use crate::{ cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, @@ -863,6 +865,141 @@ pub(crate) fn runnable( }) } +pub(crate) fn code_lens( + snap: &GlobalStateSnapshot, + annotation: Annotation, +) -> Result { + match annotation.kind { + AnnotationKind::Runnable { debug, runnable: run } => { + let line_index = snap.analysis.file_line_index(run.nav.file_id)?; + let annotation_range = range(&line_index, annotation.range); + + let action = run.action(); + let r = runnable(&snap, run.nav.file_id, run)?; + + let command = if debug { + lsp_types::Command { + title: action.run_title.to_string(), + command: "rust-analyzer.runSingle".into(), + arguments: Some(vec![to_value(r).unwrap()]), + } + } else { + lsp_types::Command { + title: "Debug".into(), + command: "rust-analyzer.debugSingle".into(), + arguments: Some(vec![to_value(r).unwrap()]), + } + }; + + Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None }) + } + AnnotationKind::HasImpls { position: file_position, data } => { + let line_index = snap.analysis.file_line_index(file_position.file_id)?; + let annotation_range = range(&line_index, annotation.range); + let url = url(snap, file_position.file_id); + + let position = position(&line_index, file_position.offset); + + let id = lsp_types::TextDocumentIdentifier { uri: url.clone() }; + + let doc_pos = lsp_types::TextDocumentPositionParams::new(id.clone(), position); + + let goto_params = lsp_types::request::GotoImplementationParams { + text_document_position_params: doc_pos.clone(), + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }; + + let command = data.map(|ranges| { + let locations: Vec = ranges + .into_iter() + .filter_map(|target| { + location( + snap, + FileRange { file_id: target.file_id, range: target.full_range }, + ) + .ok() + }) + .collect(); + + show_references_command( + implementation_title(locations.len()), + &url, + position, + locations, + ) + }); + + Ok(lsp_types::CodeLens { + range: annotation_range, + command, + data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()), + }) + } + AnnotationKind::HasReferences { position: file_position, data } => { + let line_index = snap.analysis.file_line_index(file_position.file_id)?; + let annotation_range = range(&line_index, annotation.range); + let url = url(snap, file_position.file_id); + + let position = position(&line_index, file_position.offset); + + let id = lsp_types::TextDocumentIdentifier { uri: url.clone() }; + + let doc_pos = lsp_types::TextDocumentPositionParams::new(id, position); + + let command = data.map(|ranges| { + let locations: Vec = + ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect(); + + show_references_command(reference_title(locations.len()), &url, position, locations) + }); + + Ok(lsp_types::CodeLens { + range: annotation_range, + command, + data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()), + }) + } + } +} + +pub(crate) fn show_references_command( + title: String, + uri: &lsp_types::Url, + position: lsp_types::Position, + locations: Vec, +) -> lsp_types::Command { + // We cannot use the 'editor.action.showReferences' command directly + // because that command requires vscode types which we convert in the handler + // on the client side. + + lsp_types::Command { + title, + command: "rust-analyzer.showReferences".into(), + arguments: Some(vec![ + to_value(uri).unwrap(), + to_value(position).unwrap(), + to_value(locations).unwrap(), + ]), + } +} + +pub(crate) fn implementation_title(count: usize) -> String { + if count == 1 { + "1 implementation".into() + } else { + format!("{} implementations", count) + } +} + +pub(crate) fn reference_title(count: usize) -> String { + if count == 1 { + "1 reference".into() + } else { + format!("{} references", count) + } +} + pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { let value = crate::markdown::format_docs(markup.as_str()); lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } -- cgit v1.2.3 From 02ad828c6dc544ac2fb20b85d85ccf6ea495df9c Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Feb 2021 13:22:12 +0200 Subject: Fix incorrect references annotation --- crates/ide/src/annotations.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 6d54b6b57..a6f093ad7 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -129,9 +129,10 @@ pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) result .references .into_iter() - .map(|(_, access)| access.into_iter()) + .map(|(file_id, access)| { + access.into_iter().map(move |(range, _)| FileRange { file_id, range }) + }) .flatten() - .map(|(range, _)| FileRange { file_id: position.file_id, range }) .collect() }); } -- cgit v1.2.3 From c46b32c44987de02559f7ec5898765722fa45166 Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Feb 2021 15:27:04 +0200 Subject: Added annotation tests --- crates/ide/src/annotations.rs | 267 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) (limited to 'crates') diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index a6f093ad7..97a361194 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -18,11 +18,13 @@ use crate::{ // // Provides user with annotations above items for looking up references or impl blocks // and running/debugging binaries. +#[derive(Debug)] pub struct Annotation { pub range: TextRange, pub kind: AnnotationKind, } +#[derive(Debug)] pub enum AnnotationKind { Runnable { debug: bool, runnable: Runnable }, HasImpls { position: FilePosition, data: Option> }, @@ -141,3 +143,268 @@ pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) annotation } + +#[cfg(test)] +mod tests { + use ide_db::base_db::{FileId, FileRange}; + use syntax::{TextRange, TextSize}; + + use crate::{fixture, Annotation, AnnotationConfig, AnnotationKind, RunnableKind}; + + fn get_annotations( + ra_fixture: &str, + annotation_config: AnnotationConfig, + ) -> (FileId, Vec) { + let (analysis, file_id) = fixture::file(ra_fixture); + + let annotations: Vec = analysis + .annotations(file_id, annotation_config) + .unwrap() + .into_iter() + .map(move |annotation| analysis.resolve_annotation(annotation).unwrap()) + .collect(); + + if annotations.len() == 0 { + panic!("unresolved annotations") + } + + (file_id, annotations) + } + + macro_rules! check_annotation { + ( $ra_fixture:expr, $config:expr, $item_positions:expr, $pattern:pat, $checker:expr ) => { + let (file_id, annotations) = get_annotations($ra_fixture, $config); + + annotations.into_iter().for_each(|annotation| { + assert!($item_positions.contains(&annotation.range)); + + match annotation.kind { + $pattern => $checker(file_id), + _ => panic!("Unexpected annotation kind"), + } + }); + }; + } + + #[test] + fn const_annotations() { + check_annotation!( + r#" +const DEMO: i32 = 123; + +fn main() { + let hello = DEMO; +} + "#, + AnnotationConfig { + binary_target: false, + annotate_runnables: false, + annotate_impls: false, + annotate_references: true, + annotate_method_references: false, + run: false, + debug: false, + }, + &[TextRange::new(TextSize::from(0), TextSize::from(22))], + AnnotationKind::HasReferences { data: Some(ranges), .. }, + |file_id| assert_eq!( + *ranges.first().unwrap(), + FileRange { + file_id, + range: TextRange::new(TextSize::from(52), TextSize::from(56)) + } + ) + ); + } + + #[test] + fn unused_const_annotations() { + check_annotation!( + r#" +const DEMO: i32 = 123; + +fn main() {} + "#, + AnnotationConfig { + binary_target: false, + annotate_runnables: false, + annotate_impls: false, + annotate_references: true, + annotate_method_references: false, + run: false, + debug: false, + }, + &[TextRange::new(TextSize::from(0), TextSize::from(22))], + AnnotationKind::HasReferences { data: Some(ranges), .. }, + |_| assert_eq!(ranges.len(), 0) + ); + } + + #[test] + fn struct_references_annotations() { + check_annotation!( + r#" +struct Test; + +fn main() { + let test = Test; +} + "#, + AnnotationConfig { + binary_target: false, + annotate_runnables: false, + annotate_impls: false, + annotate_references: true, + annotate_method_references: false, + run: false, + debug: false, + }, + &[TextRange::new(TextSize::from(0), TextSize::from(12))], + AnnotationKind::HasReferences { data: Some(ranges), .. }, + |file_id| assert_eq!( + *ranges.first().unwrap(), + FileRange { + file_id, + range: TextRange::new(TextSize::from(41), TextSize::from(45)) + } + ) + ); + } + + #[test] + fn struct_and_trait_impls_annotations() { + check_annotation!( + r#" +struct Test; + +trait MyCoolTrait {} + +impl MyCoolTrait for Test {} + +fn main() { + let test = Test; +} + "#, + AnnotationConfig { + binary_target: false, + annotate_runnables: false, + annotate_impls: true, + annotate_references: false, + annotate_method_references: false, + run: false, + debug: false, + }, + &[ + TextRange::new(TextSize::from(0), TextSize::from(12)), + TextRange::new(TextSize::from(14), TextSize::from(34)) + ], + AnnotationKind::HasImpls { data: Some(ranges), .. }, + |_| assert_eq!( + ranges.first().unwrap().full_range, + TextRange::new(TextSize::from(36), TextSize::from(64)) + ) + ); + } + + #[test] + fn run_annotation() { + check_annotation!( + r#" +fn main() {} + "#, + AnnotationConfig { + binary_target: true, + annotate_runnables: true, + annotate_impls: false, + annotate_references: false, + annotate_method_references: false, + run: true, + debug: false, + }, + &[TextRange::new(TextSize::from(0), TextSize::from(12))], + AnnotationKind::Runnable { debug: false, runnable }, + |_| { + assert!(matches!(runnable.kind, RunnableKind::Bin)); + assert!(runnable.action().run_title.contains("Run")); + } + ); + } + + #[test] + fn debug_annotation() { + check_annotation!( + r#" +fn main() {} + "#, + AnnotationConfig { + binary_target: true, + annotate_runnables: true, + annotate_impls: false, + annotate_references: false, + annotate_method_references: false, + run: false, + debug: true, + }, + &[TextRange::new(TextSize::from(0), TextSize::from(12))], + AnnotationKind::Runnable { debug: true, runnable }, + |_| { + assert!(matches!(runnable.kind, RunnableKind::Bin)); + assert!(runnable.action().debugee); + } + ); + } + + #[test] + fn method_annotations() { + // We actually want to skip `fn main` annotation, as it has no references in it + // but just ignoring empty reference slices would lead to false-positive if something + // goes wrong in annotation resolving mechanism. By tracking if we iterated before finding + // an empty slice we can track if everything is settled. + let mut iterated_once = false; + + check_annotation!( + r#" +struct Test; + +impl Test { + fn self_by_ref(&self) {} +} + +fn main() { + Test.self_by_ref(); +} + "#, + AnnotationConfig { + binary_target: false, + annotate_runnables: false, + annotate_impls: false, + annotate_references: false, + annotate_method_references: true, + run: false, + debug: false, + }, + &[ + TextRange::new(TextSize::from(33), TextSize::from(44)), + TextRange::new(TextSize::from(61), TextSize::from(65)) + ], + AnnotationKind::HasReferences { data: Some(ranges), .. }, + |file_id| { + match ranges.as_slice() { + [first, ..] => { + assert_eq!( + *first, + FileRange { + file_id, + range: TextRange::new(TextSize::from(79), TextSize::from(90)) + } + ); + + iterated_once = true; + } + [] if iterated_once => {} + [] => panic!("One reference was expected but not found"), + } + } + ); + } +} -- cgit v1.2.3 From ee049b256a7718fb346a7172a34f0fc324b3269b Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Feb 2021 15:47:53 +0200 Subject: Improve runnable annotations order, fix incorrect ignore detection --- crates/ide/src/annotations.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'crates') diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 97a361194..43250bf5d 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -50,25 +50,26 @@ pub(crate) fn annotations( if config.annotate_runnables { for runnable in runnables(db, file_id) { - if !matches!(runnable.kind, RunnableKind::Bin) || !config.binary_target { + if should_skip_runnable(&runnable.kind, config.binary_target) { continue; } let action = runnable.action(); let range = runnable.nav.full_range; - if config.run { + if action.debugee && config.debug { annotations.push(Annotation { range, + // FIXME: This one allocates without reason if run is enabled, but debug is disabled - kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() }, + kind: AnnotationKind::Runnable { debug: true, runnable: runnable.clone() }, }); } - if action.debugee && config.debug { + if config.run { annotations.push(Annotation { range, - kind: AnnotationKind::Runnable { debug: true, runnable }, + kind: AnnotationKind::Runnable { debug: false, runnable }, }); } } @@ -144,6 +145,13 @@ pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) annotation } +fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool { + match kind { + RunnableKind::Bin => !binary_target, + _ => false, + } +} + #[cfg(test)] mod tests { use ide_db::base_db::{FileId, FileRange}; -- cgit v1.2.3 From 4c2b201b7d836c3978665c6ed955fafea90cb3dd Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Feb 2021 19:18:13 +0200 Subject: Make annotations tests similar to those in runnables --- crates/ide/src/annotations.rs | 895 +++++++++++++++++++++++++++++++++--------- 1 file changed, 707 insertions(+), 188 deletions(-) (limited to 'crates') diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 43250bf5d..414a60bed 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -154,103 +154,143 @@ fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool { #[cfg(test)] mod tests { - use ide_db::base_db::{FileId, FileRange}; - use syntax::{TextRange, TextSize}; + use expect_test::{expect, Expect}; - use crate::{fixture, Annotation, AnnotationConfig, AnnotationKind, RunnableKind}; + use crate::{fixture, Annotation, AnnotationConfig}; - fn get_annotations( - ra_fixture: &str, - annotation_config: AnnotationConfig, - ) -> (FileId, Vec) { + fn check(ra_fixture: &str, expect: Expect) { let (analysis, file_id) = fixture::file(ra_fixture); let annotations: Vec = analysis - .annotations(file_id, annotation_config) + .annotations( + file_id, + AnnotationConfig { + binary_target: true, + annotate_runnables: true, + annotate_impls: true, + annotate_references: true, + annotate_method_references: true, + run: true, + debug: true, + }, + ) .unwrap() .into_iter() - .map(move |annotation| analysis.resolve_annotation(annotation).unwrap()) + .map(|annotation| analysis.resolve_annotation(annotation).unwrap()) .collect(); - if annotations.len() == 0 { - panic!("unresolved annotations") - } - - (file_id, annotations) - } - - macro_rules! check_annotation { - ( $ra_fixture:expr, $config:expr, $item_positions:expr, $pattern:pat, $checker:expr ) => { - let (file_id, annotations) = get_annotations($ra_fixture, $config); - - annotations.into_iter().for_each(|annotation| { - assert!($item_positions.contains(&annotation.range)); - - match annotation.kind { - $pattern => $checker(file_id), - _ => panic!("Unexpected annotation kind"), - } - }); - }; + expect.assert_debug_eq(&annotations); } #[test] fn const_annotations() { - check_annotation!( + check( r#" const DEMO: i32 = 123; +const UNUSED: i32 = 123; + fn main() { let hello = DEMO; } "#, - AnnotationConfig { - binary_target: false, - annotate_runnables: false, - annotate_impls: false, - annotate_references: true, - annotate_method_references: false, - run: false, - debug: false, - }, - &[TextRange::new(TextSize::from(0), TextSize::from(22))], - AnnotationKind::HasReferences { data: Some(ranges), .. }, - |file_id| assert_eq!( - *ranges.first().unwrap(), - FileRange { - file_id, - range: TextRange::new(TextSize::from(52), TextSize::from(56)) - } - ) - ); - } - - #[test] - fn unused_const_annotations() { - check_annotation!( - r#" -const DEMO: i32 = 123; - -fn main() {} - "#, - AnnotationConfig { - binary_target: false, - annotate_runnables: false, - annotate_impls: false, - annotate_references: true, - annotate_method_references: false, - run: false, - debug: false, - }, - &[TextRange::new(TextSize::from(0), TextSize::from(22))], - AnnotationKind::HasReferences { data: Some(ranges), .. }, - |_| assert_eq!(ranges.len(), 0) + expect![[r#" + [ + Annotation { + range: 50..85, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 50..85, + focus_range: 53..57, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 50..85, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 50..85, + focus_range: 53..57, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 0..22, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 6, + }, + data: Some( + [ + FileRange { + file_id: FileId( + 0, + ), + range: 78..82, + }, + ], + ), + }, + }, + Annotation { + range: 24..48, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 30, + }, + data: Some( + [], + ), + }, + }, + Annotation { + range: 53..57, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 53, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], ); } #[test] fn struct_references_annotations() { - check_annotation!( + check( r#" struct Test; @@ -258,30 +298,103 @@ fn main() { let test = Test; } "#, - AnnotationConfig { - binary_target: false, - annotate_runnables: false, - annotate_impls: false, - annotate_references: true, - annotate_method_references: false, - run: false, - debug: false, - }, - &[TextRange::new(TextSize::from(0), TextSize::from(12))], - AnnotationKind::HasReferences { data: Some(ranges), .. }, - |file_id| assert_eq!( - *ranges.first().unwrap(), - FileRange { - file_id, - range: TextRange::new(TextSize::from(41), TextSize::from(45)) - } - ) + expect![[r#" + [ + Annotation { + range: 14..48, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 14..48, + focus_range: 17..21, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 14..48, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 14..48, + focus_range: 17..21, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 0..12, + kind: HasImpls { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [], + ), + }, + }, + Annotation { + range: 0..12, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [ + FileRange { + file_id: FileId( + 0, + ), + range: 41..45, + }, + ], + ), + }, + }, + Annotation { + range: 17..21, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 17, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], ); } #[test] fn struct_and_trait_impls_annotations() { - check_annotation!( + check( r#" struct Test; @@ -293,84 +406,229 @@ fn main() { let test = Test; } "#, - AnnotationConfig { - binary_target: false, - annotate_runnables: false, - annotate_impls: true, - annotate_references: false, - annotate_method_references: false, - run: false, - debug: false, - }, - &[ - TextRange::new(TextSize::from(0), TextSize::from(12)), - TextRange::new(TextSize::from(14), TextSize::from(34)) - ], - AnnotationKind::HasImpls { data: Some(ranges), .. }, - |_| assert_eq!( - ranges.first().unwrap().full_range, - TextRange::new(TextSize::from(36), TextSize::from(64)) - ) - ); - } - - #[test] - fn run_annotation() { - check_annotation!( - r#" -fn main() {} - "#, - AnnotationConfig { - binary_target: true, - annotate_runnables: true, - annotate_impls: false, - annotate_references: false, - annotate_method_references: false, - run: true, - debug: false, - }, - &[TextRange::new(TextSize::from(0), TextSize::from(12))], - AnnotationKind::Runnable { debug: false, runnable }, - |_| { - assert!(matches!(runnable.kind, RunnableKind::Bin)); - assert!(runnable.action().run_title.contains("Run")); - } + expect![[r#" + [ + Annotation { + range: 66..100, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 66..100, + focus_range: 69..73, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 66..100, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 66..100, + focus_range: 69..73, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 0..12, + kind: HasImpls { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [ + NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 36..64, + focus_range: 57..61, + name: "impl", + kind: Impl, + }, + ], + ), + }, + }, + Annotation { + range: 0..12, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [ + FileRange { + file_id: FileId( + 0, + ), + range: 57..61, + }, + FileRange { + file_id: FileId( + 0, + ), + range: 93..97, + }, + ], + ), + }, + }, + Annotation { + range: 14..34, + kind: HasImpls { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 20, + }, + data: Some( + [ + NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 36..64, + focus_range: 57..61, + name: "impl", + kind: Impl, + }, + ], + ), + }, + }, + Annotation { + range: 14..34, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 20, + }, + data: Some( + [ + FileRange { + file_id: FileId( + 0, + ), + range: 41..52, + }, + ], + ), + }, + }, + Annotation { + range: 69..73, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 69, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], ); } #[test] - fn debug_annotation() { - check_annotation!( + fn runnable_annotation() { + check( r#" fn main() {} "#, - AnnotationConfig { - binary_target: true, - annotate_runnables: true, - annotate_impls: false, - annotate_references: false, - annotate_method_references: false, - run: false, - debug: true, - }, - &[TextRange::new(TextSize::from(0), TextSize::from(12))], - AnnotationKind::Runnable { debug: true, runnable }, - |_| { - assert!(matches!(runnable.kind, RunnableKind::Bin)); - assert!(runnable.action().debugee); - } + expect![[r#" + [ + Annotation { + range: 0..12, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..12, + focus_range: 3..7, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 0..12, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..12, + focus_range: 3..7, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 3..7, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 3, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], ); } #[test] fn method_annotations() { - // We actually want to skip `fn main` annotation, as it has no references in it - // but just ignoring empty reference slices would lead to false-positive if something - // goes wrong in annotation resolving mechanism. By tracking if we iterated before finding - // an empty slice we can track if everything is settled. - let mut iterated_once = false; - - check_annotation!( + check( r#" struct Test; @@ -382,37 +640,298 @@ fn main() { Test.self_by_ref(); } "#, - AnnotationConfig { - binary_target: false, - annotate_runnables: false, - annotate_impls: false, - annotate_references: false, - annotate_method_references: true, - run: false, - debug: false, - }, - &[ - TextRange::new(TextSize::from(33), TextSize::from(44)), - TextRange::new(TextSize::from(61), TextSize::from(65)) - ], - AnnotationKind::HasReferences { data: Some(ranges), .. }, - |file_id| { - match ranges.as_slice() { - [first, ..] => { - assert_eq!( - *first, - FileRange { - file_id, - range: TextRange::new(TextSize::from(79), TextSize::from(90)) - } - ); - - iterated_once = true; - } - [] if iterated_once => {} - [] => panic!("One reference was expected but not found"), - } - } + expect![[r#" + [ + Annotation { + range: 58..95, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 58..95, + focus_range: 61..65, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 58..95, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 58..95, + focus_range: 61..65, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 0..12, + kind: HasImpls { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [ + NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 14..56, + focus_range: 19..23, + name: "impl", + kind: Impl, + }, + ], + ), + }, + }, + Annotation { + range: 0..12, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, + data: Some( + [ + FileRange { + file_id: FileId( + 0, + ), + range: 19..23, + }, + FileRange { + file_id: FileId( + 0, + ), + range: 74..78, + }, + ], + ), + }, + }, + Annotation { + range: 33..44, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 33, + }, + data: Some( + [ + FileRange { + file_id: FileId( + 0, + ), + range: 79..90, + }, + ], + ), + }, + }, + Annotation { + range: 61..65, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 61, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], + ); + } + + #[test] + fn test_annotations() { + check( + r#" +fn main() {} + +mod tests { + #[test] + fn my_cool_test() {} +} + "#, + expect![[r#" + [ + Annotation { + range: 0..12, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..12, + focus_range: 3..7, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 0..12, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..12, + focus_range: 3..7, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + }, + }, + Annotation { + range: 14..64, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 14..64, + focus_range: 18..23, + name: "tests", + kind: Module, + }, + kind: TestMod { + path: "tests", + }, + cfg: None, + }, + }, + }, + Annotation { + range: 14..64, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 14..64, + focus_range: 18..23, + name: "tests", + kind: Module, + }, + kind: TestMod { + path: "tests", + }, + cfg: None, + }, + }, + }, + Annotation { + range: 30..62, + kind: Runnable { + debug: true, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 30..62, + focus_range: 45..57, + name: "my_cool_test", + kind: Function, + }, + kind: Test { + test_id: Path( + "tests::my_cool_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + }, + }, + Annotation { + range: 30..62, + kind: Runnable { + debug: false, + runnable: Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 30..62, + focus_range: 45..57, + name: "my_cool_test", + kind: Function, + }, + kind: Test { + test_id: Path( + "tests::my_cool_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + }, + }, + Annotation { + range: 3..7, + kind: HasReferences { + position: FilePosition { + file_id: FileId( + 0, + ), + offset: 3, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], ); } } -- cgit v1.2.3