Skip to content

Commit 9942cc4

Browse files
committed
Fix 14142: Annotate lifetime paramaters in doctest runnables
1 parent c867cbf commit 9942cc4

File tree

3 files changed

+155
-6
lines changed

3 files changed

+155
-6
lines changed

crates/hir-def/src/resolver.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,13 @@ impl Resolver {
459459
})
460460
}
461461

462+
pub fn generic_params(&self) -> Option<&Interned<GenericParams>> {
463+
self.scopes().find_map(|scope| match scope {
464+
Scope::GenericParams { params, .. } => Some(params),
465+
_ => None,
466+
})
467+
}
468+
462469
pub fn body_owner(&self) -> Option<DefWithBodyId> {
463470
self.scopes().find_map(|scope| match scope {
464471
Scope::ExprScope(it) => Some(it.owner),

crates/hir/src/lib.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use hir_def::{
4242
adt::VariantData,
4343
body::{BodyDiagnostic, SyntheticSyntax},
4444
expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId},
45-
generics::{TypeOrConstParamData, TypeParamProvenance},
45+
generics::{TypeOrConstParamData, TypeParamProvenance, LifetimeParamData},
4646
item_tree::ItemTreeNode,
4747
lang_item::{LangItem, LangItemTarget},
4848
layout::{Layout, LayoutError, ReprOptions},
@@ -1170,6 +1170,22 @@ impl Adt {
11701170
}
11711171
}
11721172

1173+
/// Returns the lifetime of the DataType
1174+
pub fn lifetime(&self, db: &dyn HirDatabase) -> Option<LifetimeParamData> {
1175+
let resolver = match self {
1176+
Adt::Struct(s) => s.id.resolver(db.upcast()),
1177+
Adt::Union(u) => u.id.resolver(db.upcast()),
1178+
Adt::Enum(e) => e.id.resolver(db.upcast()),
1179+
};
1180+
resolver.generic_params().and_then(|gp| {
1181+
(&gp.lifetimes)
1182+
.iter()
1183+
// there should only be a single lifetime
1184+
// but `Arena` requires to use an iterator
1185+
.nth(0)
1186+
}).map(|arena| arena.1.clone())
1187+
}
1188+
11731189
pub fn as_enum(&self) -> Option<Enum> {
11741190
if let Self::Enum(v) = self {
11751191
Some(*v)
@@ -3339,6 +3355,25 @@ impl Type {
33393355
.map(move |ty| self.derived(ty))
33403356
}
33413357

3358+
/// Combines lifetime indicators and type arguments into a single `Vec<SmolStr>`
3359+
pub fn lifetime_and_type_arguments<'a>(&'a self, db: &'a dyn HirDatabase) -> Vec<SmolStr> {
3360+
let mut names = if let Some(lt) = self
3361+
.as_adt()
3362+
.and_then(|a| {
3363+
a.lifetime(db)
3364+
.and_then(|lt| Some((&lt.name).to_smol_str().clone()))
3365+
}) {
3366+
vec![lt]
3367+
} else {
3368+
vec![]
3369+
};
3370+
3371+
for ty in self.type_arguments() {
3372+
names.push(SmolStr::new(ty.display(db).to_string()))
3373+
}
3374+
names
3375+
}
3376+
33423377
pub fn iterate_method_candidates_with_traits<T>(
33433378
&self,
33443379
db: &dyn HirDatabase,

crates/ide/src/runnables.rs

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt;
22

33
use ast::HasName;
44
use cfg::CfgExpr;
5-
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
5+
use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
66
use ide_assists::utils::test_related_attribute;
77
use ide_db::{
88
base_db::{FilePosition, FileRange},
@@ -370,9 +370,9 @@ pub(crate) fn runnable_impl(
370370
let nav = def.try_to_nav(sema.db)?;
371371
let ty = def.self_ty(sema.db);
372372
let adt_name = ty.as_adt()?.name(sema.db);
373-
let mut ty_args = ty.type_arguments().peekable();
373+
let mut ty_args = ty.lifetime_and_type_arguments(sema.db).into_iter().peekable();
374374
let params = if ty_args.peek().is_some() {
375-
format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db))))
375+
format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)))
376376
} else {
377377
String::new()
378378
};
@@ -436,13 +436,13 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
436436
let ty = imp.self_ty(db);
437437
if let Some(adt) = ty.as_adt() {
438438
let name = adt.name(db);
439-
let mut ty_args = ty.type_arguments().peekable();
439+
let mut ty_args = ty.lifetime_and_type_arguments(db).into_iter().peekable();
440440
format_to!(path, "{}", name);
441441
if ty_args.peek().is_some() {
442442
format_to!(
443443
path,
444444
"<{}>",
445-
ty_args.format_with(",", |ty, cb| cb(&ty.display(db)))
445+
ty_args.format_with(",", |ty, cb| cb(&ty))
446446
);
447447
}
448448
format_to!(path, "::{}", def_name);
@@ -999,6 +999,113 @@ impl Data {
999999
);
10001000
}
10011001

1002+
#[test]
1003+
fn test_runnables_doc_test_in_impl_with_lifetime() {
1004+
check(
1005+
r#"
1006+
//- /lib.rs
1007+
$0
1008+
fn main() {}
1009+
1010+
struct Data<'a>;
1011+
impl Data<'a> {
1012+
/// ```
1013+
/// let x = 5;
1014+
/// ```
1015+
fn foo() {}
1016+
}
1017+
"#,
1018+
&[Bin, DocTest],
1019+
expect![[r#"
1020+
[
1021+
Runnable {
1022+
use_name_in_title: false,
1023+
nav: NavigationTarget {
1024+
file_id: FileId(
1025+
0,
1026+
),
1027+
full_range: 1..13,
1028+
focus_range: 4..8,
1029+
name: "main",
1030+
kind: Function,
1031+
},
1032+
kind: Bin,
1033+
cfg: None,
1034+
},
1035+
Runnable {
1036+
use_name_in_title: false,
1037+
nav: NavigationTarget {
1038+
file_id: FileId(
1039+
0,
1040+
),
1041+
full_range: 52..106,
1042+
name: "foo",
1043+
},
1044+
kind: DocTest {
1045+
test_id: Path(
1046+
"Data<'a>::foo",
1047+
),
1048+
},
1049+
cfg: None,
1050+
},
1051+
]
1052+
"#]],
1053+
);
1054+
}
1055+
1056+
#[test]
1057+
fn test_runnables_doc_test_in_impl_with_lifetime_and_types() {
1058+
check(
1059+
r#"
1060+
//- /lib.rs
1061+
$0
1062+
fn main() {}
1063+
1064+
struct Data<'a, T, U>;
1065+
impl<T, U> Data<'a, T, U> {
1066+
/// ```
1067+
/// let x = 5;
1068+
/// ```
1069+
fn foo() {}
1070+
}
1071+
"#,
1072+
&[Bin, DocTest],
1073+
expect![[r#"
1074+
[
1075+
Runnable {
1076+
use_name_in_title: false,
1077+
nav: NavigationTarget {
1078+
file_id: FileId(
1079+
0,
1080+
),
1081+
full_range: 1..13,
1082+
focus_range: 4..8,
1083+
name: "main",
1084+
kind: Function,
1085+
},
1086+
kind: Bin,
1087+
cfg: None,
1088+
},
1089+
Runnable {
1090+
use_name_in_title: false,
1091+
nav: NavigationTarget {
1092+
file_id: FileId(
1093+
0,
1094+
),
1095+
full_range: 70..124,
1096+
name: "foo",
1097+
},
1098+
kind: DocTest {
1099+
test_id: Path(
1100+
"Data<'a,T,U>::foo",
1101+
),
1102+
},
1103+
cfg: None,
1104+
},
1105+
]
1106+
"#]],
1107+
);
1108+
}
10021109
#[test]
10031110
fn test_runnables_module() {
10041111
check(

0 commit comments

Comments
 (0)