Skip to content

Commit ce3db6c

Browse files
bors[bot]matklad
andauthored
Merge #6919
6919: Rewrite doctest runnables r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 4368a3b + c888f1d commit ce3db6c

File tree

2 files changed

+89
-121
lines changed

2 files changed

+89
-121
lines changed

crates/ide/src/display/navigation_target.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -117,25 +117,6 @@ impl NavigationTarget {
117117
)
118118
}
119119

120-
/// Allows `NavigationTarget` to be created from a `DocCommentsOwner` and a `NameOwner`
121-
pub(crate) fn from_doc_commented(
122-
db: &RootDatabase,
123-
named: InFile<&dyn ast::NameOwner>,
124-
node: InFile<&dyn ast::DocCommentsOwner>,
125-
) -> NavigationTarget {
126-
let name =
127-
named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
128-
let frange = node.map(|it| it.syntax()).original_file_range(db);
129-
130-
NavigationTarget::from_syntax(
131-
frange.file_id,
132-
name,
133-
None,
134-
frange.range,
135-
node.value.syntax().kind(),
136-
)
137-
}
138-
139120
fn from_syntax(
140121
file_id: FileId,
141122
name: SmolStr,

crates/ide/src/runnables.rs

Lines changed: 89 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use syntax::{
1010
match_ast, SyntaxNode,
1111
};
1212

13-
use crate::{display::ToNav, FileId, NavigationTarget};
13+
use crate::{
14+
display::{ToNav, TryToNav},
15+
FileId, NavigationTarget,
16+
};
1417

1518
#[derive(Debug, Clone)]
1619
pub struct Runnable {
@@ -101,125 +104,109 @@ pub(crate) fn runnable(
101104
item: SyntaxNode,
102105
file_id: FileId,
103106
) -> Option<Runnable> {
104-
match_ast! {
105-
match item {
106-
ast::Struct(it) => runnable_struct(sema, it, file_id),
107+
let runnable_item = match_ast! {
108+
match (item.clone()) {
107109
ast::Fn(it) => runnable_fn(sema, it, file_id),
108110
ast::Module(it) => runnable_mod(sema, it),
109111
_ => None,
110112
}
111-
}
113+
};
114+
runnable_item.or_else(|| runnable_doctest(sema, item))
112115
}
113116

114-
fn runnable_fn(
115-
sema: &Semantics<RootDatabase>,
116-
fn_def: ast::Fn,
117-
file_id: FileId,
118-
) -> Option<Runnable> {
119-
let def = sema.to_def(&fn_def)?;
120-
let name_string = fn_def.name()?.text().to_string();
117+
fn runnable_fn(sema: &Semantics<RootDatabase>, func: ast::Fn, file_id: FileId) -> Option<Runnable> {
118+
let def = sema.to_def(&func)?;
119+
let name_string = func.name()?.text().to_string();
121120

122-
let attrs = def.attrs(sema.db);
123121
let kind = if name_string == "main" {
124122
RunnableKind::Bin
125123
} else {
126-
let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
127-
Some(module) => {
128-
let def = sema.to_def(&fn_def)?;
129-
let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| {
130-
match assoc_item.container(sema.db) {
131-
hir::AssocItemContainer::Trait(trait_item) => {
132-
Some(trait_item.name(sema.db).to_string())
133-
}
134-
hir::AssocItemContainer::Impl(impl_def) => impl_def
135-
.target_ty(sema.db)
136-
.as_adt()
137-
.map(|adt| adt.name(sema.db).to_string()),
138-
}
139-
});
140-
141-
let path_iter = module
142-
.path_to_root(sema.db)
143-
.into_iter()
144-
.rev()
145-
.filter_map(|it| it.name(sema.db))
146-
.map(|name| name.to_string());
147-
148-
let path = if let Some(impl_trait_name) = impl_trait_name {
149-
path_iter
150-
.chain(std::iter::once(impl_trait_name))
151-
.chain(std::iter::once(name_string))
152-
.join("::")
153-
} else {
154-
path_iter.chain(std::iter::once(name_string)).join("::")
155-
};
156-
157-
TestId::Path(path)
158-
}
159-
None => TestId::Name(name_string),
160-
};
161-
162-
if test_related_attribute(&fn_def).is_some() {
163-
let attr = TestAttr::from_fn(&fn_def);
124+
let canonical_path = sema.to_def(&func).and_then(|def| {
125+
let def: hir::ModuleDef = def.into();
126+
def.canonical_path(sema.db)
127+
});
128+
let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string));
129+
130+
if test_related_attribute(&func).is_some() {
131+
let attr = TestAttr::from_fn(&func);
164132
RunnableKind::Test { test_id, attr }
165-
} else if fn_def.has_atom_attr("bench") {
133+
} else if func.has_atom_attr("bench") {
166134
RunnableKind::Bench { test_id }
167-
} else if has_runnable_doc_test(&attrs) {
168-
RunnableKind::DocTest { test_id }
169135
} else {
170136
return None;
171137
}
172138
};
173139

174-
let cfg = attrs.cfg();
175-
176-
let nav = if let RunnableKind::DocTest { .. } = kind {
177-
NavigationTarget::from_doc_commented(
178-
sema.db,
179-
InFile::new(file_id.into(), &fn_def),
180-
InFile::new(file_id.into(), &fn_def),
181-
)
182-
} else {
183-
NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def))
184-
};
140+
let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &func));
141+
let cfg = def.attrs(sema.db).cfg();
185142
Some(Runnable { nav, kind, cfg })
186143
}
187144

188-
fn runnable_struct(
189-
sema: &Semantics<RootDatabase>,
190-
struct_def: ast::Struct,
191-
file_id: FileId,
192-
) -> Option<Runnable> {
193-
let def = sema.to_def(&struct_def)?;
194-
let name_string = struct_def.name()?.text().to_string();
145+
fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
146+
match_ast! {
147+
match item {
148+
ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
149+
ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
150+
ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
151+
ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
152+
ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
153+
ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
154+
ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
155+
ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
156+
_ => None,
157+
}
158+
}
159+
}
195160

196-
let attrs = def.attrs(sema.db);
161+
fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> {
162+
let attrs = match def {
163+
hir::ModuleDef::Module(it) => it.attrs(sema.db),
164+
hir::ModuleDef::Function(it) => it.attrs(sema.db),
165+
hir::ModuleDef::Adt(it) => it.attrs(sema.db),
166+
hir::ModuleDef::EnumVariant(it) => it.attrs(sema.db),
167+
hir::ModuleDef::Const(it) => it.attrs(sema.db),
168+
hir::ModuleDef::Static(it) => it.attrs(sema.db),
169+
hir::ModuleDef::Trait(it) => it.attrs(sema.db),
170+
hir::ModuleDef::TypeAlias(it) => it.attrs(sema.db),
171+
hir::ModuleDef::BuiltinType(_) => return None,
172+
};
197173
if !has_runnable_doc_test(&attrs) {
198174
return None;
199175
}
200-
let cfg = attrs.cfg();
201-
202-
let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) {
203-
Some(module) => {
204-
let path_iter = module
205-
.path_to_root(sema.db)
206-
.into_iter()
207-
.rev()
208-
.filter_map(|it| it.name(sema.db))
209-
.map(|name| name.to_string());
210-
let path = path_iter.chain(std::iter::once(name_string)).join("::");
211-
212-
TestId::Path(path)
213-
}
214-
None => TestId::Name(name_string),
215-
};
216-
217-
let nav = NavigationTarget::from_doc_commented(
218-
sema.db,
219-
InFile::new(file_id.into(), &struct_def),
220-
InFile::new(file_id.into(), &struct_def),
221-
);
222-
Some(Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg })
176+
let def_name = def.name(sema.db).map(|it| it.to_string());
177+
let test_id = def
178+
.canonical_path(sema.db)
179+
// This probably belongs to canonical path?
180+
.map(|path| {
181+
let assoc_def = match def {
182+
hir::ModuleDef::Function(it) => it.as_assoc_item(sema.db),
183+
hir::ModuleDef::Const(it) => it.as_assoc_item(sema.db),
184+
hir::ModuleDef::TypeAlias(it) => it.as_assoc_item(sema.db),
185+
_ => None,
186+
};
187+
// FIXME: this also looks very wrong
188+
if let Some(assoc_def) = assoc_def {
189+
if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
190+
if let Some(adt) = imp.target_ty(sema.db).as_adt() {
191+
let name = adt.name(sema.db).to_string();
192+
let idx = path.rfind(':').unwrap_or(0);
193+
let (prefix, suffix) = path.split_at(idx);
194+
return format!("{}{}::{}", prefix, name, suffix);
195+
}
196+
}
197+
}
198+
path
199+
})
200+
.map(TestId::Path)
201+
.or_else(|| def_name.clone().map(TestId::Name))?;
202+
203+
let mut nav = def.try_to_nav(sema.db)?;
204+
nav.focus_range = None;
205+
nav.description = None;
206+
nav.docs = None;
207+
nav.kind = syntax::SyntaxKind::COMMENT;
208+
let res = Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg: attrs.cfg() };
209+
Some(res)
223210
}
224211

225212
#[derive(Debug, Copy, Clone)]
@@ -317,7 +304,7 @@ mod tests {
317304

318305
use crate::fixture;
319306

320-
use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST};
307+
use super::*;
321308

322309
fn check(
323310
ra_fixture: &str,
@@ -546,7 +533,7 @@ struct StructWithRunnable(String);
546533
full_range: 15..74,
547534
focus_range: None,
548535
name: "should_have_runnable",
549-
kind: FN,
536+
kind: COMMENT,
550537
container_name: None,
551538
description: None,
552539
docs: None,
@@ -566,7 +553,7 @@ struct StructWithRunnable(String);
566553
full_range: 76..148,
567554
focus_range: None,
568555
name: "should_have_runnable_1",
569-
kind: FN,
556+
kind: COMMENT,
570557
container_name: None,
571558
description: None,
572559
docs: None,
@@ -586,7 +573,7 @@ struct StructWithRunnable(String);
586573
full_range: 150..254,
587574
focus_range: None,
588575
name: "should_have_runnable_2",
589-
kind: FN,
576+
kind: COMMENT,
590577
container_name: None,
591578
description: None,
592579
docs: None,
@@ -606,7 +593,7 @@ struct StructWithRunnable(String);
606593
full_range: 756..821,
607594
focus_range: None,
608595
name: "StructWithRunnable",
609-
kind: STRUCT,
596+
kind: COMMENT,
610597
container_name: None,
611598
description: None,
612599
docs: None,
@@ -668,7 +655,7 @@ impl Data {
668655
full_range: 44..98,
669656
focus_range: None,
670657
name: "foo",
671-
kind: FN,
658+
kind: COMMENT,
672659
container_name: None,
673660
description: None,
674661
docs: None,

0 commit comments

Comments
 (0)