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:
commit
c296e77767
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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::
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -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..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user