mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 05:53:45 +00:00
Merge branch 'master' into feature/themes
This commit is contained in:
commit
1d8bb4c6c1
12 changed files with 605 additions and 103 deletions
|
@ -1053,4 +1053,13 @@ impl AssocItem {
|
||||||
AssocItem::TypeAlias(t) => t.module(db),
|
AssocItem::TypeAlias(t) => t.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn container(self, db: &impl DefDatabase) -> Container {
|
||||||
|
match self {
|
||||||
|
AssocItem::Function(f) => f.container(db),
|
||||||
|
AssocItem::Const(c) => c.container(db),
|
||||||
|
AssocItem::TypeAlias(t) => t.container(db),
|
||||||
|
}
|
||||||
|
.expect("AssocItem without container")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,9 +77,10 @@ impl GenericParams {
|
||||||
let parent = match def {
|
let parent = match def {
|
||||||
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
|
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
|
||||||
GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from),
|
GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from),
|
||||||
|
GenericDef::Const(it) => it.container(db).map(GenericDef::from),
|
||||||
GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()),
|
GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()),
|
||||||
GenericDef::Adt(_) | GenericDef::Trait(_) => None,
|
GenericDef::Adt(_) | GenericDef::Trait(_) => None,
|
||||||
GenericDef::ImplBlock(_) | GenericDef::Const(_) => None,
|
GenericDef::ImplBlock(_) => None,
|
||||||
};
|
};
|
||||||
let mut generics = GenericParams {
|
let mut generics = GenericParams {
|
||||||
def,
|
def,
|
||||||
|
|
|
@ -27,9 +27,9 @@ use crate::{
|
||||||
},
|
},
|
||||||
ids::LocationCtx,
|
ids::LocationCtx,
|
||||||
resolve::{ScopeDef, TypeNs, ValueNs},
|
resolve::{ScopeDef, TypeNs, ValueNs},
|
||||||
ty::method_resolution::implements_trait,
|
ty::method_resolution::{self, implements_trait},
|
||||||
Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module,
|
AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId,
|
||||||
Name, Path, Resolver, Static, Struct, Ty,
|
MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn try_get_resolver_for_node(
|
fn try_get_resolver_for_node(
|
||||||
|
@ -255,7 +255,9 @@ impl SourceAnalyzer {
|
||||||
|
|
||||||
let items =
|
let items =
|
||||||
self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def);
|
self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def);
|
||||||
types.or(values).or(items)
|
types.or(values).or(items).or_else(|| {
|
||||||
|
self.resolver.resolve_path_as_macro(db, &path).map(|def| PathResolution::Macro(def))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
|
pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
|
||||||
|
@ -325,16 +327,42 @@ impl SourceAnalyzer {
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
// There should be no inference vars in types passed here
|
// There should be no inference vars in types passed here
|
||||||
// FIXME check that?
|
// FIXME check that?
|
||||||
|
// FIXME replace Unknown by bound vars here
|
||||||
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||||
crate::ty::method_resolution::iterate_method_candidates(
|
method_resolution::iterate_method_candidates(
|
||||||
&canonical,
|
&canonical,
|
||||||
db,
|
db,
|
||||||
&self.resolver,
|
&self.resolver,
|
||||||
name,
|
name,
|
||||||
|
method_resolution::LookupMode::MethodCall,
|
||||||
|
|ty, it| match it {
|
||||||
|
AssocItem::Function(f) => callback(ty, f),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iterate_path_candidates<T>(
|
||||||
|
&self,
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
ty: Ty,
|
||||||
|
name: Option<&Name>,
|
||||||
|
callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
|
) -> Option<T> {
|
||||||
|
// There should be no inference vars in types passed here
|
||||||
|
// FIXME check that?
|
||||||
|
// FIXME replace Unknown by bound vars here
|
||||||
|
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||||
|
method_resolution::iterate_method_candidates(
|
||||||
|
&canonical,
|
||||||
|
db,
|
||||||
|
&self.resolver,
|
||||||
|
name,
|
||||||
|
method_resolution::LookupMode::Path,
|
||||||
callback,
|
callback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,13 +385,22 @@ impl SubstsBuilder {
|
||||||
self.param_count - self.vec.len()
|
self.param_count - self.vec.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_with_bound_vars(mut self, starting_from: u32) -> Self {
|
pub fn fill_with_bound_vars(self, starting_from: u32) -> Self {
|
||||||
self.vec.extend((starting_from..starting_from + self.remaining() as u32).map(Ty::Bound));
|
self.fill((starting_from..).map(Ty::Bound))
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_with_unknown(mut self) -> Self {
|
pub fn fill_with_params(self) -> Self {
|
||||||
self.vec.extend(iter::repeat(Ty::Unknown).take(self.remaining()));
|
let start = self.vec.len() as u32;
|
||||||
|
self.fill((start..).map(|idx| Ty::Param { idx, name: Name::missing() }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_with_unknown(self) -> Self {
|
||||||
|
self.fill(iter::repeat(Ty::Unknown))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill(mut self, filler: impl Iterator<Item = Ty>) -> Self {
|
||||||
|
self.vec.extend(filler.take(self.remaining()));
|
||||||
|
assert_eq!(self.remaining(), 0);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ use super::{ExprOrPatId, InferenceContext, TraitRef};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
|
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||||
ty::{Substs, Ty, TypableDef, TypeWalk},
|
ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk},
|
||||||
AssocItem, HasGenericParams, Namespace, Path,
|
AssocItem, Container, HasGenericParams, Name, Namespace, Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
|
@ -39,7 +39,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
|
let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
|
||||||
self.resolve_ty_assoc_item(
|
self.resolve_ty_assoc_item(
|
||||||
ty,
|
ty,
|
||||||
path.segments.last().expect("path had at least one segment"),
|
&path.segments.last().expect("path had at least one segment").name,
|
||||||
id,
|
id,
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,10 +122,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ty = self.insert_type_vars(ty);
|
||||||
|
let ty = self.normalize_associated_types_in(ty);
|
||||||
|
|
||||||
let segment =
|
let segment =
|
||||||
remaining_segments.last().expect("there should be at least one segment here");
|
remaining_segments.last().expect("there should be at least one segment here");
|
||||||
|
|
||||||
self.resolve_ty_assoc_item(ty, segment, id)
|
self.resolve_ty_assoc_item(ty, &segment.name, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +165,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
};
|
};
|
||||||
let substs = Substs::build_for_def(self.db, item)
|
let substs = Substs::build_for_def(self.db, item)
|
||||||
.use_parent_substs(&trait_ref.substs)
|
.use_parent_substs(&trait_ref.substs)
|
||||||
.fill_with_unknown()
|
.fill_with_params()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
self.write_assoc_resolution(id, item);
|
self.write_assoc_resolution(id, item);
|
||||||
|
@ -172,44 +175,51 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
fn resolve_ty_assoc_item(
|
fn resolve_ty_assoc_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
segment: &PathSegment,
|
name: &Name,
|
||||||
id: ExprOrPatId,
|
id: ExprOrPatId,
|
||||||
) -> Option<(ValueNs, Option<Substs>)> {
|
) -> Option<(ValueNs, Option<Substs>)> {
|
||||||
if let Ty::Unknown = ty {
|
if let Ty::Unknown = ty {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let krate = self.resolver.krate()?;
|
let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
|
||||||
|
|
||||||
// Find impl
|
method_resolution::iterate_method_candidates(
|
||||||
// FIXME: consider trait candidates
|
&canonical_ty.value,
|
||||||
let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
|
self.db,
|
||||||
AssocItem::Function(func) => {
|
&self.resolver.clone(),
|
||||||
if segment.name == func.name(self.db) {
|
Some(name),
|
||||||
Some(AssocItem::Function(func))
|
method_resolution::LookupMode::Path,
|
||||||
} else {
|
move |_ty, item| {
|
||||||
None
|
let def = match item {
|
||||||
}
|
AssocItem::Function(f) => ValueNs::Function(f),
|
||||||
}
|
AssocItem::Const(c) => ValueNs::Const(c),
|
||||||
|
AssocItem::TypeAlias(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
let substs = match item.container(self.db) {
|
||||||
|
Container::ImplBlock(_) => self.find_self_types(&def, ty.clone()),
|
||||||
|
Container::Trait(t) => {
|
||||||
|
// we're picking this method
|
||||||
|
let trait_substs = Substs::build_for_def(self.db, t)
|
||||||
|
.push(ty.clone())
|
||||||
|
.fill(std::iter::repeat_with(|| self.new_type_var()))
|
||||||
|
.build();
|
||||||
|
let substs = Substs::build_for_def(self.db, item)
|
||||||
|
.use_parent_substs(&trait_substs)
|
||||||
|
.fill_with_params()
|
||||||
|
.build();
|
||||||
|
self.obligations.push(super::Obligation::Trait(TraitRef {
|
||||||
|
trait_: t,
|
||||||
|
substs: trait_substs,
|
||||||
|
}));
|
||||||
|
Some(substs)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
AssocItem::Const(konst) => {
|
self.write_assoc_resolution(id, item);
|
||||||
if konst.name(self.db).map_or(false, |n| n == segment.name) {
|
Some((def, substs))
|
||||||
Some(AssocItem::Const(konst))
|
},
|
||||||
} else {
|
)
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AssocItem::TypeAlias(_) => None,
|
|
||||||
})?;
|
|
||||||
let def = match item {
|
|
||||||
AssocItem::Function(f) => ValueNs::Function(f),
|
|
||||||
AssocItem::Const(c) => ValueNs::Const(c),
|
|
||||||
AssocItem::TypeAlias(_) => unreachable!(),
|
|
||||||
};
|
|
||||||
let substs = self.find_self_types(&def, ty);
|
|
||||||
|
|
||||||
self.write_assoc_resolution(id, item);
|
|
||||||
Some((def, substs))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
|
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
|
||||||
|
|
|
@ -166,37 +166,78 @@ pub(crate) fn lookup_method(
|
||||||
name: &Name,
|
name: &Name,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
) -> Option<(Ty, Function)> {
|
) -> Option<(Ty, Function)> {
|
||||||
iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f)))
|
iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| match f
|
||||||
|
{
|
||||||
|
AssocItem::Function(f) => Some((ty.clone(), f)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether we're looking up a dotted method call (like `v.len()`) or a path
|
||||||
|
/// (like `Vec::new`).
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum LookupMode {
|
||||||
|
/// Looking up a method call like `v.len()`: We only consider candidates
|
||||||
|
/// that have a `self` parameter, and do autoderef.
|
||||||
|
MethodCall,
|
||||||
|
/// Looking up a path like `Vec::new` or `Vec::default`: We consider all
|
||||||
|
/// candidates including associated constants, but don't do autoderef.
|
||||||
|
Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This would be nicer if it just returned an iterator, but that runs into
|
// This would be nicer if it just returned an iterator, but that runs into
|
||||||
// lifetime problems, because we need to borrow temp `CrateImplBlocks`.
|
// lifetime problems, because we need to borrow temp `CrateImplBlocks`.
|
||||||
|
// FIXME add a context type here?
|
||||||
pub(crate) fn iterate_method_candidates<T>(
|
pub(crate) fn iterate_method_candidates<T>(
|
||||||
ty: &Canonical<Ty>,
|
ty: &Canonical<Ty>,
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mode: LookupMode,
|
||||||
|
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
// For method calls, rust first does any number of autoderef, and then one
|
|
||||||
// autoref (i.e. when the method takes &self or &mut self). We just ignore
|
|
||||||
// the autoref currently -- when we find a method matching the given name,
|
|
||||||
// we assume it fits.
|
|
||||||
|
|
||||||
// Also note that when we've got a receiver like &S, even if the method we
|
|
||||||
// find in the end takes &self, we still do the autoderef step (just as
|
|
||||||
// rustc does an autoderef and then autoref again).
|
|
||||||
|
|
||||||
let krate = resolver.krate()?;
|
let krate = resolver.krate()?;
|
||||||
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
|
match mode {
|
||||||
if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback)
|
LookupMode::MethodCall => {
|
||||||
{
|
// For method calls, rust first does any number of autoderef, and then one
|
||||||
return Some(result);
|
// autoref (i.e. when the method takes &self or &mut self). We just ignore
|
||||||
|
// the autoref currently -- when we find a method matching the given name,
|
||||||
|
// we assume it fits.
|
||||||
|
|
||||||
|
// Also note that when we've got a receiver like &S, even if the method we
|
||||||
|
// find in the end takes &self, we still do the autoderef step (just as
|
||||||
|
// rustc does an autoderef and then autoref again).
|
||||||
|
|
||||||
|
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
|
||||||
|
if let Some(result) =
|
||||||
|
iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
|
||||||
|
{
|
||||||
|
return Some(result);
|
||||||
|
}
|
||||||
|
if let Some(result) = iterate_trait_method_candidates(
|
||||||
|
&derefed_ty,
|
||||||
|
db,
|
||||||
|
resolver,
|
||||||
|
name,
|
||||||
|
mode,
|
||||||
|
&mut callback,
|
||||||
|
) {
|
||||||
|
return Some(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(result) =
|
LookupMode::Path => {
|
||||||
iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback)
|
// No autoderef for path lookups
|
||||||
{
|
if let Some(result) =
|
||||||
return Some(result);
|
iterate_inherent_methods(&ty, db, name, mode, krate, &mut callback)
|
||||||
|
{
|
||||||
|
return Some(result);
|
||||||
|
}
|
||||||
|
if let Some(result) =
|
||||||
|
iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback)
|
||||||
|
{
|
||||||
|
return Some(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -207,7 +248,8 @@ fn iterate_trait_method_candidates<T>(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mode: LookupMode,
|
||||||
|
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let krate = resolver.krate()?;
|
let krate = resolver.krate()?;
|
||||||
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
||||||
|
@ -231,22 +273,20 @@ fn iterate_trait_method_candidates<T>(
|
||||||
// trait, but if we find out it doesn't, we'll skip the rest of the
|
// trait, but if we find out it doesn't, we'll skip the rest of the
|
||||||
// iteration
|
// iteration
|
||||||
let mut known_implemented = inherently_implemented;
|
let mut known_implemented = inherently_implemented;
|
||||||
for item in data.items() {
|
for &item in data.items() {
|
||||||
if let AssocItem::Function(m) = *item {
|
if !is_valid_candidate(db, name, mode, item) {
|
||||||
let data = m.data(db);
|
continue;
|
||||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
}
|
||||||
if !known_implemented {
|
if !known_implemented {
|
||||||
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
||||||
if db.trait_solve(krate, goal).is_none() {
|
if db.trait_solve(krate, goal).is_none() {
|
||||||
continue 'traits;
|
continue 'traits;
|
||||||
}
|
|
||||||
}
|
|
||||||
known_implemented = true;
|
|
||||||
if let Some(result) = callback(&ty.value, m) {
|
|
||||||
return Some(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
known_implemented = true;
|
||||||
|
if let Some(result) = callback(&ty.value, item) {
|
||||||
|
return Some(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -256,21 +296,20 @@ fn iterate_inherent_methods<T>(
|
||||||
ty: &Canonical<Ty>,
|
ty: &Canonical<Ty>,
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
|
mode: LookupMode,
|
||||||
krate: Crate,
|
krate: Crate,
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
for krate in def_crates(db, krate, &ty.value)? {
|
for krate in def_crates(db, krate, &ty.value)? {
|
||||||
let impls = db.impls_in_crate(krate);
|
let impls = db.impls_in_crate(krate);
|
||||||
|
|
||||||
for impl_block in impls.lookup_impl_blocks(&ty.value) {
|
for impl_block in impls.lookup_impl_blocks(&ty.value) {
|
||||||
for item in impl_block.items(db) {
|
for item in impl_block.items(db) {
|
||||||
if let AssocItem::Function(f) = item {
|
if !is_valid_candidate(db, name, mode, item) {
|
||||||
let data = f.data(db);
|
continue;
|
||||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
}
|
||||||
if let Some(result) = callback(&ty.value, f) {
|
if let Some(result) = callback(&ty.value, item) {
|
||||||
return Some(result);
|
return Some(result);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,6 +317,26 @@ fn iterate_inherent_methods<T>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_valid_candidate(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
name: Option<&Name>,
|
||||||
|
mode: LookupMode,
|
||||||
|
item: AssocItem,
|
||||||
|
) -> bool {
|
||||||
|
match item {
|
||||||
|
AssocItem::Function(m) => {
|
||||||
|
let data = m.data(db);
|
||||||
|
name.map_or(true, |name| data.name() == name)
|
||||||
|
&& (data.has_self_param() || mode == LookupMode::Path)
|
||||||
|
}
|
||||||
|
AssocItem::Const(c) => {
|
||||||
|
name.map_or(true, |name| Some(name) == c.name(db).as_ref())
|
||||||
|
&& (mode == LookupMode::Path)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn implements_trait(
|
pub(crate) fn implements_trait(
|
||||||
ty: &Canonical<Ty>,
|
ty: &Canonical<Ty>,
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
|
|
|
@ -1841,8 +1841,8 @@ fn test() {
|
||||||
[243; 254) 'Struct::FOO': u32
|
[243; 254) 'Struct::FOO': u32
|
||||||
[264; 265) 'y': u32
|
[264; 265) 'y': u32
|
||||||
[268; 277) 'Enum::BAR': u32
|
[268; 277) 'Enum::BAR': u32
|
||||||
[287; 288) 'z': {unknown}
|
[287; 288) 'z': u32
|
||||||
[291; 304) 'TraitTest::ID': {unknown}
|
[291; 304) 'TraitTest::ID': u32
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2782,9 +2782,9 @@ fn test() {
|
||||||
[97; 99) 's1': S
|
[97; 99) 's1': S
|
||||||
[105; 121) 'Defaul...efault': fn default<S>() -> Self
|
[105; 121) 'Defaul...efault': fn default<S>() -> Self
|
||||||
[105; 123) 'Defaul...ault()': S
|
[105; 123) 'Defaul...ault()': S
|
||||||
[133; 135) 's2': {unknown}
|
[133; 135) 's2': S
|
||||||
[138; 148) 'S::default': {unknown}
|
[138; 148) 'S::default': fn default<S>() -> Self
|
||||||
[138; 150) 'S::default()': {unknown}
|
[138; 150) 'S::default()': S
|
||||||
[160; 162) 's3': S
|
[160; 162) 's3': S
|
||||||
[165; 188) '<S as ...efault': fn default<S>() -> Self
|
[165; 188) '<S as ...efault': fn default<S>() -> Self
|
||||||
[165; 190) '<S as ...ault()': S
|
[165; 190) '<S as ...ault()': S
|
||||||
|
@ -2792,6 +2792,153 @@ fn test() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_trait_assoc_method_generics_1() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn make() -> T;
|
||||||
|
}
|
||||||
|
struct S;
|
||||||
|
impl Trait<u32> for S {}
|
||||||
|
struct G<T>;
|
||||||
|
impl<T> Trait<T> for G<T> {}
|
||||||
|
fn test() {
|
||||||
|
let a = S::make();
|
||||||
|
let b = G::<u64>::make();
|
||||||
|
let c: f64 = G::make();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[127; 211) '{ ...e(); }': ()
|
||||||
|
[137; 138) 'a': u32
|
||||||
|
[141; 148) 'S::make': fn make<S, u32>() -> T
|
||||||
|
[141; 150) 'S::make()': u32
|
||||||
|
[160; 161) 'b': u64
|
||||||
|
[164; 178) 'G::<u64>::make': fn make<G<u64>, u64>() -> T
|
||||||
|
[164; 180) 'G::<u6...make()': u64
|
||||||
|
[190; 191) 'c': f64
|
||||||
|
[199; 206) 'G::make': fn make<G<f64>, f64>() -> T
|
||||||
|
[199; 208) 'G::make()': f64
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_trait_assoc_method_generics_2() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn make<U>() -> (T, U);
|
||||||
|
}
|
||||||
|
struct S;
|
||||||
|
impl Trait<u32> for S {}
|
||||||
|
struct G<T>;
|
||||||
|
impl<T> Trait<T> for G<T> {}
|
||||||
|
fn test() {
|
||||||
|
let a = S::make::<i64>();
|
||||||
|
let b: (_, i64) = S::make();
|
||||||
|
let c = G::<u32>::make::<i64>();
|
||||||
|
let d: (u32, _) = G::make::<i64>();
|
||||||
|
let e: (u32, i64) = G::make();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[135; 313) '{ ...e(); }': ()
|
||||||
|
[145; 146) 'a': (u32, i64)
|
||||||
|
[149; 163) 'S::make::<i64>': fn make<S, u32, i64>() -> (T, U)
|
||||||
|
[149; 165) 'S::mak...i64>()': (u32, i64)
|
||||||
|
[175; 176) 'b': (u32, i64)
|
||||||
|
[189; 196) 'S::make': fn make<S, u32, i64>() -> (T, U)
|
||||||
|
[189; 198) 'S::make()': (u32, i64)
|
||||||
|
[208; 209) 'c': (u32, i64)
|
||||||
|
[212; 233) 'G::<u3...:<i64>': fn make<G<u32>, u32, i64>() -> (T, U)
|
||||||
|
[212; 235) 'G::<u3...i64>()': (u32, i64)
|
||||||
|
[245; 246) 'd': (u32, i64)
|
||||||
|
[259; 273) 'G::make::<i64>': fn make<G<u32>, u32, i64>() -> (T, U)
|
||||||
|
[259; 275) 'G::mak...i64>()': (u32, i64)
|
||||||
|
[285; 286) 'e': (u32, i64)
|
||||||
|
[301; 308) 'G::make': fn make<G<u32>, u32, i64>() -> (T, U)
|
||||||
|
[301; 310) 'G::make()': (u32, i64)
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_trait_assoc_method_generics_3() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn make() -> (Self, T);
|
||||||
|
}
|
||||||
|
struct S<T>;
|
||||||
|
impl Trait<i64> for S<i32> {}
|
||||||
|
fn test() {
|
||||||
|
let a = S::make();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[101; 127) '{ ...e(); }': ()
|
||||||
|
[111; 112) 'a': (S<i32>, i64)
|
||||||
|
[115; 122) 'S::make': fn make<S<i32>, i64>() -> (Self, T)
|
||||||
|
[115; 124) 'S::make()': (S<i32>, i64)
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_trait_assoc_method_generics_4() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn make() -> (Self, T);
|
||||||
|
}
|
||||||
|
struct S<T>;
|
||||||
|
impl Trait<i64> for S<u64> {}
|
||||||
|
impl Trait<i32> for S<u32> {}
|
||||||
|
fn test() {
|
||||||
|
let a: (S<u64>, _) = S::make();
|
||||||
|
let b: (_, i32) = S::make();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[131; 203) '{ ...e(); }': ()
|
||||||
|
[141; 142) 'a': (S<u64>, i64)
|
||||||
|
[158; 165) 'S::make': fn make<S<u64>, i64>() -> (Self, T)
|
||||||
|
[158; 167) 'S::make()': (S<u64>, i64)
|
||||||
|
[177; 178) 'b': (S<u32>, i32)
|
||||||
|
[191; 198) 'S::make': fn make<S<u32>, i32>() -> (Self, T)
|
||||||
|
[191; 200) 'S::make()': (S<u32>, i32)
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_trait_assoc_method_generics_5() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn make<U>() -> (Self, T, U);
|
||||||
|
}
|
||||||
|
struct S<T>;
|
||||||
|
impl Trait<i64> for S<u64> {}
|
||||||
|
fn test() {
|
||||||
|
let a = <S as Trait<i64>>::make::<u8>();
|
||||||
|
let b: (S<u64>, _, _) = Trait::<i64>::make::<u8>();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[107; 211) '{ ...>(); }': ()
|
||||||
|
[117; 118) 'a': (S<u64>, i64, u8)
|
||||||
|
[121; 150) '<S as ...::<u8>': fn make<S<u64>, i64, u8>() -> (Self, T, U)
|
||||||
|
[121; 152) '<S as ...<u8>()': (S<u64>, i64, u8)
|
||||||
|
[162; 163) 'b': (S<u64>, i64, u8)
|
||||||
|
[182; 206) 'Trait:...::<u8>': fn make<S<u64>, i64, u8>() -> (Self, T, U)
|
||||||
|
[182; 208) 'Trait:...<u8>()': (S<u64>, i64, u8)
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_from_bound_1() {
|
fn infer_from_bound_1() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
@ -3303,6 +3450,22 @@ fn test() { S.foo()<|>; }
|
||||||
assert_eq!(t, "u128");
|
assert_eq!(t, "u128");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
|
#[test]
|
||||||
|
fn method_resolution_by_value_before_autoref() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Clone { fn clone(&self) -> Self; }
|
||||||
|
struct S;
|
||||||
|
impl Clone for S {}
|
||||||
|
impl Clone for &S {}
|
||||||
|
fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "(S, S, &S)");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn method_resolution_trait_before_autoderef() {
|
fn method_resolution_trait_before_autoderef() {
|
||||||
let t = type_at(
|
let t = type_at(
|
||||||
|
|
|
@ -50,23 +50,46 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
ctx.analyzer.iterate_path_candidates(ctx.db, ty.clone(), None, |_ty, item| {
|
||||||
|
match item {
|
||||||
|
hir::AssocItem::Function(func) => {
|
||||||
|
let data = func.data(ctx.db);
|
||||||
|
if !data.has_self_param() {
|
||||||
|
acc.add_function(ctx, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
|
||||||
|
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||||
|
}
|
||||||
|
None::<()>
|
||||||
|
});
|
||||||
|
// Iterate assoc types separately
|
||||||
|
// FIXME: complete T::AssocType
|
||||||
let krate = ctx.module.map(|m| m.krate());
|
let krate = ctx.module.map(|m| m.krate());
|
||||||
if let Some(krate) = krate {
|
if let Some(krate) = krate {
|
||||||
ty.iterate_impl_items(ctx.db, krate, |item| {
|
ty.iterate_impl_items(ctx.db, krate, |item| {
|
||||||
match item {
|
match item {
|
||||||
hir::AssocItem::Function(func) => {
|
hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
|
||||||
let data = func.data(ctx.db);
|
|
||||||
if !data.has_self_param() {
|
|
||||||
acc.add_function(ctx, func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
|
|
||||||
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||||
}
|
}
|
||||||
None::<()>
|
None::<()>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hir::ModuleDef::Trait(t) => {
|
||||||
|
for item in t.items(ctx.db) {
|
||||||
|
match item {
|
||||||
|
hir::AssocItem::Function(func) => {
|
||||||
|
let data = func.data(ctx.db);
|
||||||
|
if !data.has_self_param() {
|
||||||
|
acc.add_function(ctx, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
|
||||||
|
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -558,6 +581,111 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_trait_associated_method_1() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
trait Trait {
|
||||||
|
/// A trait method
|
||||||
|
fn m();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() { let _ = Trait::<|> }
|
||||||
|
"
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "m()",
|
||||||
|
source_range: [73; 73),
|
||||||
|
delete: [73; 73),
|
||||||
|
insert: "m()$0",
|
||||||
|
kind: Function,
|
||||||
|
lookup: "m",
|
||||||
|
detail: "fn m()",
|
||||||
|
documentation: Documentation(
|
||||||
|
"A trait method",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_trait_associated_method_2() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
trait Trait {
|
||||||
|
/// A trait method
|
||||||
|
fn m();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl Trait for S {}
|
||||||
|
|
||||||
|
fn foo() { let _ = S::<|> }
|
||||||
|
"
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "m()",
|
||||||
|
source_range: [99; 99),
|
||||||
|
delete: [99; 99),
|
||||||
|
insert: "m()$0",
|
||||||
|
kind: Function,
|
||||||
|
lookup: "m",
|
||||||
|
detail: "fn m()",
|
||||||
|
documentation: Documentation(
|
||||||
|
"A trait method",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_trait_associated_method_3() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
trait Trait {
|
||||||
|
/// A trait method
|
||||||
|
fn m();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl Trait for S {}
|
||||||
|
|
||||||
|
fn foo() { let _ = <S as Trait>::<|> }
|
||||||
|
"
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "m()",
|
||||||
|
source_range: [110; 110),
|
||||||
|
delete: [110; 110),
|
||||||
|
insert: "m()$0",
|
||||||
|
kind: Function,
|
||||||
|
lookup: "m",
|
||||||
|
detail: "fn m()",
|
||||||
|
documentation: Documentation(
|
||||||
|
"A trait method",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_type_alias() {
|
fn completes_type_alias() {
|
||||||
assert_debug_snapshot!(
|
assert_debug_snapshot!(
|
||||||
|
|
|
@ -315,6 +315,25 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_definition_works_for_macros_in_use_tree() {
|
||||||
|
check_goto(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
use foo::foo<|>;
|
||||||
|
|
||||||
|
//- /foo/lib.rs
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! foo {
|
||||||
|
() => {
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"foo MACRO_CALL FileId(2) [0; 66) [29; 32)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_definition_works_for_methods() {
|
fn goto_definition_works_for_methods() {
|
||||||
covers!(goto_definition_works_for_methods);
|
covers!(goto_definition_works_for_methods);
|
||||||
|
@ -371,6 +390,61 @@ mod tests {
|
||||||
"spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
|
"spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_definition_works_for_ufcs_inherent_methods() {
|
||||||
|
check_goto(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn frobnicate() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(foo: &Foo) {
|
||||||
|
Foo::frobnicate<|>();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"frobnicate FN_DEF FileId(1) [27; 47) [30; 40)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_definition_works_for_ufcs_trait_methods_through_traits() {
|
||||||
|
check_goto(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
trait Foo {
|
||||||
|
fn frobnicate();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
Foo::frobnicate<|>();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"frobnicate FN_DEF FileId(1) [16; 32) [19; 29)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_definition_works_for_ufcs_trait_methods_through_self() {
|
||||||
|
check_goto(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
struct Foo;
|
||||||
|
trait Trait {
|
||||||
|
fn frobnicate();
|
||||||
|
}
|
||||||
|
impl Trait for Foo {}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
Foo::frobnicate<|>();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"frobnicate FN_DEF FileId(1) [30; 46) [33; 43)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_definition_on_self() {
|
fn goto_definition_on_self() {
|
||||||
check_goto(
|
check_goto(
|
||||||
|
|
|
@ -112,6 +112,20 @@ fn test_doc_comment_of_items() {
|
||||||
assert_eq!("doc", module.doc_comment_text().unwrap());
|
assert_eq!("doc", module.doc_comment_text().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_doc_comment_of_statics() {
|
||||||
|
let file = SourceFile::parse(
|
||||||
|
r#"
|
||||||
|
/// Number of levels
|
||||||
|
static LEVELS: i32 = 0;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap();
|
||||||
|
assert_eq!("Number of levels", st.doc_comment_text().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_doc_comment_preserves_indents() {
|
fn test_doc_comment_preserves_indents() {
|
||||||
let file = SourceFile::parse(
|
let file = SourceFile::parse(
|
||||||
|
|
|
@ -147,7 +147,7 @@ fn n_attached_trivias<'a>(
|
||||||
) -> usize {
|
) -> usize {
|
||||||
match kind {
|
match kind {
|
||||||
MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF
|
MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF
|
||||||
| TRAIT_DEF | MODULE | RECORD_FIELD_DEF => {
|
| TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => {
|
||||||
let mut res = 0;
|
let mut res = 0;
|
||||||
for (i, (kind, text)) in trivias.enumerate() {
|
for (i, (kind, text)) in trivias.enumerate() {
|
||||||
match kind {
|
match kind {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
env,
|
||||||
error::Error,
|
error::Error,
|
||||||
fs,
|
fs,
|
||||||
io::{Error as IoError, ErrorKind},
|
io::{Error as IoError, ErrorKind},
|
||||||
|
@ -17,7 +18,13 @@ pub type Result<T> = std::result::Result<T, Box<dyn Error>>;
|
||||||
const TOOLCHAIN: &str = "stable";
|
const TOOLCHAIN: &str = "stable";
|
||||||
|
|
||||||
pub fn project_root() -> PathBuf {
|
pub fn project_root() -> PathBuf {
|
||||||
Path::new(&env!("CARGO_MANIFEST_DIR")).ancestors().nth(1).unwrap().to_path_buf()
|
Path::new(
|
||||||
|
&env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
|
||||||
|
)
|
||||||
|
.ancestors()
|
||||||
|
.nth(1)
|
||||||
|
.unwrap()
|
||||||
|
.to_path_buf()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cmd<'a> {
|
pub struct Cmd<'a> {
|
||||||
|
|
Loading…
Reference in a new issue