⬆️ rust-analyzer

This commit is contained in:
Laurențiu Nicola 2023-03-13 10:42:24 +02:00
parent 15b867b5db
commit b2f6fd4f96
217 changed files with 12639 additions and 3059 deletions

View file

@ -107,7 +107,18 @@ pub(crate) fn remove_links(markdown: &str) -> String {
out
}
/// Retrieve a link to documentation for the given symbol.
// Feature: Open Docs
//
// Retrieve a link to documentation for the given symbol.
//
// The simplest way to use this feature is via the context menu. Right-click on
// the selected item. The context menu opens. Select **Open Docs**.
//
// |===
// | Editor | Action Name
//
// | VS Code | **rust-analyzer: Open Docs**
// |===
pub(crate) fn external_docs(
db: &RootDatabase,
position: &FilePosition,
@ -181,6 +192,7 @@ pub(crate) fn resolve_doc_path_for_def(
Definition::Const(it) => it.resolve_doc_path(db, link, ns),
Definition::Static(it) => it.resolve_doc_path(db, link, ns),
Definition::Trait(it) => it.resolve_doc_path(db, link, ns),
Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns),
Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
Definition::Field(it) => it.resolve_doc_path(db, link, ns),
@ -493,6 +505,7 @@ fn filename_and_frag_for_def(
None => String::from("index.html"),
},
Definition::Trait(t) => format!("trait.{}.html", t.name(db)),
Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)),
Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)),
Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()),
Definition::Function(f) => format!("fn.{}.html", f.name(db)),

View file

@ -149,6 +149,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)),
ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)),
ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)),
ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)),
ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
@ -262,6 +263,8 @@ enum E { X, Y(i32) }
type T = ();
static S: i32 = 92;
const C: i32 = 92;
trait Tr {}
trait Alias = Tr;
impl E {}
@ -457,11 +460,33 @@ fn g() {}
),
deprecated: false,
},
StructureNode {
parent: None,
label: "Tr",
navigation_range: 239..241,
node_range: 233..244,
kind: SymbolKind(
Trait,
),
detail: None,
deprecated: false,
},
StructureNode {
parent: None,
label: "Alias",
navigation_range: 251..256,
node_range: 245..262,
kind: SymbolKind(
TraitAlias,
),
detail: None,
deprecated: false,
},
StructureNode {
parent: None,
label: "impl E",
navigation_range: 239..240,
node_range: 234..243,
navigation_range: 269..270,
node_range: 264..273,
kind: SymbolKind(
Impl,
),
@ -471,8 +496,8 @@ fn g() {}
StructureNode {
parent: None,
label: "impl fmt::Debug for E",
navigation_range: 265..266,
node_range: 245..269,
navigation_range: 295..296,
node_range: 275..299,
kind: SymbolKind(
Impl,
),
@ -482,8 +507,8 @@ fn g() {}
StructureNode {
parent: None,
label: "mc",
navigation_range: 284..286,
node_range: 271..303,
navigation_range: 314..316,
node_range: 301..333,
kind: SymbolKind(
Macro,
),
@ -493,8 +518,8 @@ fn g() {}
StructureNode {
parent: None,
label: "mcexp",
navigation_range: 334..339,
node_range: 305..356,
navigation_range: 364..369,
node_range: 335..386,
kind: SymbolKind(
Macro,
),
@ -504,8 +529,8 @@ fn g() {}
StructureNode {
parent: None,
label: "mcexp",
navigation_range: 387..392,
node_range: 358..409,
navigation_range: 417..422,
node_range: 388..439,
kind: SymbolKind(
Macro,
),
@ -515,8 +540,8 @@ fn g() {}
StructureNode {
parent: None,
label: "obsolete",
navigation_range: 428..436,
node_range: 411..441,
navigation_range: 458..466,
node_range: 441..471,
kind: SymbolKind(
Function,
),
@ -528,8 +553,8 @@ fn g() {}
StructureNode {
parent: None,
label: "very_obsolete",
navigation_range: 481..494,
node_range: 443..499,
navigation_range: 511..524,
node_range: 473..529,
kind: SymbolKind(
Function,
),
@ -541,8 +566,8 @@ fn g() {}
StructureNode {
parent: None,
label: "Some region name",
navigation_range: 501..528,
node_range: 501..528,
navigation_range: 531..558,
node_range: 531..558,
kind: Region,
detail: None,
deprecated: false,
@ -550,8 +575,8 @@ fn g() {}
StructureNode {
parent: None,
label: "m",
navigation_range: 568..569,
node_range: 543..606,
navigation_range: 598..599,
node_range: 573..636,
kind: SymbolKind(
Module,
),
@ -560,22 +585,22 @@ fn g() {}
},
StructureNode {
parent: Some(
20,
22,
),
label: "dontpanic",
navigation_range: 543..563,
node_range: 543..563,
navigation_range: 573..593,
node_range: 573..593,
kind: Region,
detail: None,
deprecated: false,
},
StructureNode {
parent: Some(
20,
22,
),
label: "f",
navigation_range: 575..576,
node_range: 572..581,
navigation_range: 605..606,
node_range: 602..611,
kind: SymbolKind(
Function,
),
@ -586,11 +611,11 @@ fn g() {}
},
StructureNode {
parent: Some(
20,
22,
),
label: "g",
navigation_range: 598..599,
node_range: 582..604,
navigation_range: 628..629,
node_range: 612..634,
kind: SymbolKind(
Function,
),

View file

@ -764,6 +764,13 @@ trait Foo$0 { }
"#,
);
check(
r#"
trait Foo$0 = ;
//^^^
"#,
);
check(
r#"
mod bar$0 { }
@ -1065,6 +1072,23 @@ fn f() -> impl Sub<Item$0 = u8> {}
);
}
#[test]
fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() {
check(
r#"
mod bar {
pub struct Foo {}
//^^^
pub fn Foo() {}
}
fn baz() {
let _foo_enum: bar::Foo$0 = bar::Foo {};
}
"#,
)
}
#[test]
fn unknown_assoc_ty() {
check_unresolved(
@ -1406,7 +1430,6 @@ include!("included.rs$0");
);
}
#[cfg(test)]
mod goto_impl_of_trait_fn {
use super::check;
#[test]

View file

@ -14,7 +14,7 @@ use syntax::{
SyntaxNode, SyntaxToken, TextRange, T,
};
use crate::{references, NavigationTarget, TryToNav};
use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav};
#[derive(PartialEq, Eq, Hash)]
pub struct HighlightedRange {
@ -98,32 +98,39 @@ fn highlight_references(
category: access,
});
let mut res = FxHashSet::default();
let mut def_to_hl_range = |def| {
let hl_range = match def {
Definition::Module(module) => {
Some(NavigationTarget::from_module_to_decl(sema.db, module))
}
def => def.try_to_nav(sema.db),
}
.filter(|decl| decl.file_id == file_id)
.and_then(|decl| decl.focus_range)
.map(|range| {
let category =
references::decl_mutability(&def, node, range).then_some(ReferenceCategory::Write);
HighlightedRange { range, category }
});
if let Some(hl_range) = hl_range {
res.insert(hl_range);
}
};
for &def in &defs {
match def {
Definition::Local(local) => local
.associated_locals(sema.db)
.iter()
.for_each(|&local| def_to_hl_range(Definition::Local(local))),
def => def_to_hl_range(def),
Definition::Local(local) => {
let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
local
.sources(sema.db)
.into_iter()
.map(|x| x.to_nav(sema.db))
.filter(|decl| decl.file_id == file_id)
.filter_map(|decl| decl.focus_range)
.map(|range| HighlightedRange { range, category })
.for_each(|x| {
res.insert(x);
});
}
def => {
let hl_range = match def {
Definition::Module(module) => {
Some(NavigationTarget::from_module_to_decl(sema.db, module))
}
def => def.try_to_nav(sema.db),
}
.filter(|decl| decl.file_id == file_id)
.and_then(|decl| decl.focus_range)
.map(|range| {
let category = references::decl_mutability(&def, node, range)
.then_some(ReferenceCategory::Write);
HighlightedRange { range, category }
});
if let Some(hl_range) = hl_range {
res.insert(hl_range);
}
}
}
}

View file

@ -30,6 +30,7 @@ pub struct HoverConfig {
pub documentation: bool,
pub keywords: bool,
pub format: HoverDocFormat,
pub interpret_tests: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]

View file

@ -3,7 +3,8 @@ use std::fmt::Display;
use either::Either;
use hir::{
Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
MirEvalError, Semantics, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
@ -402,7 +403,20 @@ pub(super) fn definition(
))
}),
Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| {
if !config.interpret_tests {
return None;
}
match it.eval(db) {
Ok(()) => Some("pass".into()),
Err(MirEvalError::Panic) => Some("fail".into()),
Err(MirEvalError::MirLowerError(f, e)) => {
let name = &db.function_data(f).name;
Some(format!("error: fail to lower {name} due {e:?}"))
}
Err(e) => Some(format!("error: {e:?}")),
}
}),
Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
let layout = it.layout(db).ok()?;
Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
@ -410,7 +424,7 @@ pub(super) fn definition(
Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
if !it.parent_enum(db).is_data_carrying(db) {
match it.eval(db) {
Ok(x) => Some(format!("{x}")),
Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }),
Err(_) => it.value(db).map(|x| format!("{x:?}")),
}
} else {
@ -418,9 +432,9 @@ pub(super) fn definition(
}
}),
Definition::Const(it) => label_value_and_docs(db, it, |it| {
let body = it.eval(db);
let body = it.render_eval(db);
match body {
Ok(x) => Some(format!("{x}")),
Ok(x) => Some(x),
Err(_) => {
let source = it.source(db)?;
let mut body = source.value.body()?.syntax().clone();
@ -440,6 +454,7 @@ pub(super) fn definition(
Some(body.to_string())
}),
Definition::Trait(it) => label_and_docs(db, it),
Definition::TraitAlias(it) => label_and_docs(db, it),
Definition::TypeAlias(it) => label_and_docs(db, it),
Definition::BuiltinType(it) => {
return famous_defs
@ -620,8 +635,8 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
let ty = it.ty(db);
let ty = ty.display_truncated(db, None);
let is_mut = if it.is_mut(db) { "mut " } else { "" };
let desc = match it.source(db).value {
Either::Left(ident) => {
let desc = match it.primary_source(db).into_ident_pat() {
Some(ident) => {
let name = it.name(db);
let let_kw = if ident
.syntax()
@ -634,7 +649,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
};
format!("{let_kw}{is_mut}{name}: {ty}")
}
Either::Right(_) => format!("{is_mut}self: {ty}"),
None => format!("{is_mut}self: {ty}"),
};
markup(None, desc, None)
}

View file

@ -4,16 +4,19 @@ use syntax::TextRange;
use crate::{fixture, HoverConfig, HoverDocFormat};
const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
links_in_hover: false,
documentation: true,
format: HoverDocFormat::Markdown,
keywords: true,
interpret_tests: false,
};
fn check_hover_no_result(ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
&HoverConfig {
links_in_hover: true,
documentation: true,
keywords: true,
format: HoverDocFormat::Markdown,
},
&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
.unwrap();
@ -25,12 +28,7 @@ fn check(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
&HoverConfig {
links_in_hover: true,
documentation: true,
keywords: true,
format: HoverDocFormat::Markdown,
},
&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
.unwrap()
@ -47,12 +45,7 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
&HoverConfig {
links_in_hover: false,
documentation: true,
keywords: true,
format: HoverDocFormat::Markdown,
},
&HOVER_BASE_CONFIG,
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
.unwrap()
@ -71,9 +64,8 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
.hover(
&HoverConfig {
links_in_hover: true,
documentation: true,
keywords: true,
format: HoverDocFormat::PlainText,
..HOVER_BASE_CONFIG
},
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
@ -91,12 +83,7 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
let hover = analysis
.hover(
&HoverConfig {
links_in_hover: true,
documentation: true,
keywords: true,
format: HoverDocFormat::Markdown,
},
&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
FileRange { file_id, range: position.range_or_empty() },
)
.unwrap()
@ -106,34 +93,13 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
fn check_hover_range(ra_fixture: &str, expect: Expect) {
let (analysis, range) = fixture::range(ra_fixture);
let hover = analysis
.hover(
&HoverConfig {
links_in_hover: false,
documentation: true,
keywords: true,
format: HoverDocFormat::Markdown,
},
range,
)
.unwrap()
.unwrap();
let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap();
expect.assert_eq(hover.info.markup.as_str())
}
fn check_hover_range_no_results(ra_fixture: &str) {
let (analysis, range) = fixture::range(ra_fixture);
let hover = analysis
.hover(
&HoverConfig {
links_in_hover: false,
documentation: true,
keywords: true,
format: HoverDocFormat::Markdown,
},
range,
)
.unwrap();
let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
assert!(hover.is_none());
}
@ -490,7 +456,6 @@ fn hover_field_offset() {
// Hovering over the field when instantiating
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
"#,
expect![[r#"
@ -512,7 +477,6 @@ fn hover_shows_struct_field_info() {
// Hovering over the field when instantiating
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
struct Foo { field_a: u32 }
fn main() {
@ -535,7 +499,6 @@ fn main() {
// Hovering over the field in the definition
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
struct Foo { field_a$0: u32 }
fn main() {
@ -610,6 +573,27 @@ const foo$0: u32 = {
);
}
#[test]
fn hover_eval_complex_constants() {
check(
r#"
struct X { f1: (), f2: i32 }
const foo$0: (i8, X, i64) = (1, X { f2: 5 - 1, f1: () }, 1 - 2);
"#,
expect![[r#"
*foo*
```rust
test
```
```rust
const foo: (i8, X, i64) = (1, X { f1: (), f2: 4 }, -1)
```
"#]],
);
}
#[test]
fn hover_default_generic_types() {
check(
@ -1467,8 +1451,6 @@ fn my() {}
fn test_hover_struct_doc_comment() {
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
/// This is an example
/// multiline doc
///
@ -1527,7 +1509,7 @@ fn foo() { let bar = Ba$0r; }
```
```rust
struct Bar
struct Bar // size = 0, align = 1
```
---
@ -1556,7 +1538,7 @@ fn foo() { let bar = Ba$0r; }
```
```rust
struct Bar
struct Bar // size = 0, align = 1
```
---
@ -1584,7 +1566,7 @@ pub struct B$0ar
```
```rust
pub struct Bar
pub struct Bar // size = 0, align = 1
```
---
@ -1611,7 +1593,7 @@ pub struct B$0ar
```
```rust
pub struct Bar
pub struct Bar // size = 0, align = 1
```
---
@ -2913,8 +2895,6 @@ fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
fn hover_field_pat_shorthand_ref_match_ergonomics() {
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
struct S {
f: i32,
}
@ -3506,8 +3486,8 @@ impl<const LEN: usize> Foo<LEN$0> {}
}
#[test]
fn hover_const_eval_variant() {
// show hex for <10
fn hover_const_eval_discriminant() {
// Don't show hex for <10
check(
r#"
#[repr(u8)]
@ -3532,7 +3512,7 @@ enum E {
This is a doc
"#]],
);
// show hex for >10
// Show hex for >10
check(
r#"
#[repr(u8)]
@ -3656,7 +3636,7 @@ trait T {
}
impl T for i32 {
const AA: A = A {
i: 2
i: 2 + 3
}
}
fn main() {
@ -3671,9 +3651,7 @@ fn main() {
```
```rust
const AA: A = A {
i: 2
}
const AA: A = A { i: 5 }
```
"#]],
);
@ -3792,7 +3770,6 @@ const FOO$0: usize = 1 << 3;
This is a doc
"#]],
);
// show hex for >10
check(
r#"
/// This is a doc
@ -3850,7 +3827,7 @@ const FOO$0: i32 = 2 - 3;
```
```rust
const FOO: i32 = -1
const FOO: i32 = -1 (0xFFFFFFFF)
```
---
@ -4011,6 +3988,28 @@ const FOO$0: f32 = 1f32;
This is a doc
"#]],
);
// Don't show `<ref-not-supported>` in const hover
check(
r#"
/// This is a doc
const FOO$0: &i32 = &2;
"#,
expect![[r#"
*FOO*
```rust
test
```
```rust
const FOO: &i32 = &2
```
---
This is a doc
"#]],
);
//show f64 typecasted from float
check(
r#"
@ -4354,8 +4353,6 @@ fn main() {
fn hover_intra_doc_links() {
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
pub mod theitem {
/// This is the item. Cool!
pub struct TheItem;
@ -4496,7 +4493,7 @@ trait A where
fn string_shadowed_with_inner_items() {
check(
r#"
//- /main.rs crate:main deps:alloc target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
//- /main.rs crate:main deps:alloc
/// Custom `String` type.
struct String;
@ -5191,7 +5188,7 @@ foo_macro!(
```
```rust
pub struct Foo
pub struct Foo // size = 0, align = 1
```
---
@ -5205,8 +5202,6 @@ foo_macro!(
fn hover_intra_in_attr() {
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
#[doc = "Doc comment for [`Foo$0`]"]
pub struct Foo(i32);
"#,
@ -5295,7 +5290,7 @@ pub struct Type;
```
```rust
const KONST: dep::Type = $crate::Type
const KONST: dep::Type = Type
```
"#]],
);
@ -5327,8 +5322,6 @@ enum Enum {
fn hover_record_variant_field() {
check(
r#"
//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
enum Enum {
RecordV { field$0: u32 }
}
@ -5647,3 +5640,204 @@ fn main() {
"#]],
);
}
#[test]
fn assoc_fn_in_block_local_impl() {
check(
r#"
struct S;
mod m {
const _: () = {
impl crate::S {
pub(crate) fn foo() {}
}
};
}
fn test() {
S::foo$0();
}
"#,
expect![[r#"
*foo*
```rust
test::S
```
```rust
pub(crate) fn foo()
```
"#]],
);
check(
r#"
struct S;
mod m {
const _: () = {
const _: () = {
impl crate::S {
pub(crate) fn foo() {}
}
};
};
}
fn test() {
S::foo$0();
}
"#,
expect![[r#"
*foo*
```rust
test::S
```
```rust
pub(crate) fn foo()
```
"#]],
);
check(
r#"
struct S;
mod m {
mod inner {
const _: () = {
impl crate::S {
pub(super) fn foo() {}
}
};
}
fn test() {
crate::S::foo$0();
}
}
"#,
expect![[r#"
*foo*
```rust
test::S
```
```rust
pub(super) fn foo()
```
"#]],
);
}
#[test]
fn assoc_const_in_block_local_impl() {
check(
r#"
struct S;
mod m {
const _: () = {
impl crate::S {
pub(crate) const A: () = ();
}
};
}
fn test() {
S::A$0;
}
"#,
expect![[r#"
*A*
```rust
test
```
```rust
pub(crate) const A: () = ()
```
"#]],
);
check(
r#"
struct S;
mod m {
const _: () = {
const _: () = {
impl crate::S {
pub(crate) const A: () = ();
}
};
};
}
fn test() {
S::A$0;
}
"#,
expect![[r#"
*A*
```rust
test
```
```rust
pub(crate) const A: () = ()
```
"#]],
);
check(
r#"
struct S;
mod m {
mod inner {
const _: () = {
impl crate::S {
pub(super) const A: () = ();
}
};
}
fn test() {
crate::S::A$0;
}
}
"#,
expect![[r#"
*A*
```rust
test
```
```rust
pub(super) const A: () = ()
```
"#]],
);
}
#[test]
fn field_as_method_call_fallback() {
check(
r#"
struct S { f: u32 }
fn test() {
S { f: 0 }.f$0();
}
"#,
expect![[r#"
*f*
```rust
test::S
```
```rust
f: u32 // size = 4, align = 4, offset = 0
```
"#]],
);
}

View file

@ -606,14 +606,13 @@ fn a() {
}
#[test]
fn bug() {
fn let_stmt_explicit_ty() {
check_with_config(
InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
r#"
fn main() {
// These should be identical, but they are not...
let () = return;
//^^^^^^<never-to-any>
let (): () = return;
//^^^^^^<never-to-any>
}

View file

@ -176,15 +176,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir
mod tests {
// This module also contains tests for super::closure_ret
use expect_test::expect;
use syntax::{TextRange, TextSize};
use test_utils::extract_annotations;
use crate::{fixture, inlay_hints::InlayHintsConfig};
use crate::inlay_hints::tests::{
check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
};
use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
use crate::ClosureReturnTypeHints;
#[track_caller]
@ -278,8 +275,7 @@ fn main() {
#[test]
fn iterator_hint_regression_issue_12674() {
// Ensure we don't crash while solving the projection type of iterators.
check_expect(
InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
let (analysis, file_id) = fixture::file(
r#"
//- minicore: iterators
struct S<T>(T);
@ -302,107 +298,18 @@ impl<'a, T> Iterator for SliceIter<'a, T> {
fn main(a: SliceIter<'_, Container>) {
a
.filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
.map(|e| e);
.filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
.map(|e| e);
}
"#,
expect![[r#"
[
InlayHint {
range: 484..554,
kind: Chaining,
label: [
"impl ",
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
FileRange {
file_id: FileId(
1,
),
range: 2611..2619,
},
),
tooltip: "",
},
"<",
InlayHintLabelPart {
text: "Item",
linked_location: Some(
FileRange {
file_id: FileId(
1,
),
range: 2643..2647,
},
),
tooltip: "",
},
" = impl ",
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
FileRange {
file_id: FileId(
1,
),
range: 2611..2619,
},
),
tooltip: "",
},
"<",
InlayHintLabelPart {
text: "Item",
linked_location: Some(
FileRange {
file_id: FileId(
1,
),
range: 2643..2647,
},
),
tooltip: "",
},
" = &&str>>",
],
},
InlayHint {
range: 484..485,
kind: Chaining,
label: [
"",
InlayHintLabelPart {
text: "SliceIter",
linked_location: Some(
FileRange {
file_id: FileId(
0,
),
range: 289..298,
},
),
tooltip: "",
},
"<",
InlayHintLabelPart {
text: "Container",
linked_location: Some(
FileRange {
file_id: FileId(
0,
),
range: 238..247,
},
),
tooltip: "",
},
">",
],
},
]
"#]],
"#,
);
analysis
.inlay_hints(
&InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
file_id,
None,
)
.unwrap();
}
#[test]

View file

@ -435,7 +435,7 @@ fn main() {
file_id: FileId(
1,
),
range: 2611..2619,
range: 3386..3394,
},
),
tooltip: "",
@ -448,7 +448,7 @@ fn main() {
file_id: FileId(
1,
),
range: 2643..2647,
range: 3418..3422,
},
),
tooltip: "",
@ -468,7 +468,7 @@ fn main() {
file_id: FileId(
1,
),
range: 2611..2619,
range: 3386..3394,
},
),
tooltip: "",
@ -481,7 +481,7 @@ fn main() {
file_id: FileId(
1,
),
range: 2643..2647,
range: 3418..3422,
},
),
tooltip: "",
@ -501,7 +501,7 @@ fn main() {
file_id: FileId(
1,
),
range: 2611..2619,
range: 3386..3394,
},
),
tooltip: "",
@ -514,7 +514,7 @@ fn main() {
file_id: FileId(
1,
),
range: 2643..2647,
range: 3418..3422,
},
),
tooltip: "",

View file

@ -59,8 +59,14 @@ fn variant_hints(
},
kind: InlayKind::Discriminant,
label: InlayHintLabel::simple(
match &d {
Ok(v) => format!("{}", v),
match d {
Ok(x) => {
if x >= 10 {
format!("{x} ({x:#X})")
} else {
format!("{x}")
}
}
Err(_) => "?".into(),
},
Some(InlayTooltip::String(match &d {

View file

@ -55,6 +55,7 @@ mod syntax_tree;
mod typing;
mod view_crate_graph;
mod view_hir;
mod view_mir;
mod view_item_tree;
mod shuffle_crate_graph;
@ -308,6 +309,10 @@ impl Analysis {
self.with_db(|db| view_hir::view_hir(db, position))
}
pub fn view_mir(&self, position: FilePosition) -> Cancellable<String> {
self.with_db(|db| view_mir::view_mir(db, position))
}
pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
}

View file

@ -32,7 +32,7 @@ impl Markup {
pub fn as_str(&self) -> &str {
self.text.as_str()
}
pub fn fenced_block(contents: &impl fmt::Display) -> Markup {
pub fn fenced_block(contents: impl fmt::Display) -> Markup {
format!("```rust\n{contents}\n```").into()
}
}

View file

@ -208,6 +208,9 @@ pub(crate) fn def_to_moniker(
Definition::Trait(trait_) => {
MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
}
Definition::TraitAlias(ta) => {
MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type }
}
Definition::TypeAlias(ta) => {
MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
}

View file

@ -73,6 +73,7 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
SyntaxKind::MACRO_CALL,
SyntaxKind::TYPE_ALIAS,
SyntaxKind::TRAIT,
SyntaxKind::TRAIT_ALIAS,
SyntaxKind::IMPL,
SyntaxKind::MACRO_DEF,
SyntaxKind::STRUCT,

View file

@ -5,7 +5,7 @@ use std::fmt;
use either::Either;
use hir::{
symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay,
InFile, ModuleSource, Semantics,
InFile, LocalSource, ModuleSource, Semantics,
};
use ide_db::{
base_db::{FileId, FileRange},
@ -192,6 +192,7 @@ impl TryToNav for Definition {
Definition::Const(it) => it.try_to_nav(db),
Definition::Static(it) => it.try_to_nav(db),
Definition::Trait(it) => it.try_to_nav(db),
Definition::TraitAlias(it) => it.try_to_nav(db),
Definition::TypeAlias(it) => it.try_to_nav(db),
Definition::BuiltinType(_) => None,
Definition::ToolModule(_) => None,
@ -212,6 +213,7 @@ impl TryToNav for hir::ModuleDef {
hir::ModuleDef::Const(it) => it.try_to_nav(db),
hir::ModuleDef::Static(it) => it.try_to_nav(db),
hir::ModuleDef::Trait(it) => it.try_to_nav(db),
hir::ModuleDef::TraitAlias(it) => it.try_to_nav(db),
hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db),
hir::ModuleDef::Macro(it) => it.try_to_nav(db),
hir::ModuleDef::BuiltinType(_) => None,
@ -249,6 +251,9 @@ impl ToNavFromAst for hir::TypeAlias {
impl ToNavFromAst for hir::Trait {
const KIND: SymbolKind = SymbolKind::Trait;
}
impl ToNavFromAst for hir::TraitAlias {
const KIND: SymbolKind = SymbolKind::TraitAlias;
}
impl<D> TryToNav for D
where
@ -382,9 +387,11 @@ impl TryToNav for hir::GenericParam {
}
}
impl ToNav for hir::Local {
impl ToNav for LocalSource {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
let InFile { file_id, value } = self.source(db);
let InFile { file_id, value } = &self.source;
let file_id = *file_id;
let local = self.local;
let (node, name) = match &value {
Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
Either::Right(it) => (it.syntax(), it.name()),
@ -393,10 +400,10 @@ impl ToNav for hir::Local {
let FileRange { file_id, range: full_range } =
InFile::new(file_id, node).original_file_range(db);
let name = self.name(db).to_smol_str();
let kind = if self.is_self(db) {
let name = local.name(db).to_smol_str();
let kind = if local.is_self(db) {
SymbolKind::SelfParam
} else if self.is_param(db) {
} else if local.is_param(db) {
SymbolKind::ValueParam
} else {
SymbolKind::Local
@ -414,6 +421,12 @@ impl ToNav for hir::Local {
}
}
impl ToNav for hir::Local {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
self.primary_source(db).to_nav(db)
}
}
impl ToNav for hir::Label {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
let InFile { file_id, value } = self.source(db);
@ -544,6 +557,7 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),

View file

@ -1355,6 +1355,38 @@ impl Foo {
);
}
#[test]
fn test_trait_alias() {
check(
r#"
trait Foo {}
trait Bar$0 = Foo where Self: ;
fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {}
"#,
expect![[r#"
Bar TraitAlias FileId(0) 13..42 19..22
FileId(0) 53..56
FileId(0) 66..69
FileId(0) 79..82
"#]],
);
}
#[test]
fn test_trait_alias_self() {
check(
r#"
trait Foo = where Self$0: ;
"#,
expect![[r#"
Self TypeParam FileId(0) 6..9 6..9
FileId(0) 18..22
"#]],
);
}
#[test]
fn test_attr_differs_from_fn_with_same_name() {
check(

View file

@ -353,6 +353,11 @@ mod tests {
fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
let ra_fixture_after = &trim_indent(ra_fixture_after);
let (analysis, position) = fixture::position(ra_fixture_before);
if !ra_fixture_after.starts_with("error: ") {
if let Err(err) = analysis.prepare_rename(position).unwrap() {
panic!("Prepare rename to '{new_name}' was failed: {err}")
}
}
let rename_result = analysis
.rename(position, new_name)
.unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}"));
@ -1709,6 +1714,23 @@ fn foo(bar: i32) -> Foo {
);
}
#[test]
fn test_rename_local_simple() {
check(
"i",
r#"
fn foo(bar$0: i32) -> i32 {
bar
}
"#,
r#"
fn foo(i: i32) -> i32 {
i
}
"#,
);
}
#[test]
fn test_rename_local_put_init_shorthand() {
cov_mark::check!(test_rename_local_put_init_shorthand);

View file

@ -2,7 +2,7 @@ use std::fmt;
use ast::HasName;
use cfg::CfgExpr;
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
use ide_assists::utils::test_related_attribute;
use ide_db::{
base_db::{FilePosition, FileRange},
@ -195,14 +195,13 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
//
// Provides a sneak peek of all tests where the current item is used.
//
// The simplest way to use this feature is via the context menu:
// - Right-click on the selected item. The context menu opens.
// - Select **Peek related tests**
// The simplest way to use this feature is via the context menu. Right-click on
// the selected item. The context menu opens. Select **Peek Related Tests**.
//
// |===
// | Editor | Action Name
//
// | VS Code | **rust-analyzer: Peek related tests**
// | VS Code | **rust-analyzer: Peek Related Tests**
// |===
pub(crate) fn related_tests(
db: &RootDatabase,
@ -371,9 +370,9 @@ pub(crate) fn runnable_impl(
let nav = def.try_to_nav(sema.db)?;
let ty = def.self_ty(sema.db);
let adt_name = ty.as_adt()?.name(sema.db);
let mut ty_args = ty.type_arguments().peekable();
let mut ty_args = ty.generic_parameters(sema.db).peekable();
let params = if ty_args.peek().is_some() {
format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db))))
format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)))
} else {
String::new()
};
@ -417,6 +416,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
Definition::Const(it) => it.attrs(db),
Definition::Static(it) => it.attrs(db),
Definition::Trait(it) => it.attrs(db),
Definition::TraitAlias(it) => it.attrs(db),
Definition::TypeAlias(it) => it.attrs(db),
Definition::Macro(it) => it.attrs(db),
Definition::SelfType(it) => it.attrs(db),
@ -437,14 +437,10 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
let ty = imp.self_ty(db);
if let Some(adt) = ty.as_adt() {
let name = adt.name(db);
let mut ty_args = ty.type_arguments().peekable();
let mut ty_args = ty.generic_parameters(db).peekable();
format_to!(path, "{}", name);
if ty_args.peek().is_some() {
format_to!(
path,
"<{}>",
ty_args.format_with(",", |ty, cb| cb(&ty.display(db)))
);
format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
}
format_to!(path, "::{}", def_name);
path.retain(|c| c != ' ');
@ -1000,6 +996,221 @@ impl Data {
);
}
#[test]
fn test_runnables_doc_test_in_impl_with_lifetime() {
check(
r#"
//- /lib.rs
$0
fn main() {}
struct Data<'a>;
impl Data<'a> {
/// ```
/// let x = 5;
/// ```
fn foo() {}
}
"#,
&[Bin, DocTest],
expect![[r#"
[
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 1..13,
focus_range: 4..8,
name: "main",
kind: Function,
},
kind: Bin,
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 52..106,
name: "foo",
},
kind: DocTest {
test_id: Path(
"Data<'a>::foo",
),
},
cfg: None,
},
]
"#]],
);
}
#[test]
fn test_runnables_doc_test_in_impl_with_lifetime_and_types() {
check(
r#"
//- /lib.rs
$0
fn main() {}
struct Data<'a, T, U>;
impl<T, U> Data<'a, T, U> {
/// ```
/// let x = 5;
/// ```
fn foo() {}
}
"#,
&[Bin, DocTest],
expect![[r#"
[
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 1..13,
focus_range: 4..8,
name: "main",
kind: Function,
},
kind: Bin,
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 70..124,
name: "foo",
},
kind: DocTest {
test_id: Path(
"Data<'a,T,U>::foo",
),
},
cfg: None,
},
]
"#]],
);
}
#[test]
fn test_runnables_doc_test_in_impl_with_const() {
check(
r#"
//- /lib.rs
$0
fn main() {}
struct Data<const N: usize>;
impl<const N: usize> Data<N> {
/// ```
/// let x = 5;
/// ```
fn foo() {}
}
"#,
&[Bin, DocTest],
expect![[r#"
[
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 1..13,
focus_range: 4..8,
name: "main",
kind: Function,
},
kind: Bin,
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 79..133,
name: "foo",
},
kind: DocTest {
test_id: Path(
"Data<N>::foo",
),
},
cfg: None,
},
]
"#]],
);
}
#[test]
fn test_runnables_doc_test_in_impl_with_lifetime_types_and_const() {
check(
r#"
//- /lib.rs
$0
fn main() {}
struct Data<'a, T, const N: usize>;
impl<'a, T, const N: usize> Data<'a, T, N> {
/// ```
/// let x = 5;
/// ```
fn foo() {}
}
"#,
&[Bin, DocTest],
expect![[r#"
[
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 1..13,
focus_range: 4..8,
name: "main",
kind: Function,
},
kind: Bin,
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 100..154,
name: "foo",
},
kind: DocTest {
test_id: Path(
"Data<'a,T,N>::foo",
),
},
cfg: None,
},
]
"#]],
);
}
#[test]
fn test_runnables_module() {
check(
@ -2062,6 +2273,59 @@ mod tests {
);
}
#[test]
fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() {
check(
r#"
//- /lib.rs
$0
fn main() {}
struct Data<'a, A, const B: usize, C, const D: u32>;
impl<A, C, const D: u32> Data<'a, A, 12, C, D> {
/// ```
/// ```
fn foo() {}
}
"#,
&[Bin, DocTest],
expect![[r#"
[
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 1..13,
focus_range: 4..8,
name: "main",
kind: Function,
},
kind: Bin,
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 121..156,
name: "foo",
},
kind: DocTest {
test_id: Path(
"Data<'a,A,12,C,D>::foo",
),
},
cfg: None,
},
]
"#]],
);
}
#[test]
fn doc_test_type_params() {
check(

View file

@ -172,7 +172,7 @@ fn signature_help_for_call(
res.signature.push('(');
{
if let Some(self_param) = callable.receiver_param(db) {
if let Some((self_param, _)) = callable.receiver_param(db) {
format_to!(res.signature, "{}", self_param)
}
let mut buf = String::new();
@ -252,6 +252,10 @@ fn signature_help_for_generics(
res.doc = it.docs(db).map(|it| it.into());
format_to!(res.signature, "trait {}", it.name(db));
}
hir::GenericDef::TraitAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
format_to!(res.signature, "trait {}", it.name(db));
}
hir::GenericDef::TypeAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
format_to!(res.signature, "type {}", it.name(db));

View file

@ -139,6 +139,7 @@ impl StaticIndex<'_> {
documentation: true,
keywords: true,
format: crate::HoverDocFormat::Markdown,
interpret_tests: false,
};
let tokens = tokens.filter(|token| {
matches!(

View file

@ -217,7 +217,9 @@ fn highlight_name_ref(
// to anything when used.
// We can fix this for derive attributes since derive helpers are recorded, but not for
// general attributes.
None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR) => {
None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR)
&& !sema.hir_file_for(name_ref.syntax()).is_derive_attr_pseudo_expansion(sema.db) =>
{
return HlTag::Symbol(SymbolKind::Attribute).into();
}
None => return HlTag::UnresolvedReference.into(),
@ -410,6 +412,7 @@ fn highlight_def(
h
}
Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)),
Definition::TraitAlias(_) => Highlight::new(HlTag::Symbol(SymbolKind::TraitAlias)),
Definition::TypeAlias(type_) => {
let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));

View file

@ -274,6 +274,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
Definition::Const(_) => SymbolKind::Const,
Definition::Static(_) => SymbolKind::Static,
Definition::Trait(_) => SymbolKind::Trait,
Definition::TraitAlias(_) => SymbolKind::TraitAlias,
Definition::TypeAlias(_) => SymbolKind::TypeAlias,
Definition::BuiltinType(_) => return HlTag::BuiltinType,
Definition::Macro(_) => SymbolKind::Macro,

View file

@ -150,6 +150,7 @@ impl HlTag {
SymbolKind::Struct => "struct",
SymbolKind::ToolModule => "tool_module",
SymbolKind::Trait => "trait",
SymbolKind::TraitAlias => "trait_alias",
SymbolKind::TypeAlias => "type_alias",
SymbolKind::TypeParam => "type_param",
SymbolKind::Union => "union",

View file

@ -53,6 +53,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment">// This is another normal comment</span>
<span class="comment documentation">/// This is another doc comment</span>
<span class="comment">// This is another normal comment</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Copy</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Copy</span><span class="comma attribute">,</span> <span class="unresolved_reference attribute">Unresolved</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="comment">// The reason for these being here is to test AttrIds</span>
<span class="keyword">struct</span> <span class="struct declaration">Foo</span><span class="semicolon">;</span></code></pre>

View file

@ -34,7 +34,7 @@ fn attributes() {
// This is another normal comment
/// This is another doc comment
// This is another normal comment
#[derive(Copy)]
#[derive(Copy, Unresolved)]
// The reason for these being here is to test AttrIds
struct Foo;
"#,

View file

@ -0,0 +1,29 @@
use hir::{DefWithBody, Semantics};
use ide_db::base_db::FilePosition;
use ide_db::RootDatabase;
use syntax::{algo::find_node_at_offset, ast, AstNode};
// Feature: View Mir
//
// |===
// | Editor | Action Name
//
// | VS Code | **rust-analyzer: View Mir**
// |===
pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String {
body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_string())
}
fn body_mir(db: &RootDatabase, position: FilePosition) -> Option<String> {
let sema = Semantics::new(db);
let source_file = sema.parse(position.file_id);
let item = find_node_at_offset::<ast::Item>(source_file.syntax(), position.offset)?;
let def: DefWithBody = match item {
ast::Item::Fn(it) => sema.to_def(&it)?.into(),
ast::Item::Const(it) => sema.to_def(&it)?.into(),
ast::Item::Static(it) => sema.to_def(&it)?.into(),
_ => return None,
};
Some(def.debug_mir(db))
}