Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add static method tactic
Browse files Browse the repository at this point in the history
kilpkonn committed Dec 18, 2023
1 parent b00ff5f commit 4da835f
Showing 8 changed files with 569 additions and 93 deletions.
39 changes: 24 additions & 15 deletions crates/hir-def/src/attr.rs
Original file line number Diff line number Diff line change
@@ -418,27 +418,36 @@ impl AttrsWithOwner {
AttrDefId::GenericParamId(it) => match it {
GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db);
RawAttrs::from_attrs_owner(
db.upcast(),
src.with_value(&src.value[it.local_id()]),
db.span_map(src.file_id).as_ref(),
)
match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner(
db.upcast(),
src.with_value(val),
db.span_map(src.file_id).as_ref(),
),
None => RawAttrs::EMPTY,
}
}
GenericParamId::TypeParamId(it) => {
let src = it.parent().child_source(db);
RawAttrs::from_attrs_owner(
db.upcast(),
src.with_value(&src.value[it.local_id()]),
db.span_map(src.file_id).as_ref(),
)
match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner(
db.upcast(),
src.with_value(val),
db.span_map(src.file_id).as_ref(),
),
None => RawAttrs::EMPTY,
}
}
GenericParamId::LifetimeParamId(it) => {
let src = it.parent.child_source(db);
RawAttrs::from_attrs_owner(
db.upcast(),
src.with_value(&src.value[it.local_id]),
db.span_map(src.file_id).as_ref(),
)
match src.value.get(it.local_id) {
Some(val) => RawAttrs::from_attrs_owner(
db.upcast(),
src.with_value(val),
db.span_map(src.file_id).as_ref(),
),
None => RawAttrs::EMPTY,
}
}
},
AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
102 changes: 100 additions & 2 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1170,6 +1170,10 @@ impl Struct {
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
db.struct_data(self.id).variant_data.clone()
}

pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
db.attrs(self.id.into()).is_unstable()
}
}

impl HasVisibility for Struct {
@@ -1212,6 +1216,10 @@ impl Union {
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
db.union_data(self.id).variant_data.clone()
}

pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
db.attrs(self.id.into()).is_unstable()
}
}

impl HasVisibility for Union {
@@ -1301,6 +1309,10 @@ impl Enum {
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
Adt::from(self).layout(db)
}

pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
db.attrs(self.id.into()).is_unstable()
}
}

impl HasVisibility for Enum {
@@ -1373,6 +1385,10 @@ impl Variant {
_ => parent_layout,
})
}

pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
db.variants_attrs(self.parent.into())[self.id].is_unstable()
}
}

/// Variants inherit visibility from the parent enum.
@@ -3081,7 +3097,7 @@ impl GenericDef {
.collect()
}

pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
pub fn type_or_const_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
let generics = db.generic_params(self.into());
generics
.type_or_consts
@@ -3091,6 +3107,40 @@ impl GenericDef {
})
.collect()
}

pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
let generics = db.generic_params(self.into());
generics
.type_or_consts
.iter()
.filter_map(|(local_id, data)| match data {
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => Some(TypeParam {
id: TypeParamId::from_unchecked(TypeOrConstParamId {
parent: self.into(),
local_id,
}),
}),
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => None,
})
.collect()
}

pub fn const_params(self, db: &dyn HirDatabase) -> Vec<ConstParam> {
let generics = db.generic_params(self.into());
generics
.type_or_consts
.iter()
.filter_map(|(local_id, data)| match data {
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => None,
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => Some(ConstParam {
id: ConstParamId::from_unchecked(TypeOrConstParamId {
parent: self.into(),
local_id,
}),
}),
})
.collect()
}
}

/// A single local definition.
@@ -3452,12 +3502,16 @@ impl TypeParam {
let ty = generic_arg_from_param(db, self.id.into())?;
let resolver = self.id.parent().resolver(db.upcast());
match ty.data(Interner) {
GenericArgData::Ty(it) => {
GenericArgData::Ty(it) if *it.kind(Interner) != TyKind::Error => {
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
}
_ => None,
}
}

pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
db.attrs(GenericParamId::from(self.id).into()).is_unstable()
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -3940,6 +3994,50 @@ impl Type {
matches!(self.ty.kind(Interner), TyKind::Ref(..))
}

pub fn contains_reference(&self, db: &dyn HirDatabase) -> bool {
return go(db, self.env.krate, &self.ty);

fn go(db: &dyn HirDatabase, krate: CrateId, ty: &Ty) -> bool {
match ty.kind(Interner) {
// Reference itself
TyKind::Ref(_, _, _) => true,

// For non-phantom_data adts we check variants/fields as well as generic parameters
TyKind::Adt(adt_id, substitution)
if !db.struct_datum(krate, *adt_id).flags.phantom_data =>
{
let adt_datum = &db.struct_datum(krate, *adt_id);
let adt_datum_bound =
adt_datum.binders.clone().substitute(Interner, substitution);
adt_datum_bound
.variants
.into_iter()
.flat_map(|variant| variant.fields.into_iter())
.any(|ty| go(db, krate, &ty))
|| substitution
.iter(Interner)
.filter_map(|x| x.ty(Interner))
.any(|ty| go(db, krate, ty))
}
// And for `PhantomData<T>`, we check `T`.
TyKind::Adt(_, substitution)
| TyKind::Tuple(_, substitution)
| TyKind::OpaqueType(_, substitution)
| TyKind::AssociatedType(_, substitution)
| TyKind::FnDef(_, substitution) => substitution
.iter(Interner)
.filter_map(|x| x.ty(Interner))
.any(|ty| go(db, krate, ty)),

// For `[T]` or `*T` we check `T`
TyKind::Array(ty, _) | TyKind::Slice(ty) | TyKind::Raw(_, ty) => go(db, krate, ty),

// Consider everything else as not reference
_ => false,
}
}
}

pub fn as_reference(&self) -> Option<(Type, Mutability)> {
let (ty, _lt, m) = self.ty.as_reference()?;
let m = Mutability::from_mutable(matches!(m, hir_ty::Mutability::Mut));
7 changes: 7 additions & 0 deletions crates/hir/src/term_search/mod.rs
Original file line number Diff line number Diff line change
@@ -47,6 +47,8 @@ struct LookupTable {
round_scopedef_hits: FxHashSet<ScopeDef>,
/// Amount of rounds since scopedef was first used.
rounds_since_sopedef_hit: FxHashMap<ScopeDef, u32>,
/// Types queried but not present
types_wishlist: FxHashSet<Type>,
}

impl LookupTable {
@@ -149,6 +151,10 @@ impl LookupTable {
fn exhausted_scopedefs(&self) -> &FxHashSet<ScopeDef> {
&self.exhausted_scopedefs
}

fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
std::mem::take(&mut self.types_wishlist)
}
}

/// # Term search
@@ -205,6 +211,7 @@ pub fn term_search<DB: HirDatabase>(
solutions.extend(tactics::free_function(sema.db, &module, &defs, &mut lookup, goal));
solutions.extend(tactics::impl_method(sema.db, &module, &defs, &mut lookup, goal));
solutions.extend(tactics::struct_projection(sema.db, &module, &defs, &mut lookup, goal));
solutions.extend(tactics::impl_static_method(sema.db, &module, &defs, &mut lookup, goal));

// Break after 1 round after successful solution
if solution_found {
348 changes: 303 additions & 45 deletions crates/hir/src/term_search/tactics.rs

Large diffs are not rendered by default.

115 changes: 91 additions & 24 deletions crates/hir/src/term_search/type_tree.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
//! Type tree for term search
use hir_def::find_path::PrefixKind;
use hir_expand::mod_path::ModPath;
use hir_ty::{db::HirDatabase, display::HirDisplay};
use itertools::Itertools;

use crate::{
Adt, AsAssocItem, Const, ConstParam, Field, Function, Local, ModuleDef, SemanticsScope, Static,
Struct, StructKind, Trait, Type, Variant,
Adt, AsAssocItem, Const, ConstParam, Field, Function, GenericDef, Local, ModuleDef,
SemanticsScope, Static, Struct, StructKind, Trait, Type, Variant,
};

/// Helper function to prefix items with modules when required
fn mod_item_path(db: &dyn HirDatabase, sema_scope: &SemanticsScope<'_>, def: &ModuleDef) -> String {
/// Helper function to get path to `ModuleDef`
fn mod_item_path(sema_scope: &SemanticsScope<'_>, def: &ModuleDef) -> Option<ModPath> {
let db = sema_scope.db;
// Account for locals shadowing items from module
let name_hit_count = def.name(db).map(|def_name| {
let mut name_hit_count = 0;
@@ -23,12 +25,45 @@ fn mod_item_path(db: &dyn HirDatabase, sema_scope: &SemanticsScope<'_>, def: &Mo
});

let m = sema_scope.module();
let path = match name_hit_count {
match name_hit_count {
Some(0..=1) | None => m.find_use_path(db.upcast(), *def, false, true),
Some(_) => m.find_use_path_prefixed(db.upcast(), *def, PrefixKind::ByCrate, false, true),
};
}
}

/// Helper function to get path to `ModuleDef` as string
fn mod_item_path_str(sema_scope: &SemanticsScope<'_>, def: &ModuleDef) -> String {
let path = mod_item_path(sema_scope, def);
path.map(|it| it.display(sema_scope.db.upcast()).to_string()).unwrap()
}

/// Helper function to get path to `Type`
fn type_path(sema_scope: &SemanticsScope<'_>, ty: &Type) -> String {
let db = sema_scope.db;
match ty.as_adt() {
Some(adt) => {
let ty_name = ty.display(db).to_string();

let mut path = mod_item_path(sema_scope, &ModuleDef::Adt(adt)).unwrap();
path.pop_segment();
let path = path.display(db.upcast()).to_string();
match path.is_empty() {
true => ty_name,
false => format!("{path}::{ty_name}"),
}
}
None => ty.display(db).to_string(),
}
}

path.map(|it| it.display(db.upcast()).to_string()).expect("use path error")
/// Helper function to filter out generic parameters that are default
fn non_default_generics(db: &dyn HirDatabase, def: GenericDef, generics: &[Type]) -> Vec<Type> {
def.type_params(db)
.into_iter()
.zip(generics)
.filter(|(tp, arg)| tp.default(db).as_ref() != Some(arg))
.map(|(_, arg)| arg.clone())
.collect()
}

/// Type tree shows how can we get from set of types to some type.
@@ -85,8 +120,8 @@ impl TypeTree {
pub fn gen_source_code(&self, sema_scope: &SemanticsScope<'_>) -> String {
let db = sema_scope.db;
match self {
TypeTree::Const(it) => mod_item_path(db, sema_scope, &ModuleDef::Const(*it)),
TypeTree::Static(it) => mod_item_path(db, sema_scope, &ModuleDef::Static(*it)),
TypeTree::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
TypeTree::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)),
TypeTree::Local(it) => return it.name(db).display(db.upcast()).to_string(),
TypeTree::ConstParam(it) => return it.name(db).display(db.upcast()).to_string(),
TypeTree::FamousType { value, .. } => return value.to_string(),
@@ -100,7 +135,7 @@ impl TypeTree {
match func.as_assoc_item(db).unwrap().containing_trait_or_trait_impl(db) {
Some(trait_) => {
let trait_name =
mod_item_path(db, sema_scope, &ModuleDef::Trait(trait_));
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_));
let target = match self_param.access(db) {
crate::Access::Shared => format!("&{target}"),
crate::Access::Exclusive => format!("&mut {target}"),
@@ -116,15 +151,51 @@ impl TypeTree {
} else {
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");

let fn_name = mod_item_path(db, sema_scope, &ModuleDef::Function(*func));
format!("{fn_name}({args})",)
match func.as_assoc_item(db).map(|it| it.container(db)) {
Some(container) => {
let container_name = match container {
crate::AssocItemContainer::Trait(trait_) => {
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))
}
crate::AssocItemContainer::Impl(imp) => {
let self_ty = imp.self_ty(db);
// Should it be guaranteed that `mod_item_path` always exists?
match self_ty
.as_adt()
.and_then(|adt| mod_item_path(sema_scope, &adt.into()))
{
Some(path) => {
path.display(sema_scope.db.upcast()).to_string()
}
None => self_ty.display(db).to_string(),
}
}
};
let fn_name = func.name(db).display(db.upcast()).to_string();
format!("{container_name}::{fn_name}({args})",)
}
None => {
let fn_name =
mod_item_path_str(sema_scope, &ModuleDef::Function(*func));
format!("{fn_name}({args})",)
}
}
}
}
TypeTree::Variant { variant, generics, params } => {
let generics = non_default_generics(db, (*variant).into(), generics);
let generics_str = match generics.is_empty() {
true => String::new(),
false => {
let generics =
generics.iter().map(|it| type_path(sema_scope, it)).join(", ");
format!("::<{generics}>")
}
};
let inner = match variant.kind(db) {
StructKind::Tuple => {
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");
format!("({args})")
format!("{generics_str}({args})")
}
StructKind::Record => {
let fields = variant.fields(db);
@@ -139,21 +210,16 @@ impl TypeTree {
)
})
.join(", ");
format!("{{ {args} }}")
format!("{generics_str}{{ {args} }}")
}
StructKind::Unit => match generics.is_empty() {
true => String::new(),
false => {
let generics = generics.iter().map(|it| it.display(db)).join(", ");
format!("::<{generics}>")
}
},
StructKind::Unit => generics_str,
};

let prefix = mod_item_path(db, sema_scope, &ModuleDef::Variant(*variant));
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant));
format!("{prefix}{inner}")
}
TypeTree::Struct { strukt, generics, params } => {
let generics = non_default_generics(db, (*strukt).into(), generics);
let inner = match strukt.kind(db) {
StructKind::Tuple => {
let args = params.iter().map(|a| a.gen_source_code(sema_scope)).join(", ");
@@ -177,13 +243,14 @@ impl TypeTree {
StructKind::Unit => match generics.is_empty() {
true => String::new(),
false => {
let generics = generics.iter().map(|it| it.display(db)).join(", ");
let generics =
generics.iter().map(|it| type_path(sema_scope, it)).join(", ");
format!("::<{generics}>")
}
},
};

let prefix = mod_item_path(db, sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)));
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)));
format!("{prefix}{inner}")
}
TypeTree::Field { type_tree, field } => {
38 changes: 37 additions & 1 deletion crates/ide-assists/src/handlers/term_search.rs
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ mod tests {
fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) };
enum Option<T> { Some(T), None }
fn f() { let a: i32 = 1; let b: Option<i32> = Option::None::<i32>; }"#,
fn f() { let a: i32 = 1; let b: Option<i32> = Option::None; }"#,
)
}

@@ -108,6 +108,42 @@ mod tests {
)
}

#[test]
fn test_enum_with_generics3() {
check_assist(
term_search,
r#"macro_rules! todo { () => (_) };
enum Option<T> { None, Some(T) }
fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) };
enum Option<T> { None, Some(T) }
fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = Option::Some(a); }"#,
)
}

#[test]
fn test_enum_with_generics4() {
check_assist(
term_search,
r#"macro_rules! todo { () => (_) };
enum Foo<T = i32> { Foo(T) }
fn f() { let a = 0; let b: Foo = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) };
enum Foo<T = i32> { Foo(T) }
fn f() { let a = 0; let b: Foo = Foo::Foo(a); }"#,
);

check_assist(
term_search,
r#"macro_rules! todo { () => (_) };
enum Foo<T = i32> { Foo(T) }
fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) };
enum Foo<T = i32> { Foo(T) }
fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = a; }"#,
)
}

#[test]
fn test_newtype() {
check_assist(
2 changes: 1 addition & 1 deletion crates/ide-db/src/path_transform.rs
Original file line number Diff line number Diff line change
@@ -119,7 +119,7 @@ impl<'a> PathTransform<'a> {
let mut defaulted_params: Vec<DefaultedParam> = Default::default();
self.generic_def
.into_iter()
.flat_map(|it| it.type_params(db))
.flat_map(|it| it.type_or_const_params(db))
.skip(skip)
// The actual list of trait type parameters may be longer than the one
// used in the `impl` block due to trailing default type parameters.
11 changes: 6 additions & 5 deletions crates/rust-analyzer/src/cli/analysis_stats.rs
Original file line number Diff line number Diff line change
@@ -436,17 +436,18 @@ impl flags::AnalysisStats {
if let Some(mut err_idx) = err.find("error[E") {
err_idx += 7;
let err_code = &err[err_idx..err_idx + 4];
// if err_code == "0308" {
println!("{}", err);
println!("{}", generated);
// }
if err_code == "0282" {
continue; // Byproduct of testing method
}
bar.println(err);
bar.println(generated);
acc.error_codes
.entry(err_code.to_owned())
.and_modify(|n| *n += 1)
.or_insert(1);
} else {
acc.syntax_errors += 1;
bar.println(format!("Syntax error here >>>>\n{}", err));
bar.println(format!("Syntax error: \n{}", err));
}
}
}

0 comments on commit 4da835f

Please sign in to comment.