Auto merge of #12695 - xuhongxu96:fix-12140, r=jonas-schievink

Complete type param/associated type in trait generic arg per arg index

- Fix #12140
- Also fix tidy check does not work for marks in multiline
This commit is contained in:
bors 2022-07-06 23:58:52 +00:00
commit c296e77767
5 changed files with 347 additions and 50 deletions

View File

@ -47,6 +47,7 @@ pub struct LifetimeParamData {
pub struct ConstParamData { pub struct ConstParamData {
pub name: Name, pub name: Name,
pub ty: Interned<TypeRef>, pub ty: Interned<TypeRef>,
pub has_default: bool,
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@ -70,6 +71,13 @@ impl TypeOrConstParamData {
} }
} }
pub fn has_default(&self) -> bool {
match self {
TypeOrConstParamData::TypeParamData(x) => x.default.is_some(),
TypeOrConstParamData::ConstParamData(x) => x.has_default,
}
}
pub fn type_param(&self) -> Option<&TypeParamData> { pub fn type_param(&self) -> Option<&TypeParamData> {
match self { match self {
TypeOrConstParamData::TypeParamData(x) => Some(x), TypeOrConstParamData::TypeParamData(x) => Some(x),
@ -232,7 +240,11 @@ impl GenericParams {
let ty = const_param let ty = const_param
.ty() .ty()
.map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it)); .map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
let param = ConstParamData { name, ty: Interned::new(ty) }; let param = ConstParamData {
name,
ty: Interned::new(ty),
has_default: const_param.default_val().is_some(),
};
self.type_or_consts.alloc(param.into()); self.type_or_consts.alloc(param.into());
} }
} }

View File

@ -41,6 +41,7 @@ use hir_def::{
adt::{ReprKind, VariantData}, adt::{ReprKind, VariantData},
body::{BodyDiagnostic, SyntheticSyntax}, body::{BodyDiagnostic, SyntheticSyntax},
expr::{BindingAnnotation, LabelId, Pat, PatId}, expr::{BindingAnnotation, LabelId, Pat, PatId},
generics::{TypeOrConstParamData, TypeParamProvenance},
item_tree::ItemTreeNode, item_tree::ItemTreeNode,
lang_item::LangItemTarget, lang_item::LangItemTarget,
nameres::{self, diagnostics::DefDiagnostic}, nameres::{self, diagnostics::DefDiagnostic},
@ -1707,6 +1708,26 @@ impl Trait {
pub fn is_unsafe(&self, db: &dyn HirDatabase) -> bool { pub fn is_unsafe(&self, db: &dyn HirDatabase) -> bool {
db.trait_data(self.id).is_unsafe db.trait_data(self.id).is_unsafe
} }
pub fn type_or_const_param_count(
&self,
db: &dyn HirDatabase,
count_required_only: bool,
) -> usize {
db.generic_params(GenericDefId::from(self.id))
.type_or_consts
.iter()
.filter(|(_, ty)| match ty {
TypeOrConstParamData::TypeParamData(ty)
if ty.provenance != TypeParamProvenance::TypeParamList =>
{
false
}
_ => true,
})
.filter(|(_, ty)| !count_required_only || !ty.has_default())
.count()
}
} }
impl HasVisibility for Trait { impl HasVisibility for Trait {

View File

@ -1,7 +1,7 @@
//! Completion of names from the current scope in type position. //! Completion of names from the current scope in type position.
use hir::{HirDisplay, ScopeDef}; use hir::{HirDisplay, ScopeDef};
use syntax::{ast, AstNode}; use syntax::{ast, AstNode, SyntaxKind};
use crate::{ use crate::{
context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation}, context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
@ -120,39 +120,83 @@ pub(crate) fn complete_type_path(
} }
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
Qualified::No => { Qualified::No => {
acc.add_nameref_keywords_with_colon(ctx); match location {
if let TypeLocation::TypeBound = location { TypeLocation::TypeBound => {
ctx.process_all_names(&mut |name, res| { acc.add_nameref_keywords_with_colon(ctx);
let add_resolution = match res { ctx.process_all_names(&mut |name, res| {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), let add_resolution = match res {
ScopeDef::ModuleDef( ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_), mac.is_fn_like(ctx.db)
) => true, }
_ => false, ScopeDef::ModuleDef(
}; hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_),
if add_resolution { ) => true,
acc.add_path_resolution(ctx, path_ctx, name, res); _ => false,
} };
}); if add_resolution {
return; acc.add_path_resolution(ctx, path_ctx, name, res);
} }
if let TypeLocation::GenericArgList(Some(arg_list)) = location { });
if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) return;
{ }
if path_seg.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { TypeLocation::GenericArgList(Some(arg_list)) => {
if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) = let in_assoc_type_arg = ctx
ctx.sema.resolve_path(&path_seg.parent_path()) .original_token
.parent_ancestors()
.any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);
if !in_assoc_type_arg {
if let Some(path_seg) =
arg_list.syntax().parent().and_then(ast::PathSegment::cast)
{ {
trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| { if path_seg
if let hir::AssocItem::TypeAlias(alias) = it { .syntax()
cov_mark::hit!(complete_assoc_type_in_generics_list); .ancestors()
acc.add_type_alias_with_eq(ctx, alias) .find_map(ast::TypeBound::cast)
.is_some()
{
if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
trait_,
))) = ctx.sema.resolve_path(&path_seg.parent_path())
{
let arg_idx = arg_list
.generic_args()
.filter(|arg| {
arg.syntax().text_range().end()
< ctx.original_token.text_range().start()
})
.count();
let n_required_params =
trait_.type_or_const_param_count(ctx.sema.db, true);
if arg_idx >= n_required_params {
trait_
.items_with_supertraits(ctx.sema.db)
.into_iter()
.for_each(|it| {
if let hir::AssocItem::TypeAlias(alias) = it {
cov_mark::hit!(
complete_assoc_type_in_generics_list
);
acc.add_type_alias_with_eq(ctx, alias);
}
});
let n_params =
trait_.type_or_const_param_count(ctx.sema.db, false);
if arg_idx >= n_params {
return; // only show assoc types
}
}
} }
}); }
} }
} }
} }
} _ => {}
};
acc.add_nameref_keywords_with_colon(ctx);
ctx.process_all_names(&mut |name, def| { ctx.process_all_names(&mut |name, def| {
if scope_def_applicable(def) { if scope_def_applicable(def) {
acc.add_path_resolution(ctx, path_ctx, name, def); acc.add_path_resolution(ctx, path_ctx, name, def);

View File

@ -377,13 +377,29 @@ trait Trait2: Trait1 {
type Foo; type Foo;
} }
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r#"
ta Foo = (as Trait2) type Foo
ta Super = (as Trait1) type Super
"#]],
);
check(
r#"
trait Trait1 {
type Super;
}
trait Trait2<T>: Trait1 {
type Foo;
}
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#, "#,
expect![[r#" expect![[r#"
ct CONST ct CONST
cp CONST_PARAM cp CONST_PARAM
en Enum en Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record st Record
st Tuple st Tuple
@ -391,8 +407,6 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
tt Trait tt Trait
tt Trait1 tt Trait1
tt Trait2 tt Trait2
ta Foo = (as Trait2) type Foo
ta Super = (as Trait1) type Super
tp T tp T
un Union un Union
bt u32 bt u32
@ -472,3 +486,206 @@ fn func(_: Enum::$0) {}
"#]], "#]],
); );
} }
#[test]
fn completes_type_parameter_or_associated_type() {
check(
r#"
trait MyTrait<T, U> {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<u$0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
tt MyTrait
tt Trait
un Union
bt u32
kw crate::
kw self::
kw super::
"#]],
);
check(
r#"
trait MyTrait<T, U> {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<u8, u$0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
tt MyTrait
tt Trait
un Union
bt u32
kw crate::
kw self::
kw super::
"#]],
);
check(
r#"
trait MyTrait<T, U> {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<u8, u8, I$0
"#,
expect![[r#"
ta Item1 = (as MyTrait) type Item1
ta Item2 = (as MyTrait) type Item2
"#]],
);
}
#[test]
fn completes_type_parameter_or_associated_type_with_default_value() {
check(
r#"
trait MyTrait<T, U = u8> {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<u$0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
tt MyTrait
tt Trait
un Union
bt u32
kw crate::
kw self::
kw super::
"#]],
);
check(
r#"
trait MyTrait<T, U = u8> {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<u8, u$0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
tt MyTrait
tt Trait
ta Item1 = (as MyTrait) type Item1
ta Item2 = (as MyTrait) type Item2
un Union
bt u32
kw crate::
kw self::
kw super::
"#]],
);
check(
r#"
trait MyTrait<T, U = u8> {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<u8, u8, I$0
"#,
expect![[r#"
ta Item1 = (as MyTrait) type Item1
ta Item2 = (as MyTrait) type Item2
"#]],
);
}
#[test]
fn completes_types_after_associated_type() {
check(
r#"
trait MyTrait {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<Item1 = $0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
tt MyTrait
tt Trait
un Union
bt u32
kw crate::
kw self::
kw super::
"#]],
);
check(
r#"
trait MyTrait {
type Item1;
type Item2;
};
fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
tt MyTrait
tt Trait
un Union
bt u32
kw crate::
kw self::
kw super::
"#]],
);
}

View File

@ -471,17 +471,9 @@ struct TidyMarks {
impl TidyMarks { impl TidyMarks {
fn visit(&mut self, _path: &Path, text: &str) { fn visit(&mut self, _path: &Path, text: &str) {
for line in text.lines() { find_marks(&mut self.hits, text, "hit");
if let Some(mark) = find_mark(line, "hit") { find_marks(&mut self.checks, text, "check");
self.hits.insert(mark.to_string()); find_marks(&mut self.checks, text, "check_count");
}
if let Some(mark) = find_mark(line, "check") {
self.checks.insert(mark.to_string());
}
if let Some(mark) = find_mark(line, "check_count") {
self.checks.insert(mark.to_string());
}
}
} }
fn finish(self) { fn finish(self) {
@ -506,10 +498,21 @@ fn stable_hash(text: &str) -> u64 {
hasher.finish() hasher.finish()
} }
fn find_mark<'a>(text: &'a str, mark: &'static str) -> Option<&'a str> { fn find_marks(set: &mut HashSet<String>, text: &str, mark: &str) {
let idx = text.find(mark)?; let mut text = text;
let text = text[idx + mark.len()..].strip_prefix("!(")?; let mut prev_text = "";
let idx = text.find(|c: char| !(c.is_alphanumeric() || c == '_'))?; while text != prev_text {
let text = &text[..idx]; prev_text = text;
Some(text) if let Some(idx) = text.find(mark) {
text = &text[idx + mark.len()..];
if let Some(stripped_text) = text.strip_prefix("!(") {
text = stripped_text.trim_start();
if let Some(idx2) = text.find(|c: char| !(c.is_alphanumeric() || c == '_')) {
let mark_text = &text[..idx2];
set.insert(mark_text.to_string());
text = &text[idx2..];
}
}
}
}
} }