From 9942cc425ba31cf47ebf9498ed3cd58f064b2b89 Mon Sep 17 00:00:00 2001 From: Jonas Marcello Date: Sun, 19 Feb 2023 22:07:33 +0100 Subject: [PATCH] Fix 14142: Annotate lifetime paramaters in doctest runnables --- crates/hir-def/src/resolver.rs | 7 ++ crates/hir/src/lib.rs | 37 ++++++++++- crates/ide/src/runnables.rs | 117 +++++++++++++++++++++++++++++++-- 3 files changed, 155 insertions(+), 6 deletions(-) diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 86958e3daea4..b2323915c1fd 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -459,6 +459,13 @@ impl Resolver { }) } + pub fn generic_params(&self) -> Option<&Interned> { + self.scopes().find_map(|scope| match scope { + Scope::GenericParams { params, .. } => Some(params), + _ => None, + }) + } + pub fn body_owner(&self) -> Option { self.scopes().find_map(|scope| match scope { Scope::ExprScope(it) => Some(it.owner), diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 6206a541c1e8..28d87e14e15a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -42,7 +42,7 @@ use hir_def::{ adt::VariantData, body::{BodyDiagnostic, SyntheticSyntax}, expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId}, - generics::{TypeOrConstParamData, TypeParamProvenance}, + generics::{TypeOrConstParamData, TypeParamProvenance, LifetimeParamData}, item_tree::ItemTreeNode, lang_item::{LangItem, LangItemTarget}, layout::{Layout, LayoutError, ReprOptions}, @@ -1170,6 +1170,22 @@ impl Adt { } } + /// Returns the lifetime of the DataType + pub fn lifetime(&self, db: &dyn HirDatabase) -> Option { + let resolver = match self { + Adt::Struct(s) => s.id.resolver(db.upcast()), + Adt::Union(u) => u.id.resolver(db.upcast()), + Adt::Enum(e) => e.id.resolver(db.upcast()), + }; + resolver.generic_params().and_then(|gp| { + (&gp.lifetimes) + .iter() + // there should only be a single lifetime + // but `Arena` requires to use an iterator + .nth(0) + }).map(|arena| arena.1.clone()) + } + pub fn as_enum(&self) -> Option { if let Self::Enum(v) = self { Some(*v) @@ -3339,6 +3355,25 @@ impl Type { .map(move |ty| self.derived(ty)) } + /// Combines lifetime indicators and type arguments into a single `Vec` + pub fn lifetime_and_type_arguments<'a>(&'a self, db: &'a dyn HirDatabase) -> Vec { + let mut names = if let Some(lt) = self + .as_adt() + .and_then(|a| { + a.lifetime(db) + .and_then(|lt| Some((<.name).to_smol_str().clone())) + }) { + vec![lt] + } else { + vec![] + }; + + for ty in self.type_arguments() { + names.push(SmolStr::new(ty.display(db).to_string())) + } + names + } + pub fn iterate_method_candidates_with_traits( &self, db: &dyn HirDatabase, diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index af53adee898a..2e8f3906afc5 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -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}, @@ -370,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.lifetime_and_type_arguments(sema.db).into_iter().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() }; @@ -436,13 +436,13 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { 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.lifetime_and_type_arguments(db).into_iter().peekable(); format_to!(path, "{}", name); if ty_args.peek().is_some() { format_to!( path, "<{}>", - ty_args.format_with(",", |ty, cb| cb(&ty.display(db))) + ty_args.format_with(",", |ty, cb| cb(&ty)) ); } format_to!(path, "::{}", def_name); @@ -999,6 +999,113 @@ 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 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_module() { check(