Auto merge of #148471 - lnicola:sync-from-ra, r=lnicola
`rust-analyzer` subtree update
Subtree update of `rust-analyzer` to 51af7a37c5.
Created using https://github.com/rust-lang/josh-sync.
r? `@ghost`
This commit is contained in:
commit
f15a7f3858
96 changed files with 2985 additions and 1094 deletions
|
|
@ -33,6 +33,7 @@ trivias = "trivias"
|
||||||
thir = "thir"
|
thir = "thir"
|
||||||
jod = "jod"
|
jod = "jod"
|
||||||
tructure = "tructure"
|
tructure = "tructure"
|
||||||
|
taits = "taits"
|
||||||
|
|
||||||
[default.extend-identifiers]
|
[default.extend-identifiers]
|
||||||
anc = "anc"
|
anc = "anc"
|
||||||
|
|
|
||||||
|
|
@ -418,6 +418,22 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dhat"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"lazy_static",
|
||||||
|
"mintex",
|
||||||
|
"parking_lot",
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thousands",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
|
|
@ -1383,6 +1399,12 @@ dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mintex"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -1452,7 +1474,7 @@ version = "0.50.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2011,6 +2033,7 @@ dependencies = [
|
||||||
"cargo_metadata 0.21.0",
|
"cargo_metadata 0.21.0",
|
||||||
"cfg",
|
"cfg",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
"dhat",
|
||||||
"dirs",
|
"dirs",
|
||||||
"dissimilar",
|
"dissimilar",
|
||||||
"expect-test",
|
"expect-test",
|
||||||
|
|
@ -2047,6 +2070,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"smallvec",
|
||||||
"stdx",
|
"stdx",
|
||||||
"syntax",
|
"syntax",
|
||||||
"syntax-bridge",
|
"syntax-bridge",
|
||||||
|
|
@ -2528,6 +2552,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thousands"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.9"
|
||||||
|
|
|
||||||
|
|
@ -370,18 +370,13 @@ impl<'a> Ctx<'a> {
|
||||||
});
|
});
|
||||||
match &vis {
|
match &vis {
|
||||||
RawVisibility::Public => RawVisibilityId::PUB,
|
RawVisibility::Public => RawVisibilityId::PUB,
|
||||||
RawVisibility::Module(path, explicitness) if path.segments().is_empty() => {
|
RawVisibility::PubSelf(VisibilityExplicitness::Explicit) => {
|
||||||
match (path.kind, explicitness) {
|
RawVisibilityId::PRIV_EXPLICIT
|
||||||
(PathKind::SELF, VisibilityExplicitness::Explicit) => {
|
|
||||||
RawVisibilityId::PRIV_EXPLICIT
|
|
||||||
}
|
|
||||||
(PathKind::SELF, VisibilityExplicitness::Implicit) => {
|
|
||||||
RawVisibilityId::PRIV_IMPLICIT
|
|
||||||
}
|
|
||||||
(PathKind::Crate, _) => RawVisibilityId::PUB_CRATE,
|
|
||||||
_ => RawVisibilityId(self.visibilities.insert_full(vis).0 as u32),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
RawVisibility::PubSelf(VisibilityExplicitness::Implicit) => {
|
||||||
|
RawVisibilityId::PRIV_IMPLICIT
|
||||||
|
}
|
||||||
|
RawVisibility::PubCrate => RawVisibilityId::PUB_CRATE,
|
||||||
_ => RawVisibilityId(self.visibilities.insert_full(vis).0 as u32),
|
_ => RawVisibilityId(self.visibilities.insert_full(vis).0 as u32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -466,10 +461,7 @@ pub(crate) fn lower_use_tree(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn private_vis() -> RawVisibility {
|
fn private_vis() -> RawVisibility {
|
||||||
RawVisibility::Module(
|
RawVisibility::PubSelf(VisibilityExplicitness::Implicit)
|
||||||
Interned::new(ModPath::from_kind(PathKind::SELF)),
|
|
||||||
VisibilityExplicitness::Implicit,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn visibility_from_ast(
|
pub(crate) fn visibility_from_ast(
|
||||||
|
|
@ -486,9 +478,11 @@ pub(crate) fn visibility_from_ast(
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::VisibilityKind::PubCrate => ModPath::from_kind(PathKind::Crate),
|
ast::VisibilityKind::PubCrate => return RawVisibility::PubCrate,
|
||||||
ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
|
ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
|
||||||
ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::SELF),
|
ast::VisibilityKind::PubSelf => {
|
||||||
|
return RawVisibility::PubSelf(VisibilityExplicitness::Explicit);
|
||||||
|
}
|
||||||
ast::VisibilityKind::Pub => return RawVisibility::Public,
|
ast::VisibilityKind::Pub => return RawVisibility::Public,
|
||||||
};
|
};
|
||||||
RawVisibility::Module(Interned::new(path), VisibilityExplicitness::Explicit)
|
RawVisibility::Module(Interned::new(path), VisibilityExplicitness::Explicit)
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ macro_rules! m2 { () => ( ${invalid()} ) }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rustc_issue_57597() {
|
fn test_rustc_issue_57597() {
|
||||||
// <https://github.com/rust-lang/rust/blob/HEAD/tests/ui/issues/issue-57597.rs>
|
// <https://github.com/rust-lang/rust/blob/ec2cc76/tests/ui/macros/issue-57597.rs>
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! m0 { ($($($i:ident)?)+) => {}; }
|
macro_rules! m0 { ($($($i:ident)?)+) => {}; }
|
||||||
|
|
|
||||||
|
|
@ -1075,7 +1075,9 @@ fn resolver_for_scope_<'db>(
|
||||||
if let Some(block) = scopes.block(scope) {
|
if let Some(block) = scopes.block(scope) {
|
||||||
let def_map = block_def_map(db, block);
|
let def_map = block_def_map(db, block);
|
||||||
let local_def_map = block.lookup(db).module.only_local_def_map(db);
|
let local_def_map = block.lookup(db).module.only_local_def_map(db);
|
||||||
r = r.push_block_scope(def_map, local_def_map);
|
// Using `DefMap::ROOT` is okay here since inside modules other than the root,
|
||||||
|
// there can't directly be expressions.
|
||||||
|
r = r.push_block_scope(def_map, local_def_map, DefMap::ROOT);
|
||||||
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
|
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
|
||||||
// already traverses all parents, so this is O(n²). I think we could only store the
|
// already traverses all parents, so this is O(n²). I think we could only store the
|
||||||
// innermost module scope instead?
|
// innermost module scope instead?
|
||||||
|
|
@ -1108,12 +1110,9 @@ impl<'db> Resolver<'db> {
|
||||||
self,
|
self,
|
||||||
def_map: &'db DefMap,
|
def_map: &'db DefMap,
|
||||||
local_def_map: &'db LocalDefMap,
|
local_def_map: &'db LocalDefMap,
|
||||||
|
module_id: LocalModuleId,
|
||||||
) -> Resolver<'db> {
|
) -> Resolver<'db> {
|
||||||
self.push_scope(Scope::BlockScope(ModuleItemMap {
|
self.push_scope(Scope::BlockScope(ModuleItemMap { def_map, local_def_map, module_id }))
|
||||||
def_map,
|
|
||||||
local_def_map,
|
|
||||||
module_id: DefMap::ROOT,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_expr_scope(
|
fn push_expr_scope(
|
||||||
|
|
@ -1273,7 +1272,7 @@ impl HasResolver for ModuleId {
|
||||||
let (mut def_map, local_def_map) = self.local_def_map(db);
|
let (mut def_map, local_def_map) = self.local_def_map(db);
|
||||||
let mut module_id = self.local_id;
|
let mut module_id = self.local_id;
|
||||||
|
|
||||||
if !self.is_block_module() {
|
if !self.is_within_block() {
|
||||||
return Resolver {
|
return Resolver {
|
||||||
scopes: vec![],
|
scopes: vec![],
|
||||||
module_scope: ModuleItemMap { def_map, local_def_map, module_id },
|
module_scope: ModuleItemMap { def_map, local_def_map, module_id },
|
||||||
|
|
@ -1283,9 +1282,9 @@ impl HasResolver for ModuleId {
|
||||||
let mut modules: SmallVec<[_; 1]> = smallvec![];
|
let mut modules: SmallVec<[_; 1]> = smallvec![];
|
||||||
while let Some(parent) = def_map.parent() {
|
while let Some(parent) = def_map.parent() {
|
||||||
let block_def_map = mem::replace(&mut def_map, parent.def_map(db));
|
let block_def_map = mem::replace(&mut def_map, parent.def_map(db));
|
||||||
modules.push(block_def_map);
|
let block_module_id = mem::replace(&mut module_id, parent.local_id);
|
||||||
if !parent.is_block_module() {
|
modules.push((block_def_map, block_module_id));
|
||||||
module_id = parent.local_id;
|
if !parent.is_within_block() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1293,8 +1292,8 @@ impl HasResolver for ModuleId {
|
||||||
scopes: Vec::with_capacity(modules.len()),
|
scopes: Vec::with_capacity(modules.len()),
|
||||||
module_scope: ModuleItemMap { def_map, local_def_map, module_id },
|
module_scope: ModuleItemMap { def_map, local_def_map, module_id },
|
||||||
};
|
};
|
||||||
for def_map in modules.into_iter().rev() {
|
for (def_map, module_id) in modules.into_iter().rev() {
|
||||||
resolver = resolver.push_block_scope(def_map, local_def_map);
|
resolver = resolver.push_block_scope(def_map, local_def_map, module_id);
|
||||||
}
|
}
|
||||||
resolver
|
resolver
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,18 +289,21 @@ pub(crate) fn field_visibilities_query(
|
||||||
|
|
||||||
pub fn visibility_from_ast(
|
pub fn visibility_from_ast(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
has_resolver: impl HasResolver,
|
has_resolver: impl HasResolver + HasModule,
|
||||||
ast_vis: InFile<Option<ast::Visibility>>,
|
ast_vis: InFile<Option<ast::Visibility>>,
|
||||||
) -> Visibility {
|
) -> Visibility {
|
||||||
let mut span_map = None;
|
let mut span_map = None;
|
||||||
let raw_vis = crate::item_tree::visibility_from_ast(db, ast_vis.value, &mut |range| {
|
let raw_vis = crate::item_tree::visibility_from_ast(db, ast_vis.value, &mut |range| {
|
||||||
span_map.get_or_insert_with(|| db.span_map(ast_vis.file_id)).span_for_range(range).ctx
|
span_map.get_or_insert_with(|| db.span_map(ast_vis.file_id)).span_for_range(range).ctx
|
||||||
});
|
});
|
||||||
if raw_vis == RawVisibility::Public {
|
match raw_vis {
|
||||||
return Visibility::Public;
|
RawVisibility::PubSelf(explicitness) => {
|
||||||
|
Visibility::Module(has_resolver.module(db), explicitness)
|
||||||
|
}
|
||||||
|
RawVisibility::PubCrate => Visibility::PubCrate(has_resolver.krate(db)),
|
||||||
|
RawVisibility::Public => Visibility::Public,
|
||||||
|
RawVisibility::Module(..) => Visibility::resolve(db, &has_resolver.resolver(db), &raw_vis),
|
||||||
}
|
}
|
||||||
|
|
||||||
Visibility::resolve(db, &has_resolver.resolver(db), &raw_vis)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve visibility of a type alias.
|
/// Resolve visibility of a type alias.
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ pub fn autoderef<'db>(
|
||||||
env: Arc<TraitEnvironment<'db>>,
|
env: Arc<TraitEnvironment<'db>>,
|
||||||
ty: Canonical<'db, Ty<'db>>,
|
ty: Canonical<'db, Ty<'db>>,
|
||||||
) -> impl Iterator<Item = Ty<'db>> + use<'db> {
|
) -> impl Iterator<Item = Ty<'db>> + use<'db> {
|
||||||
let mut table = InferenceTable::new(db, env);
|
let mut table = InferenceTable::new(db, env, None);
|
||||||
let ty = table.instantiate_canonical(ty);
|
let ty = table.instantiate_canonical(ty);
|
||||||
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty);
|
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty);
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
|
|
|
||||||
|
|
@ -2078,9 +2078,10 @@ pub fn write_visibility<'db>(
|
||||||
if vis_id == module_id {
|
if vis_id == module_id {
|
||||||
// pub(self) or omitted
|
// pub(self) or omitted
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if root_module_id == vis_id {
|
} else if root_module_id == vis_id && !root_module_id.is_within_block() {
|
||||||
write!(f, "pub(crate) ")
|
write!(f, "pub(crate) ")
|
||||||
} else if module_id.containing_module(f.db) == Some(vis_id) {
|
} else if module_id.containing_module(f.db) == Some(vis_id) && !vis_id.is_block_module()
|
||||||
|
{
|
||||||
write!(f, "pub(super) ")
|
write!(f, "pub(super) ")
|
||||||
} else {
|
} else {
|
||||||
write!(f, "pub(in ...) ")
|
write!(f, "pub(in ...) ")
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ pub(crate) mod diagnostics;
|
||||||
mod expr;
|
mod expr;
|
||||||
mod fallback;
|
mod fallback;
|
||||||
mod mutability;
|
mod mutability;
|
||||||
|
mod opaques;
|
||||||
mod pat;
|
mod pat;
|
||||||
mod path;
|
mod path;
|
||||||
pub(crate) mod unify;
|
pub(crate) mod unify;
|
||||||
|
|
@ -31,8 +32,7 @@ use base_db::Crate;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId,
|
AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId,
|
||||||
ImplId, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId,
|
ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||||
VariantId,
|
|
||||||
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
|
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
|
||||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
||||||
lang_item::{LangItem, LangItemTarget, lang_item},
|
lang_item::{LangItem, LangItemTarget, lang_item},
|
||||||
|
|
@ -44,11 +44,11 @@ use hir_def::{
|
||||||
use hir_expand::{mod_path::ModPath, name::Name};
|
use hir_expand::{mod_path::ModPath, name::Name};
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
use la_arena::{ArenaMap, Entry};
|
use la_arena::ArenaMap;
|
||||||
use rustc_ast_ir::Mutability;
|
use rustc_ast_ir::Mutability;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use rustc_type_ir::{
|
use rustc_type_ir::{
|
||||||
AliasTyKind, Flags, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
AliasTyKind, TypeFoldable,
|
||||||
inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _},
|
inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _},
|
||||||
};
|
};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
@ -61,7 +61,6 @@ use crate::{
|
||||||
coerce::{CoerceMany, DynamicCoerceMany},
|
coerce::{CoerceMany, DynamicCoerceMany},
|
||||||
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
|
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
|
||||||
expr::ExprIsRead,
|
expr::ExprIsRead,
|
||||||
unify::InferenceTable,
|
|
||||||
},
|
},
|
||||||
lower::{
|
lower::{
|
||||||
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
|
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
|
||||||
|
|
@ -69,10 +68,7 @@ use crate::{
|
||||||
mir::MirSpan,
|
mir::MirSpan,
|
||||||
next_solver::{
|
next_solver::{
|
||||||
AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind,
|
AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind,
|
||||||
Tys,
|
Tys, abi::Safety, infer::traits::ObligationCause,
|
||||||
abi::Safety,
|
|
||||||
fold::fold_tys,
|
|
||||||
infer::traits::{Obligation, ObligationCause},
|
|
||||||
},
|
},
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
utils::TargetFeatureIsSafeInTarget,
|
utils::TargetFeatureIsSafeInTarget,
|
||||||
|
|
@ -132,6 +128,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
||||||
|
|
||||||
ctx.infer_mut_body();
|
ctx.infer_mut_body();
|
||||||
|
|
||||||
|
ctx.handle_opaque_type_uses();
|
||||||
|
|
||||||
ctx.type_inference_fallback();
|
ctx.type_inference_fallback();
|
||||||
|
|
||||||
// Comment from rustc:
|
// Comment from rustc:
|
||||||
|
|
@ -148,6 +146,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
||||||
|
|
||||||
ctx.infer_closures();
|
ctx.infer_closures();
|
||||||
|
|
||||||
|
ctx.table.select_obligations_where_possible();
|
||||||
|
|
||||||
|
ctx.handle_opaque_type_uses();
|
||||||
|
|
||||||
Arc::new(ctx.resolve_all())
|
Arc::new(ctx.resolve_all())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -454,7 +456,7 @@ pub struct InferenceResult<'db> {
|
||||||
/// unresolved or missing subpatterns or subpatterns of mismatched types.
|
/// unresolved or missing subpatterns or subpatterns of mismatched types.
|
||||||
pub(crate) type_of_pat: ArenaMap<PatId, Ty<'db>>,
|
pub(crate) type_of_pat: ArenaMap<PatId, Ty<'db>>,
|
||||||
pub(crate) type_of_binding: ArenaMap<BindingId, Ty<'db>>,
|
pub(crate) type_of_binding: ArenaMap<BindingId, Ty<'db>>,
|
||||||
pub(crate) type_of_rpit: ArenaMap<ImplTraitIdx<'db>, Ty<'db>>,
|
pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, Ty<'db>>,
|
||||||
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>,
|
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>,
|
||||||
/// Whether there are any type-mismatching errors in the result.
|
/// Whether there are any type-mismatching errors in the result.
|
||||||
// FIXME: This isn't as useful as initially thought due to us falling back placeholders to
|
// FIXME: This isn't as useful as initially thought due to us falling back placeholders to
|
||||||
|
|
@ -499,7 +501,7 @@ impl<'db> InferenceResult<'db> {
|
||||||
type_of_expr: Default::default(),
|
type_of_expr: Default::default(),
|
||||||
type_of_pat: Default::default(),
|
type_of_pat: Default::default(),
|
||||||
type_of_binding: Default::default(),
|
type_of_binding: Default::default(),
|
||||||
type_of_rpit: Default::default(),
|
type_of_opaque: Default::default(),
|
||||||
type_mismatches: Default::default(),
|
type_mismatches: Default::default(),
|
||||||
has_errors: Default::default(),
|
has_errors: Default::default(),
|
||||||
error_ty,
|
error_ty,
|
||||||
|
|
@ -640,8 +642,14 @@ impl<'db> InferenceResult<'db> {
|
||||||
// This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please.
|
// This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please.
|
||||||
pub fn return_position_impl_trait_types(
|
pub fn return_position_impl_trait_types(
|
||||||
&self,
|
&self,
|
||||||
|
db: &'db dyn HirDatabase,
|
||||||
) -> impl Iterator<Item = (ImplTraitIdx<'db>, Ty<'db>)> {
|
) -> impl Iterator<Item = (ImplTraitIdx<'db>, Ty<'db>)> {
|
||||||
self.type_of_rpit.iter().map(|(k, v)| (k, *v))
|
self.type_of_opaque.iter().filter_map(move |(&id, &ty)| {
|
||||||
|
let ImplTraitId::ReturnTypeImplTrait(_, rpit_idx) = id.loc(db) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some((rpit_idx, ty))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -707,6 +715,7 @@ struct InternedStandardTypes<'db> {
|
||||||
|
|
||||||
re_static: Region<'db>,
|
re_static: Region<'db>,
|
||||||
re_error: Region<'db>,
|
re_error: Region<'db>,
|
||||||
|
re_erased: Region<'db>,
|
||||||
|
|
||||||
empty_args: GenericArgs<'db>,
|
empty_args: GenericArgs<'db>,
|
||||||
empty_tys: Tys<'db>,
|
empty_tys: Tys<'db>,
|
||||||
|
|
@ -742,6 +751,7 @@ impl<'db> InternedStandardTypes<'db> {
|
||||||
|
|
||||||
re_static,
|
re_static,
|
||||||
re_error: Region::error(interner),
|
re_error: Region::error(interner),
|
||||||
|
re_erased: Region::new_erased(interner),
|
||||||
|
|
||||||
empty_args: GenericArgs::new_from_iter(interner, []),
|
empty_args: GenericArgs::new_from_iter(interner, []),
|
||||||
empty_tys: Tys::new_from_iter(interner, []),
|
empty_tys: Tys::new_from_iter(interner, []),
|
||||||
|
|
@ -848,11 +858,6 @@ fn find_continuable<'a, 'db>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ImplTraitReplacingMode<'db> {
|
|
||||||
ReturnPosition(FxHashSet<Ty<'db>>),
|
|
||||||
TypeAlias,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'body, 'db> InferenceContext<'body, 'db> {
|
impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
fn new(
|
fn new(
|
||||||
db: &'db dyn HirDatabase,
|
db: &'db dyn HirDatabase,
|
||||||
|
|
@ -861,7 +866,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
resolver: Resolver<'db>,
|
resolver: Resolver<'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let trait_env = db.trait_environment_for_body(owner);
|
let trait_env = db.trait_environment_for_body(owner);
|
||||||
let table = unify::InferenceTable::new(db, trait_env);
|
let table = unify::InferenceTable::new(db, trait_env, Some(owner));
|
||||||
let types = InternedStandardTypes::new(table.interner());
|
let types = InternedStandardTypes::new(table.interner());
|
||||||
InferenceContext {
|
InferenceContext {
|
||||||
result: InferenceResult::new(types.error),
|
result: InferenceResult::new(types.error),
|
||||||
|
|
@ -952,7 +957,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
// `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
|
// `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
|
||||||
// used this function for another workaround, mention it here. If you really need this function and believe that
|
// used this function for another workaround, mention it here. If you really need this function and believe that
|
||||||
// there is no problem in it being `pub(crate)`, remove this comment.
|
// there is no problem in it being `pub(crate)`, remove this comment.
|
||||||
pub(crate) fn resolve_all(self) -> InferenceResult<'db> {
|
fn resolve_all(self) -> InferenceResult<'db> {
|
||||||
let InferenceContext {
|
let InferenceContext {
|
||||||
mut table, mut result, tuple_field_accesses_rev, diagnostics, ..
|
mut table, mut result, tuple_field_accesses_rev, diagnostics, ..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
@ -967,7 +972,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
type_of_expr,
|
type_of_expr,
|
||||||
type_of_pat,
|
type_of_pat,
|
||||||
type_of_binding,
|
type_of_binding,
|
||||||
type_of_rpit,
|
type_of_opaque,
|
||||||
type_mismatches,
|
type_mismatches,
|
||||||
has_errors,
|
has_errors,
|
||||||
error_ty: _,
|
error_ty: _,
|
||||||
|
|
@ -999,11 +1004,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
*has_errors = *has_errors || ty.references_non_lt_error();
|
*has_errors = *has_errors || ty.references_non_lt_error();
|
||||||
}
|
}
|
||||||
type_of_binding.shrink_to_fit();
|
type_of_binding.shrink_to_fit();
|
||||||
for ty in type_of_rpit.values_mut() {
|
type_of_opaque.shrink_to_fit();
|
||||||
*ty = table.resolve_completely(*ty);
|
|
||||||
*has_errors = *has_errors || ty.references_non_lt_error();
|
|
||||||
}
|
|
||||||
type_of_rpit.shrink_to_fit();
|
|
||||||
|
|
||||||
*has_errors |= !type_mismatches.is_empty();
|
*has_errors |= !type_mismatches.is_empty();
|
||||||
|
|
||||||
|
|
@ -1084,9 +1085,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container),
|
LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Constants might be defining usage sites of TAITs.
|
|
||||||
self.make_tait_coercion_table(iter::once(return_ty));
|
|
||||||
|
|
||||||
self.return_ty = return_ty;
|
self.return_ty = return_ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1098,9 +1096,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
LifetimeElisionKind::Elided(self.types.re_static),
|
LifetimeElisionKind::Elided(self.types.re_static),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Statics might be defining usage sites of TAITs.
|
|
||||||
self.make_tait_coercion_table(iter::once(return_ty));
|
|
||||||
|
|
||||||
self.return_ty = return_ty;
|
self.return_ty = return_ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1138,16 +1133,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
let ty = self.process_user_written_ty(ty);
|
let ty = self.process_user_written_ty(ty);
|
||||||
self.write_binding_ty(self_param, ty);
|
self.write_binding_ty(self_param, ty);
|
||||||
}
|
}
|
||||||
let mut tait_candidates = FxHashSet::default();
|
|
||||||
for (ty, pat) in param_tys.zip(&*self.body.params) {
|
for (ty, pat) in param_tys.zip(&*self.body.params) {
|
||||||
let ty = self.process_user_written_ty(ty);
|
let ty = self.process_user_written_ty(ty);
|
||||||
|
|
||||||
self.infer_top_pat(*pat, ty, None);
|
self.infer_top_pat(*pat, ty, None);
|
||||||
if ty.flags().intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) {
|
|
||||||
tait_candidates.insert(ty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let return_ty = match data.ret_type {
|
self.return_ty = match data.ret_type {
|
||||||
Some(return_ty) => {
|
Some(return_ty) => {
|
||||||
let return_ty = self.with_ty_lowering(
|
let return_ty = self.with_ty_lowering(
|
||||||
&data.store,
|
&data.store,
|
||||||
|
|
@ -1158,45 +1149,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
ctx.lower_ty(return_ty)
|
ctx.lower_ty(return_ty)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let return_ty = self.insert_type_vars(return_ty);
|
self.process_user_written_ty(return_ty)
|
||||||
if let Some(rpits) = self.db.return_type_impl_traits(func) {
|
|
||||||
let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default());
|
|
||||||
let result = self.insert_inference_vars_for_impl_trait(return_ty, &mut mode);
|
|
||||||
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
|
|
||||||
tait_candidates.extend(taits);
|
|
||||||
}
|
|
||||||
let rpits = (*rpits).as_ref().skip_binder();
|
|
||||||
for (id, _) in rpits.impl_traits.iter() {
|
|
||||||
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
|
|
||||||
never!("Missed RPIT in `insert_inference_vars_for_rpit`");
|
|
||||||
e.insert(self.types.error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
return_ty
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => self.types.unit,
|
None => self.types.unit,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.return_ty = self.process_user_written_ty(return_ty);
|
|
||||||
self.return_coercion = Some(CoerceMany::new(self.return_ty));
|
self.return_coercion = Some(CoerceMany::new(self.return_ty));
|
||||||
|
|
||||||
// Functions might be defining usage sites of TAITs.
|
|
||||||
// To define an TAITs, that TAIT must appear in the function's signatures.
|
|
||||||
// So, it suffices to check for params and return types.
|
|
||||||
fold_tys(self.interner(), self.return_ty, |ty| {
|
|
||||||
match ty.kind() {
|
|
||||||
TyKind::Alias(AliasTyKind::Opaque, _) | TyKind::Infer(..) => {
|
|
||||||
tait_candidates.insert(self.return_ty);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
ty
|
|
||||||
});
|
|
||||||
|
|
||||||
self.make_tait_coercion_table(tait_candidates.iter().copied());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1204,193 +1162,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
self.table.interner()
|
self.table.interner()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_inference_vars_for_impl_trait<T>(
|
|
||||||
&mut self,
|
|
||||||
t: T,
|
|
||||||
mode: &mut ImplTraitReplacingMode<'db>,
|
|
||||||
) -> T
|
|
||||||
where
|
|
||||||
T: TypeFoldable<DbInterner<'db>>,
|
|
||||||
{
|
|
||||||
fold_tys(self.interner(), t, |ty| {
|
|
||||||
let ty = self.table.try_structurally_resolve_type(ty);
|
|
||||||
let opaque_ty_id = match ty.kind() {
|
|
||||||
TyKind::Alias(AliasTyKind::Opaque, alias_ty) => alias_ty.def_id.expect_opaque_ty(),
|
|
||||||
_ => return ty,
|
|
||||||
};
|
|
||||||
let (impl_traits, idx) = match self.db.lookup_intern_impl_trait_id(opaque_ty_id) {
|
|
||||||
// We don't replace opaque types from other kind with inference vars
|
|
||||||
// because `insert_inference_vars_for_impl_traits` for each kinds
|
|
||||||
// and unreplaced opaque types of other kind are resolved while
|
|
||||||
// inferencing because of `tait_coercion_table`.
|
|
||||||
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
|
|
||||||
if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
|
|
||||||
// RPITs don't have `tait_coercion_table`, so use inserted inference
|
|
||||||
// vars for them.
|
|
||||||
if let Some(ty) = self.result.type_of_rpit.get(idx) {
|
|
||||||
return *ty;
|
|
||||||
}
|
|
||||||
return ty;
|
|
||||||
}
|
|
||||||
(self.db.return_type_impl_traits(def), idx)
|
|
||||||
}
|
|
||||||
ImplTraitId::TypeAliasImplTrait(def, idx) => {
|
|
||||||
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
|
|
||||||
// Gather TAITs while replacing RPITs because TAITs inside RPITs
|
|
||||||
// may not visited while replacing TAITs
|
|
||||||
taits.insert(ty);
|
|
||||||
return ty;
|
|
||||||
}
|
|
||||||
(self.db.type_alias_impl_traits(def), idx)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let Some(impl_traits) = impl_traits else {
|
|
||||||
return ty;
|
|
||||||
};
|
|
||||||
let bounds =
|
|
||||||
(*impl_traits).as_ref().map_bound(|its| its.impl_traits[idx].predicates.as_slice());
|
|
||||||
let var = match self.result.type_of_rpit.entry(idx) {
|
|
||||||
Entry::Occupied(entry) => return *entry.get(),
|
|
||||||
Entry::Vacant(entry) => *entry.insert(self.table.next_ty_var()),
|
|
||||||
};
|
|
||||||
for clause in bounds.iter_identity_copied() {
|
|
||||||
let clause = self.insert_inference_vars_for_impl_trait(clause, mode);
|
|
||||||
self.table.register_predicate(Obligation::new(
|
|
||||||
self.interner(),
|
|
||||||
ObligationCause::new(),
|
|
||||||
self.table.trait_env.env,
|
|
||||||
clause,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
var
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The coercion of a non-inference var into an opaque type should fail,
|
|
||||||
/// but not in the defining sites of the TAITs.
|
|
||||||
/// In such cases, we insert an proxy inference var for each TAIT,
|
|
||||||
/// and coerce into it instead of TAIT itself.
|
|
||||||
///
|
|
||||||
/// The inference var stretagy is effective because;
|
|
||||||
///
|
|
||||||
/// - It can still unify types that coerced into TAITs
|
|
||||||
/// - We are pushing `impl Trait` bounds into it
|
|
||||||
///
|
|
||||||
/// This function inserts a map that maps the opaque type to that proxy inference var.
|
|
||||||
fn make_tait_coercion_table(&mut self, tait_candidates: impl Iterator<Item = Ty<'db>>) {
|
|
||||||
struct TypeAliasImplTraitCollector<'a, 'db> {
|
|
||||||
db: &'a dyn HirDatabase,
|
|
||||||
table: &'a mut InferenceTable<'db>,
|
|
||||||
assocs: FxHashMap<InternedOpaqueTyId, (ImplId, Ty<'db>)>,
|
|
||||||
non_assocs: FxHashMap<InternedOpaqueTyId, Ty<'db>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'db> TypeVisitor<DbInterner<'db>> for TypeAliasImplTraitCollector<'_, 'db> {
|
|
||||||
type Result = ();
|
|
||||||
|
|
||||||
fn visit_ty(&mut self, ty: Ty<'db>) {
|
|
||||||
let ty = self.table.try_structurally_resolve_type(ty);
|
|
||||||
|
|
||||||
if let TyKind::Alias(AliasTyKind::Opaque, alias_ty) = ty.kind()
|
|
||||||
&& let id = alias_ty.def_id.expect_opaque_ty()
|
|
||||||
&& let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
|
|
||||||
self.db.lookup_intern_impl_trait_id(id)
|
|
||||||
{
|
|
||||||
let loc = self.db.lookup_intern_type_alias(alias_id);
|
|
||||||
match loc.container {
|
|
||||||
ItemContainerId::ImplId(impl_id) => {
|
|
||||||
self.assocs.insert(id, (impl_id, ty));
|
|
||||||
}
|
|
||||||
ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => {
|
|
||||||
self.non_assocs.insert(id, ty);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty.super_visit_with(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut collector = TypeAliasImplTraitCollector {
|
|
||||||
db: self.db,
|
|
||||||
table: &mut self.table,
|
|
||||||
assocs: FxHashMap::default(),
|
|
||||||
non_assocs: FxHashMap::default(),
|
|
||||||
};
|
|
||||||
for ty in tait_candidates {
|
|
||||||
ty.visit_with(&mut collector);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-assoc TAITs can be define-used everywhere as long as they are
|
|
||||||
// in function signatures or const types, etc
|
|
||||||
let mut taits = collector.non_assocs;
|
|
||||||
|
|
||||||
// assoc TAITs(ATPITs) can be only define-used inside their impl block.
|
|
||||||
// They cannot be define-used in inner items like in the following;
|
|
||||||
//
|
|
||||||
// ```
|
|
||||||
// impl Trait for Struct {
|
|
||||||
// type Assoc = impl Default;
|
|
||||||
//
|
|
||||||
// fn assoc_fn() -> Self::Assoc {
|
|
||||||
// let foo: Self::Assoc = true; // Allowed here
|
|
||||||
//
|
|
||||||
// fn inner() -> Self::Assoc {
|
|
||||||
// false // Not allowed here
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// foo
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
let impl_id = match self.owner {
|
|
||||||
DefWithBodyId::FunctionId(it) => {
|
|
||||||
let loc = self.db.lookup_intern_function(it);
|
|
||||||
if let ItemContainerId::ImplId(impl_id) = loc.container {
|
|
||||||
Some(impl_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DefWithBodyId::ConstId(it) => {
|
|
||||||
let loc = self.db.lookup_intern_const(it);
|
|
||||||
if let ItemContainerId::ImplId(impl_id) = loc.container {
|
|
||||||
Some(impl_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(impl_id) = impl_id {
|
|
||||||
taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| {
|
|
||||||
if impl_ == impl_id { Some((id, ty)) } else { None }
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let tait_coercion_table: FxHashMap<_, _> = taits
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(id, ty)| {
|
|
||||||
if let ImplTraitId::TypeAliasImplTrait(..) = self.db.lookup_intern_impl_trait_id(id)
|
|
||||||
{
|
|
||||||
let ty = self.insert_inference_vars_for_impl_trait(
|
|
||||||
ty,
|
|
||||||
&mut ImplTraitReplacingMode::TypeAlias,
|
|
||||||
);
|
|
||||||
Some((id, ty))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if !tait_coercion_table.is_empty() {
|
|
||||||
self.table.tait_coercion_table = Some(tait_coercion_table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infer_body(&mut self) {
|
fn infer_body(&mut self) {
|
||||||
match self.return_coercion {
|
match self.return_coercion {
|
||||||
Some(_) => self.infer_return(self.body.body_expr),
|
Some(_) => self.infer_return(self.body.body_expr),
|
||||||
|
|
@ -2006,12 +1777,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||||
Some(struct_.into())
|
Some(struct_.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> {
|
fn get_traits_in_scope<'a>(
|
||||||
let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable();
|
resolver: &Resolver<'db>,
|
||||||
|
traits_in_scope: &'a FxHashSet<TraitId>,
|
||||||
|
) -> Either<FxHashSet<TraitId>, &'a FxHashSet<TraitId>> {
|
||||||
|
let mut b_traits = resolver.traits_in_scope_from_block_scopes().peekable();
|
||||||
if b_traits.peek().is_some() {
|
if b_traits.peek().is_some() {
|
||||||
Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect())
|
Either::Left(traits_in_scope.iter().copied().chain(b_traits).collect())
|
||||||
} else {
|
} else {
|
||||||
Either::Right(&self.traits_in_scope)
|
Either::Right(traits_in_scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,7 @@ use crate::{
|
||||||
next_solver::{
|
next_solver::{
|
||||||
Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
|
Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
|
||||||
Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed,
|
Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed,
|
||||||
GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, SolverDefId, TraitRef, Ty,
|
GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
|
||||||
TyKind,
|
|
||||||
infer::{
|
infer::{
|
||||||
InferCtxt, InferOk, InferResult,
|
InferCtxt, InferOk, InferResult,
|
||||||
relate::RelateResult,
|
relate::RelateResult,
|
||||||
|
|
@ -223,24 +222,6 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.
|
|
||||||
// FIXME(next-solver): This should not be here. This is not how rustc does thing, and it also not allows us
|
|
||||||
// to normalize opaques defined in our scopes. Instead, we should properly register
|
|
||||||
// `TypingMode::Analysis::defining_opaque_types_and_generators`, and rely on the solver to reveal
|
|
||||||
// them for us (we'll also need some global-like registry for the values, something we cannot
|
|
||||||
// really implement, therefore we can really support only RPITs and ITIAT or the new `#[define_opaque]`
|
|
||||||
// TAIT, not the old global TAIT).
|
|
||||||
let mut b = b;
|
|
||||||
if let Some(tait_table) = &self.table.tait_coercion_table
|
|
||||||
&& let TyKind::Alias(rustc_type_ir::Opaque, opaque_ty) = b.kind()
|
|
||||||
&& let SolverDefId::InternedOpaqueTyId(opaque_ty_id) = opaque_ty.def_id
|
|
||||||
&& !matches!(a.kind(), TyKind::Infer(..) | TyKind::Alias(rustc_type_ir::Opaque, _))
|
|
||||||
&& let Some(ty) = tait_table.get(&opaque_ty_id)
|
|
||||||
{
|
|
||||||
b = self.table.shallow_resolve(*ty);
|
|
||||||
}
|
|
||||||
let b = b;
|
|
||||||
|
|
||||||
// Coercing *from* an unresolved inference variable means that
|
// Coercing *from* an unresolved inference variable means that
|
||||||
// we have no information about the source type. This will always
|
// we have no information about the source type. This will always
|
||||||
// ultimately fall back to some form of subtyping.
|
// ultimately fall back to some form of subtyping.
|
||||||
|
|
@ -1528,7 +1509,7 @@ fn coerce<'db>(
|
||||||
env: Arc<TraitEnvironment<'db>>,
|
env: Arc<TraitEnvironment<'db>>,
|
||||||
tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>,
|
tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>,
|
||||||
) -> Result<(Vec<Adjustment<'db>>, Ty<'db>), TypeError<DbInterner<'db>>> {
|
) -> Result<(Vec<Adjustment<'db>>, Ty<'db>), TypeError<DbInterner<'db>>> {
|
||||||
let mut table = InferenceTable::new(db, env);
|
let mut table = InferenceTable::new(db, env, None);
|
||||||
let interner = table.interner();
|
let interner = table.interner();
|
||||||
let ((ty1_with_vars, ty2_with_vars), vars) = table.infer_ctxt.instantiate_canonical(tys);
|
let ((ty1_with_vars, ty2_with_vars), vars) = table.infer_ctxt.instantiate_canonical(tys);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1458,10 +1458,11 @@ impl<'db> InferenceContext<'_, 'db> {
|
||||||
) -> Ty<'db> {
|
) -> Ty<'db> {
|
||||||
let coerce_ty = expected.coercion_target_type(&mut self.table);
|
let coerce_ty = expected.coercion_target_type(&mut self.table);
|
||||||
let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr);
|
let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr);
|
||||||
let prev_env = block_id.map(|block_id| {
|
let prev_state = block_id.map(|block_id| {
|
||||||
let prev_env = self.table.trait_env.clone();
|
let prev_env = self.table.trait_env.clone();
|
||||||
TraitEnvironment::with_block(&mut self.table.trait_env, block_id);
|
TraitEnvironment::with_block(&mut self.table.trait_env, block_id);
|
||||||
prev_env
|
let prev_block = self.table.infer_ctxt.interner.block.replace(block_id);
|
||||||
|
(prev_env, prev_block)
|
||||||
});
|
});
|
||||||
|
|
||||||
let (break_ty, ty) =
|
let (break_ty, ty) =
|
||||||
|
|
@ -1576,8 +1577,9 @@ impl<'db> InferenceContext<'_, 'db> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.resolver.reset_to_guard(g);
|
self.resolver.reset_to_guard(g);
|
||||||
if let Some(prev_env) = prev_env {
|
if let Some((prev_env, prev_block)) = prev_state {
|
||||||
self.table.trait_env = prev_env;
|
self.table.trait_env = prev_env;
|
||||||
|
self.table.infer_ctxt.interner.block = prev_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
break_ty.unwrap_or(ty)
|
break_ty.unwrap_or(ty)
|
||||||
|
|
@ -1689,10 +1691,11 @@ impl<'db> InferenceContext<'_, 'db> {
|
||||||
// work out while people are typing
|
// work out while people are typing
|
||||||
let canonicalized_receiver = self.canonicalize(receiver_ty);
|
let canonicalized_receiver = self.canonicalize(receiver_ty);
|
||||||
let resolved = method_resolution::lookup_method(
|
let resolved = method_resolution::lookup_method(
|
||||||
self.db,
|
|
||||||
&canonicalized_receiver,
|
&canonicalized_receiver,
|
||||||
self.table.trait_env.clone(),
|
&mut self.table,
|
||||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||||
|
.as_ref()
|
||||||
|
.left_or_else(|&it| it),
|
||||||
VisibleFromModule::Filter(self.resolver.module()),
|
VisibleFromModule::Filter(self.resolver.module()),
|
||||||
name,
|
name,
|
||||||
);
|
);
|
||||||
|
|
@ -1844,10 +1847,11 @@ impl<'db> InferenceContext<'_, 'db> {
|
||||||
let canonicalized_receiver = self.canonicalize(receiver_ty);
|
let canonicalized_receiver = self.canonicalize(receiver_ty);
|
||||||
|
|
||||||
let resolved = method_resolution::lookup_method(
|
let resolved = method_resolution::lookup_method(
|
||||||
self.db,
|
|
||||||
&canonicalized_receiver,
|
&canonicalized_receiver,
|
||||||
self.table.trait_env.clone(),
|
&mut self.table,
|
||||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||||
|
.as_ref()
|
||||||
|
.left_or_else(|&it| it),
|
||||||
VisibleFromModule::Filter(self.resolver.module()),
|
VisibleFromModule::Filter(self.resolver.module()),
|
||||||
method_name,
|
method_name,
|
||||||
);
|
);
|
||||||
|
|
@ -1892,9 +1896,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
||||||
|
|
||||||
let assoc_func_with_same_name = method_resolution::iterate_method_candidates(
|
let assoc_func_with_same_name = method_resolution::iterate_method_candidates(
|
||||||
&canonicalized_receiver,
|
&canonicalized_receiver,
|
||||||
self.db,
|
&mut self.table,
|
||||||
self.table.trait_env.clone(),
|
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
.as_ref()
|
||||||
|
.left_or_else(|&it| it),
|
||||||
VisibleFromModule::Filter(self.resolver.module()),
|
VisibleFromModule::Filter(self.resolver.module()),
|
||||||
Some(method_name),
|
Some(method_name),
|
||||||
method_resolution::LookupMode::Path,
|
method_resolution::LookupMode::Path,
|
||||||
|
|
|
||||||
147
src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs
Normal file
147
src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
//! Defining opaque types via inference.
|
||||||
|
|
||||||
|
use rustc_type_ir::{TypeVisitableExt, fold_regions};
|
||||||
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
infer::InferenceContext,
|
||||||
|
next_solver::{
|
||||||
|
EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode,
|
||||||
|
infer::{opaque_types::OpaqueHiddenType, traits::ObligationCause},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'db> InferenceContext<'_, 'db> {
|
||||||
|
/// This takes all the opaque type uses during HIR typeck. It first computes
|
||||||
|
/// the concrete hidden type by iterating over all defining uses.
|
||||||
|
///
|
||||||
|
/// A use during HIR typeck is defining if all non-lifetime arguments are
|
||||||
|
/// unique generic parameters and the hidden type does not reference any
|
||||||
|
/// inference variables.
|
||||||
|
///
|
||||||
|
/// It then uses these defining uses to guide inference for all other uses.
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
pub(super) fn handle_opaque_type_uses(&mut self) {
|
||||||
|
// We clone the opaques instead of stealing them here as they are still used for
|
||||||
|
// normalization in the next generation trait solver.
|
||||||
|
let opaque_types: Vec<_> = self.table.infer_ctxt.clone_opaque_types();
|
||||||
|
|
||||||
|
self.compute_definition_site_hidden_types(opaque_types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(unused, reason = "rustc has this")]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum UsageKind<'db> {
|
||||||
|
None,
|
||||||
|
NonDefiningUse(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>),
|
||||||
|
UnconstrainedHiddenType(OpaqueHiddenType<'db>),
|
||||||
|
HasDefiningUse(OpaqueHiddenType<'db>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> UsageKind<'db> {
|
||||||
|
fn merge(&mut self, other: UsageKind<'db>) {
|
||||||
|
match (&*self, &other) {
|
||||||
|
(UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(),
|
||||||
|
(UsageKind::None, _) => *self = other,
|
||||||
|
// When mergining non-defining uses, prefer earlier ones. This means
|
||||||
|
// the error happens as early as possible.
|
||||||
|
(
|
||||||
|
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
|
||||||
|
UsageKind::NonDefiningUse(..),
|
||||||
|
) => {}
|
||||||
|
// When merging unconstrained hidden types, we prefer later ones. This is
|
||||||
|
// used as in most cases, the defining use is the final return statement
|
||||||
|
// of our function, and other uses with defining arguments are likely not
|
||||||
|
// intended to be defining.
|
||||||
|
(
|
||||||
|
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
|
||||||
|
UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_),
|
||||||
|
) => *self = other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> InferenceContext<'_, 'db> {
|
||||||
|
fn compute_definition_site_hidden_types(
|
||||||
|
&mut self,
|
||||||
|
mut opaque_types: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>,
|
||||||
|
) {
|
||||||
|
for entry in opaque_types.iter_mut() {
|
||||||
|
*entry = self.table.infer_ctxt.resolve_vars_if_possible(*entry);
|
||||||
|
}
|
||||||
|
debug!(?opaque_types);
|
||||||
|
|
||||||
|
let interner = self.interner();
|
||||||
|
let TypingMode::Analysis { defining_opaque_types_and_generators } =
|
||||||
|
self.table.infer_ctxt.typing_mode()
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
for def_id in defining_opaque_types_and_generators {
|
||||||
|
let def_id = match def_id {
|
||||||
|
SolverDefId::InternedOpaqueTyId(it) => it,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We do actually need to check this the second pass (we can't just
|
||||||
|
// store this), because we can go from `UnconstrainedHiddenType` to
|
||||||
|
// `HasDefiningUse` (because of fallback)
|
||||||
|
let mut usage_kind = UsageKind::None;
|
||||||
|
for &(opaque_type_key, hidden_type) in &opaque_types {
|
||||||
|
if opaque_type_key.def_id != def_id.into() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type));
|
||||||
|
|
||||||
|
if let UsageKind::HasDefiningUse(..) = usage_kind {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let UsageKind::HasDefiningUse(ty) = usage_kind {
|
||||||
|
for &(opaque_type_key, hidden_type) in &opaque_types {
|
||||||
|
if opaque_type_key.def_id != def_id.into() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
EarlyBinder::bind(ty.ty).instantiate(interner, opaque_type_key.args);
|
||||||
|
self.demand_eqtype(expected, hidden_type.ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.result.type_of_opaque.insert(def_id, ty.ty);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.result.type_of_opaque.insert(def_id, self.types.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self), ret)]
|
||||||
|
fn consider_opaque_type_use(
|
||||||
|
&self,
|
||||||
|
opaque_type_key: OpaqueTypeKey<'db>,
|
||||||
|
hidden_type: OpaqueHiddenType<'db>,
|
||||||
|
) -> UsageKind<'db> {
|
||||||
|
// We ignore uses of the opaque if they have any inference variables
|
||||||
|
// as this can frequently happen with recursive calls.
|
||||||
|
//
|
||||||
|
// See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`.
|
||||||
|
if hidden_type.ty.has_non_region_infer() {
|
||||||
|
return UsageKind::UnconstrainedHiddenType(hidden_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cause = ObligationCause::new();
|
||||||
|
let at = self.table.infer_ctxt.at(&cause, self.table.trait_env.env);
|
||||||
|
let hidden_type = match at.deeply_normalize(hidden_type) {
|
||||||
|
Ok(hidden_type) => hidden_type,
|
||||||
|
Err(_errors) => OpaqueHiddenType { ty: self.types.error },
|
||||||
|
};
|
||||||
|
let hidden_type = fold_regions(self.interner(), hidden_type, |_, _| self.types.re_erased);
|
||||||
|
UsageKind::HasDefiningUse(hidden_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -310,9 +310,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
||||||
let mut not_visible = None;
|
let mut not_visible = None;
|
||||||
let res = method_resolution::iterate_method_candidates(
|
let res = method_resolution::iterate_method_candidates(
|
||||||
&canonical_ty,
|
&canonical_ty,
|
||||||
self.db,
|
&mut self.table,
|
||||||
self.table.trait_env.clone(),
|
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
.as_ref()
|
||||||
|
.left_or_else(|&it| it),
|
||||||
VisibleFromModule::Filter(self.resolver.module()),
|
VisibleFromModule::Filter(self.resolver.module()),
|
||||||
Some(name),
|
Some(name),
|
||||||
method_resolution::LookupMode::Path,
|
method_resolution::LookupMode::Path,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use hir_def::{AdtId, GenericParamId, lang_item::LangItem};
|
use hir_def::{AdtId, DefWithBodyId, GenericParamId, lang_item::LangItem};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::FxHashSet;
|
||||||
use rustc_type_ir::{
|
use rustc_type_ir::{
|
||||||
DebruijnIndex, InferConst, InferTy, RegionVid, TyVid, TypeFoldable, TypeFolder,
|
DebruijnIndex, InferConst, InferTy, RegionVid, TyVid, TypeFoldable, TypeFolder,
|
||||||
TypeSuperFoldable, TypeVisitableExt, UpcastFrom,
|
TypeSuperFoldable, TypeVisitableExt, UpcastFrom,
|
||||||
|
|
@ -17,12 +17,12 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
TraitEnvironment,
|
TraitEnvironment,
|
||||||
db::{HirDatabase, InternedOpaqueTyId},
|
db::HirDatabase,
|
||||||
infer::InferenceContext,
|
infer::InferenceContext,
|
||||||
next_solver::{
|
next_solver::{
|
||||||
self, AliasTy, Binder, Canonical, ClauseKind, Const, ConstKind, DbInterner,
|
self, AliasTy, Binder, Canonical, ClauseKind, Const, ConstKind, DbInterner,
|
||||||
ErrorGuaranteed, GenericArg, GenericArgs, Predicate, PredicateKind, Region, RegionKind,
|
ErrorGuaranteed, GenericArg, GenericArgs, Predicate, PredicateKind, Region, RegionKind,
|
||||||
SolverDefId, SolverDefIds, TraitRef, Ty, TyKind, TypingMode,
|
SolverDefId, TraitRef, Ty, TyKind, TypingMode,
|
||||||
fulfill::{FulfillmentCtxt, NextSolverError},
|
fulfill::{FulfillmentCtxt, NextSolverError},
|
||||||
infer::{
|
infer::{
|
||||||
DbInternerInferExt, InferCtxt, InferOk, InferResult,
|
DbInternerInferExt, InferCtxt, InferOk, InferResult,
|
||||||
|
|
@ -139,10 +139,7 @@ fn could_unify_impl<'db>(
|
||||||
select: for<'a> fn(&mut ObligationCtxt<'a, 'db>) -> Vec<NextSolverError<'db>>,
|
select: for<'a> fn(&mut ObligationCtxt<'a, 'db>) -> Vec<NextSolverError<'db>>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
|
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
|
||||||
// FIXME(next-solver): I believe this should use `PostAnalysis` (this is only used for IDE things),
|
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||||
// but this causes some bug because of our incorrect impl of `type_of_opaque_hir_typeck()` for TAIT
|
|
||||||
// and async blocks.
|
|
||||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
|
||||||
let cause = ObligationCause::dummy();
|
let cause = ObligationCause::dummy();
|
||||||
let at = infcx.at(&cause, env.env);
|
let at = infcx.at(&cause, env.env);
|
||||||
let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys);
|
let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys);
|
||||||
|
|
@ -158,7 +155,6 @@ fn could_unify_impl<'db>(
|
||||||
pub(crate) struct InferenceTable<'db> {
|
pub(crate) struct InferenceTable<'db> {
|
||||||
pub(crate) db: &'db dyn HirDatabase,
|
pub(crate) db: &'db dyn HirDatabase,
|
||||||
pub(crate) trait_env: Arc<TraitEnvironment<'db>>,
|
pub(crate) trait_env: Arc<TraitEnvironment<'db>>,
|
||||||
pub(crate) tait_coercion_table: Option<FxHashMap<InternedOpaqueTyId, Ty<'db>>>,
|
|
||||||
pub(crate) infer_ctxt: InferCtxt<'db>,
|
pub(crate) infer_ctxt: InferCtxt<'db>,
|
||||||
pub(super) fulfillment_cx: FulfillmentCtxt<'db>,
|
pub(super) fulfillment_cx: FulfillmentCtxt<'db>,
|
||||||
pub(super) diverging_type_vars: FxHashSet<Ty<'db>>,
|
pub(super) diverging_type_vars: FxHashSet<Ty<'db>>,
|
||||||
|
|
@ -170,15 +166,23 @@ pub(crate) struct InferenceTableSnapshot<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> InferenceTable<'db> {
|
impl<'db> InferenceTable<'db> {
|
||||||
pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc<TraitEnvironment<'db>>) -> Self {
|
/// Inside hir-ty you should use this for inference only, and always pass `owner`.
|
||||||
|
/// Outside it, always pass `owner = None`.
|
||||||
|
pub(crate) fn new(
|
||||||
|
db: &'db dyn HirDatabase,
|
||||||
|
trait_env: Arc<TraitEnvironment<'db>>,
|
||||||
|
owner: Option<DefWithBodyId>,
|
||||||
|
) -> Self {
|
||||||
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
|
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
|
||||||
let infer_ctxt = interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis {
|
let typing_mode = match owner {
|
||||||
defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []),
|
Some(owner) => TypingMode::typeck_for_body(interner, owner.into()),
|
||||||
});
|
// IDE things wants to reveal opaque types.
|
||||||
|
None => TypingMode::PostAnalysis,
|
||||||
|
};
|
||||||
|
let infer_ctxt = interner.infer_ctxt().build(typing_mode);
|
||||||
InferenceTable {
|
InferenceTable {
|
||||||
db,
|
db,
|
||||||
trait_env,
|
trait_env,
|
||||||
tait_coercion_table: None,
|
|
||||||
fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt),
|
fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt),
|
||||||
infer_ctxt,
|
infer_ctxt,
|
||||||
diverging_type_vars: FxHashSet::default(),
|
diverging_type_vars: FxHashSet::default(),
|
||||||
|
|
@ -698,40 +702,7 @@ impl<'db> InferenceTable<'db> {
|
||||||
where
|
where
|
||||||
T: TypeFoldable<DbInterner<'db>>,
|
T: TypeFoldable<DbInterner<'db>>,
|
||||||
{
|
{
|
||||||
struct Folder<'a, 'db> {
|
self.infer_ctxt.insert_type_vars(ty)
|
||||||
table: &'a mut InferenceTable<'db>,
|
|
||||||
}
|
|
||||||
impl<'db> TypeFolder<DbInterner<'db>> for Folder<'_, 'db> {
|
|
||||||
fn cx(&self) -> DbInterner<'db> {
|
|
||||||
self.table.interner()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
|
|
||||||
if !ty.references_error() {
|
|
||||||
return ty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ty.is_ty_error() { self.table.next_ty_var() } else { ty.super_fold_with(self) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
|
|
||||||
if !ct.references_error() {
|
|
||||||
return ct;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ct.is_ct_error() {
|
|
||||||
self.table.next_const_var()
|
|
||||||
} else {
|
|
||||||
ct.super_fold_with(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
|
|
||||||
if r.is_error() { self.table.next_region_var() } else { r }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty.fold_with(&mut Folder { table: self })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
|
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,11 @@ mod infer;
|
||||||
mod inhabitedness;
|
mod inhabitedness;
|
||||||
mod lower;
|
mod lower;
|
||||||
pub mod next_solver;
|
pub mod next_solver;
|
||||||
|
mod opaques;
|
||||||
mod specialization;
|
mod specialization;
|
||||||
mod target_feature;
|
mod target_feature;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod variance;
|
||||||
|
|
||||||
pub mod autoderef;
|
pub mod autoderef;
|
||||||
pub mod consteval;
|
pub mod consteval;
|
||||||
|
|
@ -50,7 +52,6 @@ pub mod traits;
|
||||||
mod test_db;
|
mod test_db;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
mod variance;
|
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
|
@ -471,6 +472,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// To be used from `hir` only.
|
||||||
pub fn callable_sig_from_fn_trait<'db>(
|
pub fn callable_sig_from_fn_trait<'db>(
|
||||||
self_ty: Ty<'db>,
|
self_ty: Ty<'db>,
|
||||||
trait_env: Arc<TraitEnvironment<'db>>,
|
trait_env: Arc<TraitEnvironment<'db>>,
|
||||||
|
|
@ -482,7 +484,7 @@ pub fn callable_sig_from_fn_trait<'db>(
|
||||||
.trait_items(db)
|
.trait_items(db)
|
||||||
.associated_type_by_name(&Name::new_symbol_root(sym::Output))?;
|
.associated_type_by_name(&Name::new_symbol_root(sym::Output))?;
|
||||||
|
|
||||||
let mut table = InferenceTable::new(db, trait_env.clone());
|
let mut table = InferenceTable::new(db, trait_env.clone(), None);
|
||||||
|
|
||||||
// Register two obligations:
|
// Register two obligations:
|
||||||
// - Self: FnOnce<?args_ty>
|
// - Self: FnOnce<?args_ty>
|
||||||
|
|
|
||||||
|
|
@ -489,9 +489,8 @@ pub fn def_crates<'db>(
|
||||||
|
|
||||||
/// Look up the method with the given name.
|
/// Look up the method with the given name.
|
||||||
pub(crate) fn lookup_method<'db>(
|
pub(crate) fn lookup_method<'db>(
|
||||||
db: &'db dyn HirDatabase,
|
|
||||||
ty: &Canonical<'db, Ty<'db>>,
|
ty: &Canonical<'db, Ty<'db>>,
|
||||||
env: Arc<TraitEnvironment<'db>>,
|
table: &mut InferenceTable<'db>,
|
||||||
traits_in_scope: &FxHashSet<TraitId>,
|
traits_in_scope: &FxHashSet<TraitId>,
|
||||||
visible_from_module: VisibleFromModule,
|
visible_from_module: VisibleFromModule,
|
||||||
name: &Name,
|
name: &Name,
|
||||||
|
|
@ -499,8 +498,7 @@ pub(crate) fn lookup_method<'db>(
|
||||||
let mut not_visible = None;
|
let mut not_visible = None;
|
||||||
let res = iterate_method_candidates(
|
let res = iterate_method_candidates(
|
||||||
ty,
|
ty,
|
||||||
db,
|
table,
|
||||||
env,
|
|
||||||
traits_in_scope,
|
traits_in_scope,
|
||||||
visible_from_module,
|
visible_from_module,
|
||||||
Some(name),
|
Some(name),
|
||||||
|
|
@ -656,8 +654,7 @@ impl ReceiverAdjustments {
|
||||||
// FIXME add a context type here?
|
// FIXME add a context type here?
|
||||||
pub(crate) fn iterate_method_candidates<'db, T>(
|
pub(crate) fn iterate_method_candidates<'db, T>(
|
||||||
ty: &Canonical<'db, Ty<'db>>,
|
ty: &Canonical<'db, Ty<'db>>,
|
||||||
db: &'db dyn HirDatabase,
|
table: &mut InferenceTable<'db>,
|
||||||
env: Arc<TraitEnvironment<'db>>,
|
|
||||||
traits_in_scope: &FxHashSet<TraitId>,
|
traits_in_scope: &FxHashSet<TraitId>,
|
||||||
visible_from_module: VisibleFromModule,
|
visible_from_module: VisibleFromModule,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
|
|
@ -665,10 +662,9 @@ pub(crate) fn iterate_method_candidates<'db, T>(
|
||||||
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>,
|
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let mut slot = None;
|
let mut slot = None;
|
||||||
_ = iterate_method_candidates_dyn(
|
_ = iterate_method_candidates_dyn_impl(
|
||||||
ty,
|
ty,
|
||||||
db,
|
table,
|
||||||
env,
|
|
||||||
traits_in_scope,
|
traits_in_scope,
|
||||||
visible_from_module,
|
visible_from_module,
|
||||||
name,
|
name,
|
||||||
|
|
@ -985,6 +981,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool
|
||||||
is_not_orphan
|
is_not_orphan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// To be used from `hir` only.
|
||||||
pub fn iterate_path_candidates<'db>(
|
pub fn iterate_path_candidates<'db>(
|
||||||
ty: &Canonical<'db, Ty<'db>>,
|
ty: &Canonical<'db, Ty<'db>>,
|
||||||
db: &'db dyn HirDatabase,
|
db: &'db dyn HirDatabase,
|
||||||
|
|
@ -1007,6 +1004,7 @@ pub fn iterate_path_candidates<'db>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// To be used from `hir` only.
|
||||||
pub fn iterate_method_candidates_dyn<'db>(
|
pub fn iterate_method_candidates_dyn<'db>(
|
||||||
ty: &Canonical<'db, Ty<'db>>,
|
ty: &Canonical<'db, Ty<'db>>,
|
||||||
db: &'db dyn HirDatabase,
|
db: &'db dyn HirDatabase,
|
||||||
|
|
@ -1016,6 +1014,26 @@ pub fn iterate_method_candidates_dyn<'db>(
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
mode: LookupMode,
|
mode: LookupMode,
|
||||||
callback: &mut dyn MethodCandidateCallback,
|
callback: &mut dyn MethodCandidateCallback,
|
||||||
|
) -> ControlFlow<()> {
|
||||||
|
iterate_method_candidates_dyn_impl(
|
||||||
|
ty,
|
||||||
|
&mut InferenceTable::new(db, env, None),
|
||||||
|
traits_in_scope,
|
||||||
|
visible_from_module,
|
||||||
|
name,
|
||||||
|
mode,
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iterate_method_candidates_dyn_impl<'db>(
|
||||||
|
ty: &Canonical<'db, Ty<'db>>,
|
||||||
|
table: &mut InferenceTable<'db>,
|
||||||
|
traits_in_scope: &FxHashSet<TraitId>,
|
||||||
|
visible_from_module: VisibleFromModule,
|
||||||
|
name: Option<&Name>,
|
||||||
|
mode: LookupMode,
|
||||||
|
callback: &mut dyn MethodCandidateCallback,
|
||||||
) -> ControlFlow<()> {
|
) -> ControlFlow<()> {
|
||||||
let _p = tracing::info_span!(
|
let _p = tracing::info_span!(
|
||||||
"iterate_method_candidates_dyn",
|
"iterate_method_candidates_dyn",
|
||||||
|
|
@ -1046,28 +1064,28 @@ pub fn iterate_method_candidates_dyn<'db>(
|
||||||
// the methods by autoderef order of *receiver types*, not *self
|
// the methods by autoderef order of *receiver types*, not *self
|
||||||
// types*.
|
// types*.
|
||||||
|
|
||||||
let mut table = InferenceTable::new(db, env);
|
table.run_in_snapshot(|table| {
|
||||||
let ty = table.instantiate_canonical(*ty);
|
let ty = table.instantiate_canonical(*ty);
|
||||||
let deref_chain = autoderef_method_receiver(&mut table, ty);
|
let deref_chain = autoderef_method_receiver(table, ty);
|
||||||
|
|
||||||
deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
|
deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
|
||||||
iterate_method_candidates_with_autoref(
|
iterate_method_candidates_with_autoref(
|
||||||
&mut table,
|
table,
|
||||||
receiver_ty,
|
receiver_ty,
|
||||||
adj,
|
adj,
|
||||||
traits_in_scope,
|
traits_in_scope,
|
||||||
visible_from_module,
|
visible_from_module,
|
||||||
name,
|
name,
|
||||||
callback,
|
callback,
|
||||||
)
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
LookupMode::Path => {
|
LookupMode::Path => {
|
||||||
// No autoderef for path lookups
|
// No autoderef for path lookups
|
||||||
iterate_method_candidates_for_self_ty(
|
iterate_method_candidates_for_self_ty(
|
||||||
ty,
|
ty,
|
||||||
db,
|
table,
|
||||||
env,
|
|
||||||
traits_in_scope,
|
traits_in_scope,
|
||||||
visible_from_module,
|
visible_from_module,
|
||||||
name,
|
name,
|
||||||
|
|
@ -1250,39 +1268,39 @@ fn iterate_method_candidates_by_receiver<'db>(
|
||||||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||||
fn iterate_method_candidates_for_self_ty<'db>(
|
fn iterate_method_candidates_for_self_ty<'db>(
|
||||||
self_ty: &Canonical<'db, Ty<'db>>,
|
self_ty: &Canonical<'db, Ty<'db>>,
|
||||||
db: &'db dyn HirDatabase,
|
table: &mut InferenceTable<'db>,
|
||||||
env: Arc<TraitEnvironment<'db>>,
|
|
||||||
traits_in_scope: &FxHashSet<TraitId>,
|
traits_in_scope: &FxHashSet<TraitId>,
|
||||||
visible_from_module: VisibleFromModule,
|
visible_from_module: VisibleFromModule,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
callback: &mut dyn MethodCandidateCallback,
|
callback: &mut dyn MethodCandidateCallback,
|
||||||
) -> ControlFlow<()> {
|
) -> ControlFlow<()> {
|
||||||
let mut table = InferenceTable::new(db, env);
|
table.run_in_snapshot(|table| {
|
||||||
let self_ty = table.instantiate_canonical(*self_ty);
|
let self_ty = table.instantiate_canonical(*self_ty);
|
||||||
iterate_inherent_methods(
|
iterate_inherent_methods(
|
||||||
self_ty,
|
self_ty,
|
||||||
&mut table,
|
table,
|
||||||
name,
|
name,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
visible_from_module,
|
visible_from_module,
|
||||||
LookupMode::Path,
|
LookupMode::Path,
|
||||||
&mut |adjustments, item, is_visible| {
|
&mut |adjustments, item, is_visible| {
|
||||||
callback.on_inherent_method(adjustments, item, is_visible)
|
callback.on_inherent_method(adjustments, item, is_visible)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
iterate_trait_method_candidates(
|
iterate_trait_method_candidates(
|
||||||
self_ty,
|
self_ty,
|
||||||
&mut table,
|
table,
|
||||||
traits_in_scope,
|
traits_in_scope,
|
||||||
name,
|
name,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
LookupMode::Path,
|
LookupMode::Path,
|
||||||
&mut |adjustments, item, is_visible| {
|
&mut |adjustments, item, is_visible| {
|
||||||
callback.on_trait_method(adjustments, item, is_visible)
|
callback.on_trait_method(adjustments, item, is_visible)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
|
#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
display::DisplayTarget,
|
display::DisplayTarget,
|
||||||
mir::OperandKind,
|
mir::OperandKind,
|
||||||
next_solver::{
|
next_solver::{
|
||||||
DbInterner, GenericArgs, SolverDefIds, Ty, TypingMode,
|
DbInterner, GenericArgs, Ty, TypingMode,
|
||||||
infer::{DbInternerInferExt, InferCtxt},
|
infer::{DbInternerInferExt, InferCtxt},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -100,11 +100,11 @@ pub fn borrowck_query<'db>(
|
||||||
let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
|
let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
|
||||||
let env = db.trait_environment_for_body(def);
|
let env = db.trait_environment_for_body(def);
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
|
// This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`.
|
||||||
|
let typing_mode = TypingMode::borrowck(interner, def.into());
|
||||||
all_mir_bodies(db, def, |body| {
|
all_mir_bodies(db, def, |body| {
|
||||||
// FIXME(next-solver): Opaques.
|
// FIXME(next-solver): Opaques.
|
||||||
let infcx = interner.infer_ctxt().build(TypingMode::Borrowck {
|
let infcx = interner.infer_ctxt().build(typing_mode);
|
||||||
defining_opaque_types: SolverDefIds::new_from_iter(interner, []),
|
|
||||||
});
|
|
||||||
res.push(BorrowckResult {
|
res.push(BorrowckResult {
|
||||||
mutability_of_locals: mutability_of_locals(&infcx, &body),
|
mutability_of_locals: mutability_of_locals(&infcx, &body),
|
||||||
moved_out_of_ref: moved_out_of_ref(&infcx, &env, &body),
|
moved_out_of_ref: moved_out_of_ref(&infcx, &env, &body),
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,29 @@ impl From<DefWithBodyId> for SolverDefId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SolverDefId> for DefWithBodyId {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
|
||||||
|
let id = match value {
|
||||||
|
SolverDefId::ConstId(id) => id.into(),
|
||||||
|
SolverDefId::FunctionId(id) => id.into(),
|
||||||
|
SolverDefId::StaticId(id) => id.into(),
|
||||||
|
SolverDefId::EnumVariantId(id) | SolverDefId::Ctor(Ctor::Enum(id)) => id.into(),
|
||||||
|
SolverDefId::InternedOpaqueTyId(_)
|
||||||
|
| SolverDefId::TraitId(_)
|
||||||
|
| SolverDefId::TypeAliasId(_)
|
||||||
|
| SolverDefId::ImplId(_)
|
||||||
|
| SolverDefId::InternedClosureId(_)
|
||||||
|
| SolverDefId::InternedCoroutineId(_)
|
||||||
|
| SolverDefId::Ctor(Ctor::Struct(_))
|
||||||
|
| SolverDefId::AdtId(_) => return Err(()),
|
||||||
|
};
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<SolverDefId> for GenericDefId {
|
impl TryFrom<SolverDefId> for GenericDefId {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,14 @@ impl<'db> GenericArg<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn expect_region(self) -> Region<'db> {
|
||||||
|
match self {
|
||||||
|
GenericArg::Lifetime(region) => region,
|
||||||
|
_ => panic!("expected a region, got {self:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn error_from_id(interner: DbInterner<'db>, id: GenericParamId) -> GenericArg<'db> {
|
pub fn error_from_id(interner: DbInterner<'db>, id: GenericParamId) -> GenericArg<'db> {
|
||||||
match id {
|
match id {
|
||||||
GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(),
|
GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(),
|
||||||
|
|
|
||||||
|
|
@ -13,27 +13,27 @@ use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage};
|
||||||
use region_constraints::{RegionConstraintCollector, RegionConstraintStorage};
|
use region_constraints::{RegionConstraintCollector, RegionConstraintStorage};
|
||||||
use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
|
use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
|
||||||
use rustc_pattern_analysis::Captures;
|
use rustc_pattern_analysis::Captures;
|
||||||
use rustc_type_ir::TypeFoldable;
|
|
||||||
use rustc_type_ir::error::{ExpectedFound, TypeError};
|
|
||||||
use rustc_type_ir::inherent::{
|
|
||||||
Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _,
|
|
||||||
};
|
|
||||||
use rustc_type_ir::{
|
use rustc_type_ir::{
|
||||||
ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy,
|
ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy,
|
||||||
IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex,
|
IntVarValue, IntVid, OutlivesPredicate, RegionVid, TermKind, TyVid, TypeFoldable, TypeFolder,
|
||||||
|
TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
|
||||||
|
error::{ExpectedFound, TypeError},
|
||||||
|
inherent::{
|
||||||
|
Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use rustc_type_ir::{TermKind, TypeVisitableExt};
|
|
||||||
use snapshot::undo_log::InferCtxtUndoLogs;
|
use snapshot::undo_log::InferCtxtUndoLogs;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
use traits::{ObligationCause, PredicateObligations};
|
use traits::{ObligationCause, PredicateObligations};
|
||||||
use type_variable::TypeVariableOrigin;
|
use type_variable::TypeVariableOrigin;
|
||||||
use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
|
use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
|
||||||
|
|
||||||
use crate::next_solver::fold::BoundVarReplacerDelegate;
|
use crate::next_solver::{
|
||||||
use crate::next_solver::infer::select::EvaluationResult;
|
BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, SolverContext,
|
||||||
use crate::next_solver::infer::traits::PredicateObligation;
|
fold::BoundVarReplacerDelegate,
|
||||||
use crate::next_solver::obligation_ctxt::ObligationCtxt;
|
infer::{select::EvaluationResult, traits::PredicateObligation},
|
||||||
use crate::next_solver::{BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, SolverContext};
|
obligation_ctxt::ObligationCtxt,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AliasTerm, Binder, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, DbInterner,
|
AliasTerm, Binder, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, DbInterner,
|
||||||
|
|
@ -46,7 +46,7 @@ use super::{
|
||||||
pub mod at;
|
pub mod at;
|
||||||
pub mod canonical;
|
pub mod canonical;
|
||||||
mod context;
|
mod context;
|
||||||
mod opaque_types;
|
pub mod opaque_types;
|
||||||
pub mod region_constraints;
|
pub mod region_constraints;
|
||||||
pub mod relate;
|
pub mod relate;
|
||||||
pub mod resolve;
|
pub mod resolve;
|
||||||
|
|
@ -400,6 +400,46 @@ impl<'db> InferCtxt<'db> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert_type_vars<T>(&self, ty: T) -> T
|
||||||
|
where
|
||||||
|
T: TypeFoldable<DbInterner<'db>>,
|
||||||
|
{
|
||||||
|
struct Folder<'a, 'db> {
|
||||||
|
infcx: &'a InferCtxt<'db>,
|
||||||
|
}
|
||||||
|
impl<'db> TypeFolder<DbInterner<'db>> for Folder<'_, 'db> {
|
||||||
|
fn cx(&self) -> DbInterner<'db> {
|
||||||
|
self.infcx.interner
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
|
||||||
|
if !ty.references_error() {
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ty.is_ty_error() { self.infcx.next_ty_var() } else { ty.super_fold_with(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
|
||||||
|
if !ct.references_error() {
|
||||||
|
return ct;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ct.is_ct_error() {
|
||||||
|
self.infcx.next_const_var()
|
||||||
|
} else {
|
||||||
|
ct.super_fold_with(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
|
||||||
|
if r.is_error() { self.infcx.next_region_var() } else { r }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty.fold_with(&mut Folder { infcx: self })
|
||||||
|
}
|
||||||
|
|
||||||
/// Evaluates whether the predicate can be satisfied in the given
|
/// Evaluates whether the predicate can be satisfied in the given
|
||||||
/// `ParamEnv`, and returns `false` if not certain. However, this is
|
/// `ParamEnv`, and returns `false` if not certain. However, this is
|
||||||
/// not entirely accurate if inference variables are involved.
|
/// not entirely accurate if inference variables are involved.
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,11 @@ pub(crate) mod table;
|
||||||
|
|
||||||
pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
|
pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
|
||||||
|
|
||||||
|
use macros::{TypeFoldable, TypeVisitable};
|
||||||
|
|
||||||
use crate::next_solver::{OpaqueTypeKey, Ty, infer::InferCtxt};
|
use crate::next_solver::{OpaqueTypeKey, Ty, infer::InferCtxt};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)]
|
||||||
pub struct OpaqueHiddenType<'db> {
|
pub struct OpaqueHiddenType<'db> {
|
||||||
pub ty: Ty<'db>,
|
pub ty: Ty<'db>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,14 +122,6 @@ impl<'db> OpaqueTypeStorage<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Drop for OpaqueTypeStorage<'db> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if !self.opaque_types.is_empty() {
|
|
||||||
panic!("{:?}", self.opaque_types)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct OpaqueTypeTable<'a, 'db> {
|
pub(crate) struct OpaqueTypeTable<'a, 'db> {
|
||||||
storage: &'a mut OpaqueTypeStorage<'db>,
|
storage: &'a mut OpaqueTypeStorage<'db>,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db};
|
||||||
|
|
||||||
use base_db::Crate;
|
use base_db::Crate;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId,
|
AdtId, AttrDefId, BlockId, CallableDefId, DefWithBodyId, EnumVariantId, ItemContainerId,
|
||||||
VariantId,
|
StructId, UnionId, VariantId,
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags},
|
signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags},
|
||||||
};
|
};
|
||||||
|
|
@ -29,7 +29,7 @@ use rustc_type_ir::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
FnAbi,
|
FnAbi,
|
||||||
db::{HirDatabase, InternedCoroutine},
|
db::{HirDatabase, InternedCoroutine, InternedCoroutineId},
|
||||||
method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
|
method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
|
||||||
next_solver::{
|
next_solver::{
|
||||||
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
|
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
|
||||||
|
|
@ -96,7 +96,7 @@ macro_rules! _interned_vec_nolifetime_salsa {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name:ident, $ty:ty, nofold) => {
|
($name:ident, $ty:ty, nofold) => {
|
||||||
#[salsa::interned(constructor = new_, debug)]
|
#[salsa::interned(constructor = new_)]
|
||||||
pub struct $name {
|
pub struct $name {
|
||||||
#[returns(ref)]
|
#[returns(ref)]
|
||||||
inner_: smallvec::SmallVec<[$ty; 2]>,
|
inner_: smallvec::SmallVec<[$ty; 2]>,
|
||||||
|
|
@ -119,6 +119,12 @@ macro_rules! _interned_vec_nolifetime_salsa {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'db> std::fmt::Debug for $name<'db> {
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.as_slice().fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> {
|
impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> {
|
||||||
type Item = $ty;
|
type Item = $ty;
|
||||||
|
|
||||||
|
|
@ -1866,9 +1872,42 @@ impl<'db> Interner for DbInterner<'db> {
|
||||||
Binder::bind_with_vars(inner, bound_vars)
|
Binder::bind_with_vars(inner, bound_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opaque_types_defined_by(self, _defining_anchor: Self::LocalDefId) -> Self::LocalDefIds {
|
fn opaque_types_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds {
|
||||||
// FIXME(next-solver)
|
let Ok(def_id) = DefWithBodyId::try_from(def_id) else {
|
||||||
SolverDefIds::new_from_iter(self, [])
|
return SolverDefIds::default();
|
||||||
|
};
|
||||||
|
let mut result = Vec::new();
|
||||||
|
crate::opaques::opaque_types_defined_by(self.db, def_id, &mut result);
|
||||||
|
SolverDefIds::new_from_iter(self, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opaque_types_and_coroutines_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds {
|
||||||
|
let Ok(def_id) = DefWithBodyId::try_from(def_id) else {
|
||||||
|
return SolverDefIds::default();
|
||||||
|
};
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
crate::opaques::opaque_types_defined_by(self.db, def_id, &mut result);
|
||||||
|
|
||||||
|
// Collect coroutines.
|
||||||
|
let body = self.db.body(def_id);
|
||||||
|
body.exprs().for_each(|(expr_id, expr)| {
|
||||||
|
if matches!(
|
||||||
|
expr,
|
||||||
|
hir_def::hir::Expr::Async { .. }
|
||||||
|
| hir_def::hir::Expr::Closure {
|
||||||
|
closure_kind: hir_def::hir::ClosureKind::Async
|
||||||
|
| hir_def::hir::ClosureKind::Coroutine(_),
|
||||||
|
..
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
let coroutine =
|
||||||
|
InternedCoroutineId::new(self.db, InternedCoroutine(def_id, expr_id));
|
||||||
|
result.push(coroutine.into());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
SolverDefIds::new_from_iter(self, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alias_has_const_conditions(self, _def_id: Self::DefId) -> bool {
|
fn alias_has_const_conditions(self, _def_id: Self::DefId) -> bool {
|
||||||
|
|
@ -1913,12 +1952,10 @@ impl<'db> Interner for DbInterner<'db> {
|
||||||
let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque);
|
let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque);
|
||||||
match impl_trait_id {
|
match impl_trait_id {
|
||||||
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
||||||
let infer = self.db().infer(func.into());
|
crate::opaques::rpit_hidden_types(self.db, func)[idx]
|
||||||
EarlyBinder::bind(infer.type_of_rpit[idx])
|
|
||||||
}
|
}
|
||||||
crate::ImplTraitId::TypeAliasImplTrait(..) => {
|
crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => {
|
||||||
// FIXME(next-solver)
|
crate::opaques::tait_hidden_types(self.db, type_alias)[idx]
|
||||||
EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1969,13 +2006,6 @@ impl<'db> Interner for DbInterner<'db> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opaque_types_and_coroutines_defined_by(
|
|
||||||
self,
|
|
||||||
_defining_anchor: Self::LocalDefId,
|
|
||||||
) -> Self::LocalDefIds {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Probe = rustc_type_ir::solve::inspect::Probe<DbInterner<'db>>;
|
type Probe = rustc_type_ir::solve::inspect::Probe<DbInterner<'db>>;
|
||||||
fn mk_probe(self, probe: rustc_type_ir::solve::inspect::Probe<Self>) -> Self::Probe {
|
fn mk_probe(self, probe: rustc_type_ir::solve::inspect::Probe<Self>) -> Self::Probe {
|
||||||
probe
|
probe
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,22 @@
|
||||||
|
|
||||||
use hir_def::{AssocItemId, GeneralConstId};
|
use hir_def::{AssocItemId, GeneralConstId};
|
||||||
use rustc_next_trait_solver::delegate::SolverDelegate;
|
use rustc_next_trait_solver::delegate::SolverDelegate;
|
||||||
use rustc_type_ir::GenericArgKind;
|
|
||||||
use rustc_type_ir::lang_items::SolverTraitLangItem;
|
|
||||||
use rustc_type_ir::{
|
use rustc_type_ir::{
|
||||||
InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt,
|
AliasTyKind, GenericArgKind, InferCtxtLike, Interner, PredicatePolarity, TypeFlags,
|
||||||
inherent::{IntoKind, Term as _, Ty as _},
|
TypeVisitableExt,
|
||||||
|
inherent::{IntoKind, SliceLike, Term as _, Ty as _},
|
||||||
|
lang_items::SolverTraitLangItem,
|
||||||
solve::{Certainty, NoSolution},
|
solve::{Certainty, NoSolution},
|
||||||
};
|
};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::next_solver::{CanonicalVarKind, ImplIdWrapper};
|
use crate::{
|
||||||
use crate::next_solver::{
|
ImplTraitId,
|
||||||
ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, util::sizedness_fast_path,
|
next_solver::{
|
||||||
|
AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
|
||||||
|
ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
|
||||||
|
util::sizedness_fast_path,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -76,7 +81,7 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
||||||
|
|
||||||
fn well_formed_goals(
|
fn well_formed_goals(
|
||||||
&self,
|
&self,
|
||||||
_param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
|
_param_env: ParamEnv<'db>,
|
||||||
_arg: <Self::Interner as rustc_type_ir::Interner>::Term,
|
_arg: <Self::Interner as rustc_type_ir::Interner>::Term,
|
||||||
) -> Option<
|
) -> Option<
|
||||||
Vec<
|
Vec<
|
||||||
|
|
@ -125,18 +130,60 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
||||||
|
|
||||||
fn add_item_bounds_for_hidden_type(
|
fn add_item_bounds_for_hidden_type(
|
||||||
&self,
|
&self,
|
||||||
_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
|
def_id: SolverDefId,
|
||||||
_args: <Self::Interner as rustc_type_ir::Interner>::GenericArgs,
|
args: GenericArgs<'db>,
|
||||||
_param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
|
param_env: ParamEnv<'db>,
|
||||||
_hidden_ty: <Self::Interner as rustc_type_ir::Interner>::Ty,
|
hidden_ty: Ty<'db>,
|
||||||
_goals: &mut Vec<
|
goals: &mut Vec<Goal<'db, Predicate<'db>>>,
|
||||||
rustc_type_ir::solve::Goal<
|
|
||||||
Self::Interner,
|
|
||||||
<Self::Interner as rustc_type_ir::Interner>::Predicate,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
) {
|
) {
|
||||||
unimplemented!()
|
let interner = self.interner;
|
||||||
|
let opaque_id = def_id.expect_opaque_ty();
|
||||||
|
// Require that the hidden type is well-formed. We have to
|
||||||
|
// make sure we wf-check the hidden type to fix #114728.
|
||||||
|
//
|
||||||
|
// However, we don't check that all types are well-formed.
|
||||||
|
// We only do so for types provided by the user or if they are
|
||||||
|
// "used", e.g. for method selection.
|
||||||
|
//
|
||||||
|
// This means we never check the wf requirements of the hidden
|
||||||
|
// type during MIR borrowck, causing us to infer the wrong
|
||||||
|
// lifetime for its member constraints which then results in
|
||||||
|
// unexpected region errors.
|
||||||
|
goals.push(Goal::new(interner, param_env, ClauseKind::WellFormed(hidden_ty.into())));
|
||||||
|
|
||||||
|
let replace_opaques_in = |clause: Clause<'db>| {
|
||||||
|
fold_tys(interner, clause, |ty| match ty.kind() {
|
||||||
|
// Replace all other mentions of the same opaque type with the hidden type,
|
||||||
|
// as the bounds must hold on the hidden type after all.
|
||||||
|
TyKind::Alias(
|
||||||
|
AliasTyKind::Opaque,
|
||||||
|
AliasTy { def_id: def_id2, args: args2, .. },
|
||||||
|
) if def_id == def_id2 && args == args2 => hidden_ty,
|
||||||
|
_ => ty,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = interner.db;
|
||||||
|
let (opaques_table, opaque_idx) = match opaque_id.loc(db) {
|
||||||
|
ImplTraitId::ReturnTypeImplTrait(func, opaque_idx) => {
|
||||||
|
(db.return_type_impl_traits(func), opaque_idx)
|
||||||
|
}
|
||||||
|
ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx) => {
|
||||||
|
(db.type_alias_impl_traits(type_alias), opaque_idx)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let item_bounds = opaques_table
|
||||||
|
.as_deref()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.map_bound(|table| &table.impl_traits[opaque_idx].predicates);
|
||||||
|
for predicate in item_bounds.iter_instantiated_copied(interner, args.as_slice()) {
|
||||||
|
let predicate = replace_opaques_in(predicate);
|
||||||
|
|
||||||
|
// Require that the predicate holds for the concrete type.
|
||||||
|
debug!(?predicate);
|
||||||
|
goals.push(Goal::new(interner, param_env, predicate));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_eligible_assoc_item(
|
fn fetch_eligible_assoc_item(
|
||||||
|
|
@ -190,8 +237,8 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
||||||
|
|
||||||
fn is_transmutable(
|
fn is_transmutable(
|
||||||
&self,
|
&self,
|
||||||
_dst: <Self::Interner as rustc_type_ir::Interner>::Ty,
|
_dst: Ty<'db>,
|
||||||
_src: <Self::Interner as rustc_type_ir::Interner>::Ty,
|
_src: Ty<'db>,
|
||||||
_assume: <Self::Interner as rustc_type_ir::Interner>::Const,
|
_assume: <Self::Interner as rustc_type_ir::Interner>::Const,
|
||||||
) -> Result<Certainty, NoSolution> {
|
) -> Result<Certainty, NoSolution> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
@ -199,7 +246,7 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
||||||
|
|
||||||
fn evaluate_const(
|
fn evaluate_const(
|
||||||
&self,
|
&self,
|
||||||
_param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
|
_param_env: ParamEnv<'db>,
|
||||||
uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
|
uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
|
||||||
) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
|
) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
|
||||||
let c = match uv.def {
|
let c = match uv.def {
|
||||||
|
|
|
||||||
199
src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs
Normal file
199
src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
//! Handling of opaque types, detection of defining scope and hidden type.
|
||||||
|
|
||||||
|
use hir_def::{
|
||||||
|
AssocItemId, AssocItemLoc, DefWithBodyId, FunctionId, HasModule, ItemContainerId, TypeAliasId,
|
||||||
|
};
|
||||||
|
use hir_expand::name::Name;
|
||||||
|
use la_arena::ArenaMap;
|
||||||
|
use rustc_type_ir::inherent::Ty as _;
|
||||||
|
use syntax::ast;
|
||||||
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ImplTraitId,
|
||||||
|
db::{HirDatabase, InternedOpaqueTyId},
|
||||||
|
lower::{ImplTraitIdx, ImplTraits},
|
||||||
|
next_solver::{
|
||||||
|
DbInterner, EarlyBinder, ErrorGuaranteed, SolverDefId, Ty, TypingMode,
|
||||||
|
infer::{DbInternerInferExt, traits::ObligationCause},
|
||||||
|
obligation_ctxt::ObligationCtxt,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) fn opaque_types_defined_by(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
def_id: DefWithBodyId,
|
||||||
|
result: &mut Vec<SolverDefId>,
|
||||||
|
) {
|
||||||
|
if let DefWithBodyId::FunctionId(func) = def_id {
|
||||||
|
// A function may define its own RPITs.
|
||||||
|
extend_with_opaques(
|
||||||
|
db,
|
||||||
|
db.return_type_impl_traits(func),
|
||||||
|
|opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx),
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let extend_with_taits = |type_alias| {
|
||||||
|
extend_with_opaques(
|
||||||
|
db,
|
||||||
|
db.type_alias_impl_traits(type_alias),
|
||||||
|
|opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx),
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Collect opaques from assoc items.
|
||||||
|
let extend_with_atpit_from_assoc_items = |assoc_items: &[(Name, AssocItemId)]| {
|
||||||
|
assoc_items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&(_, assoc_id)| match assoc_id {
|
||||||
|
AssocItemId::TypeAliasId(it) => Some(it),
|
||||||
|
AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => None,
|
||||||
|
})
|
||||||
|
.for_each(extend_with_taits);
|
||||||
|
};
|
||||||
|
let extend_with_atpit_from_container = |container| match container {
|
||||||
|
ItemContainerId::ImplId(impl_id) => {
|
||||||
|
if db.impl_signature(impl_id).target_trait.is_some() {
|
||||||
|
extend_with_atpit_from_assoc_items(&impl_id.impl_items(db).items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemContainerId::TraitId(trait_id) => {
|
||||||
|
extend_with_atpit_from_assoc_items(&trait_id.trait_items(db).items);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
match def_id {
|
||||||
|
DefWithBodyId::ConstId(id) => extend_with_atpit_from_container(id.loc(db).container),
|
||||||
|
DefWithBodyId::FunctionId(id) => extend_with_atpit_from_container(id.loc(db).container),
|
||||||
|
DefWithBodyId::StaticId(_) | DefWithBodyId::VariantId(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Collect opaques from `#[define_opaque]`.
|
||||||
|
|
||||||
|
fn extend_with_opaques<'db>(
|
||||||
|
db: &'db dyn HirDatabase,
|
||||||
|
opaques: Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>>,
|
||||||
|
mut make_impl_trait: impl FnMut(ImplTraitIdx<'db>) -> ImplTraitId<'db>,
|
||||||
|
result: &mut Vec<SolverDefId>,
|
||||||
|
) {
|
||||||
|
if let Some(opaques) = opaques {
|
||||||
|
for (opaque_idx, _) in (*opaques).as_ref().skip_binder().impl_traits.iter() {
|
||||||
|
let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx));
|
||||||
|
result.push(opaque_id.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are firewall queries to prevent drawing dependencies between infers:
|
||||||
|
|
||||||
|
#[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
|
||||||
|
pub(crate) fn rpit_hidden_types<'db>(
|
||||||
|
db: &'db dyn HirDatabase,
|
||||||
|
function: FunctionId,
|
||||||
|
) -> ArenaMap<ImplTraitIdx<'db>, EarlyBinder<'db, Ty<'db>>> {
|
||||||
|
let infer = db.infer(function.into());
|
||||||
|
let mut result = ArenaMap::new();
|
||||||
|
for (opaque, hidden_type) in infer.return_position_impl_trait_types(db) {
|
||||||
|
result.insert(opaque, EarlyBinder::bind(hidden_type));
|
||||||
|
}
|
||||||
|
result.shrink_to_fit();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
|
||||||
|
pub(crate) fn tait_hidden_types<'db>(
|
||||||
|
db: &'db dyn HirDatabase,
|
||||||
|
type_alias: TypeAliasId,
|
||||||
|
) -> ArenaMap<ImplTraitIdx<'db>, EarlyBinder<'db, Ty<'db>>> {
|
||||||
|
let loc = type_alias.loc(db);
|
||||||
|
let module = loc.module(db);
|
||||||
|
let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
|
||||||
|
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||||
|
let mut ocx = ObligationCtxt::new(&infcx);
|
||||||
|
let cause = ObligationCause::dummy();
|
||||||
|
let param_env = db.trait_environment(type_alias.into()).env;
|
||||||
|
|
||||||
|
let defining_bodies = tait_defining_bodies(db, &loc);
|
||||||
|
|
||||||
|
let taits_count = db
|
||||||
|
.type_alias_impl_traits(type_alias)
|
||||||
|
.map_or(0, |taits| (*taits).as_ref().skip_binder().impl_traits.len());
|
||||||
|
|
||||||
|
let mut result = ArenaMap::with_capacity(taits_count);
|
||||||
|
for defining_body in defining_bodies {
|
||||||
|
let infer = db.infer(defining_body);
|
||||||
|
for (&opaque, &hidden_type) in &infer.type_of_opaque {
|
||||||
|
let ImplTraitId::TypeAliasImplTrait(opaque_owner, opaque_idx) = opaque.loc(db) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if opaque_owner != type_alias {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// In the presence of errors, we attempt to create a unified type from all
|
||||||
|
// types. rustc doesn't do that, but this should improve the experience.
|
||||||
|
let hidden_type = infcx.insert_type_vars(hidden_type);
|
||||||
|
match result.entry(opaque_idx) {
|
||||||
|
la_arena::Entry::Vacant(entry) => {
|
||||||
|
entry.insert(EarlyBinder::bind(hidden_type));
|
||||||
|
}
|
||||||
|
la_arena::Entry::Occupied(entry) => {
|
||||||
|
_ = ocx.eq(&cause, param_env, entry.get().instantiate_identity(), hidden_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ocx.try_evaluate_obligations();
|
||||||
|
|
||||||
|
// Fill missing entries.
|
||||||
|
for idx in 0..taits_count {
|
||||||
|
let idx = la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx as u32));
|
||||||
|
match result.entry(idx) {
|
||||||
|
la_arena::Entry::Vacant(entry) => {
|
||||||
|
entry.insert(EarlyBinder::bind(Ty::new_error(interner, ErrorGuaranteed)));
|
||||||
|
}
|
||||||
|
la_arena::Entry::Occupied(mut entry) => {
|
||||||
|
*entry.get_mut() = entry.get().map_bound(|hidden_type| {
|
||||||
|
infcx.resolve_vars_if_possible(hidden_type).replace_infer_with_error(interner)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tait_defining_bodies(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
loc: &AssocItemLoc<ast::TypeAlias>,
|
||||||
|
) -> Vec<DefWithBodyId> {
|
||||||
|
let from_assoc_items = |assoc_items: &[(Name, AssocItemId)]| {
|
||||||
|
// Associated Type Position Impl Trait.
|
||||||
|
assoc_items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&(_, assoc_id)| match assoc_id {
|
||||||
|
AssocItemId::FunctionId(it) => Some(it.into()),
|
||||||
|
AssocItemId::ConstId(it) => Some(it.into()),
|
||||||
|
AssocItemId::TypeAliasId(_) => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
match loc.container {
|
||||||
|
ItemContainerId::ImplId(impl_id) => {
|
||||||
|
if db.impl_signature(impl_id).target_trait.is_some() {
|
||||||
|
return from_assoc_items(&impl_id.impl_items(db).items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemContainerId::TraitId(trait_id) => {
|
||||||
|
return from_assoc_items(&trait_id.trait_items(db).items);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Support general TAITs, or decisively decide not to.
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
// and indeed I was unable to cause cycles even with erroneous code. However, in r-a we can
|
// and indeed I was unable to cause cycles even with erroneous code. However, in r-a we can
|
||||||
// create a cycle if there is an error in the impl's where clauses. I believe well formed code
|
// create a cycle if there is an error in the impl's where clauses. I believe well formed code
|
||||||
// cannot create a cycle, but a cycle handler is required nevertheless.
|
// cannot create a cycle, but a cycle handler is required nevertheless.
|
||||||
fn specializes_cycle(
|
fn specializes_query_cycle(
|
||||||
_db: &dyn HirDatabase,
|
_db: &dyn HirDatabase,
|
||||||
_specializing_impl_def_id: ImplId,
|
_specializing_impl_def_id: ImplId,
|
||||||
_parent_impl_def_id: ImplId,
|
_parent_impl_def_id: ImplId,
|
||||||
|
|
@ -39,31 +39,14 @@ fn specializes_cycle(
|
||||||
/// `parent_impl_def_id` is a const impl (conditionally based off of some `[const]`
|
/// `parent_impl_def_id` is a const impl (conditionally based off of some `[const]`
|
||||||
/// bounds), then `specializing_impl_def_id` must also be const for the same
|
/// bounds), then `specializing_impl_def_id` must also be const for the same
|
||||||
/// set of types.
|
/// set of types.
|
||||||
#[salsa::tracked(cycle_result = specializes_cycle)]
|
#[salsa::tracked(cycle_result = specializes_query_cycle)]
|
||||||
pub(crate) fn specializes(
|
fn specializes_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
specializing_impl_def_id: ImplId,
|
specializing_impl_def_id: ImplId,
|
||||||
parent_impl_def_id: ImplId,
|
parent_impl_def_id: ImplId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let module = specializing_impl_def_id.loc(db).container;
|
let trait_env = db.trait_environment(specializing_impl_def_id.into());
|
||||||
|
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
|
||||||
// We check that the specializing impl comes from a crate that has specialization enabled.
|
|
||||||
//
|
|
||||||
// We don't really care if the specialized impl (the parent) is in a crate that has
|
|
||||||
// specialization enabled, since it's not being specialized.
|
|
||||||
//
|
|
||||||
// rustc also checks whether the specializing impls comes from a macro marked
|
|
||||||
// `#[allow_internal_unstable(specialization)]`, but `#[allow_internal_unstable]`
|
|
||||||
// is an internal feature, std is not using it for specialization nor is likely to
|
|
||||||
// ever use it, and we don't have the span information necessary to replicate that.
|
|
||||||
let def_map = crate_def_map(db, module.krate());
|
|
||||||
if !def_map.is_unstable_feature_enabled(&sym::specialization)
|
|
||||||
&& !def_map.is_unstable_feature_enabled(&sym::min_specialization)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
|
|
||||||
|
|
||||||
let specializing_impl_signature = db.impl_signature(specializing_impl_def_id);
|
let specializing_impl_signature = db.impl_signature(specializing_impl_def_id);
|
||||||
let parent_impl_signature = db.impl_signature(parent_impl_def_id);
|
let parent_impl_signature = db.impl_signature(parent_impl_def_id);
|
||||||
|
|
@ -87,7 +70,7 @@ pub(crate) fn specializes(
|
||||||
|
|
||||||
// create a parameter environment corresponding to an identity instantiation of the specializing impl,
|
// create a parameter environment corresponding to an identity instantiation of the specializing impl,
|
||||||
// i.e. the most generic instantiation of the specializing impl.
|
// i.e. the most generic instantiation of the specializing impl.
|
||||||
let param_env = db.trait_environment(specializing_impl_def_id.into()).env;
|
let param_env = trait_env.env;
|
||||||
|
|
||||||
// Create an infcx, taking the predicates of the specializing impl as assumptions:
|
// Create an infcx, taking the predicates of the specializing impl as assumptions:
|
||||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||||
|
|
@ -148,3 +131,31 @@ pub(crate) fn specializes(
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is used to avoid creating the query for crates that does not define `#![feature(specialization)]`,
|
||||||
|
// as the solver is calling this a lot, and creating the query consumes a lot of memory.
|
||||||
|
pub(crate) fn specializes(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
specializing_impl_def_id: ImplId,
|
||||||
|
parent_impl_def_id: ImplId,
|
||||||
|
) -> bool {
|
||||||
|
let module = specializing_impl_def_id.loc(db).container;
|
||||||
|
|
||||||
|
// We check that the specializing impl comes from a crate that has specialization enabled.
|
||||||
|
//
|
||||||
|
// We don't really care if the specialized impl (the parent) is in a crate that has
|
||||||
|
// specialization enabled, since it's not being specialized.
|
||||||
|
//
|
||||||
|
// rustc also checks whether the specializing impls comes from a macro marked
|
||||||
|
// `#[allow_internal_unstable(specialization)]`, but `#[allow_internal_unstable]`
|
||||||
|
// is an internal feature, std is not using it for specialization nor is likely to
|
||||||
|
// ever use it, and we don't have the span information necessary to replicate that.
|
||||||
|
let def_map = crate_def_map(db, module.krate());
|
||||||
|
if !def_map.is_unstable_feature_enabled(&sym::specialization)
|
||||||
|
&& !def_map.is_unstable_feature_enabled(&sym::min_specialization)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
specializes_query(db, specializing_impl_def_id, parent_impl_def_id)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -591,6 +591,7 @@ fn main() {
|
||||||
"function_signature_shim",
|
"function_signature_shim",
|
||||||
"function_signature_with_source_map_shim",
|
"function_signature_with_source_map_shim",
|
||||||
"trait_environment_shim",
|
"trait_environment_shim",
|
||||||
|
"return_type_impl_traits_shim",
|
||||||
"expr_scopes_shim",
|
"expr_scopes_shim",
|
||||||
"struct_signature_shim",
|
"struct_signature_shim",
|
||||||
"struct_signature_with_source_map_shim",
|
"struct_signature_with_source_map_shim",
|
||||||
|
|
@ -686,6 +687,7 @@ fn main() {
|
||||||
"return_type_impl_traits_shim",
|
"return_type_impl_traits_shim",
|
||||||
"infer_shim",
|
"infer_shim",
|
||||||
"function_signature_with_source_map_shim",
|
"function_signature_with_source_map_shim",
|
||||||
|
"return_type_impl_traits_shim",
|
||||||
"expr_scopes_shim",
|
"expr_scopes_shim",
|
||||||
"struct_signature_with_source_map_shim",
|
"struct_signature_with_source_map_shim",
|
||||||
"generic_predicates_shim",
|
"generic_predicates_shim",
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ fn test() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "FIXME(next-solver): This currently generates a type mismatch, need to switch opaque type handling to the solver"]
|
|
||||||
fn associated_type_impl_traits_complex() {
|
fn associated_type_impl_traits_complex() {
|
||||||
check_types(
|
check_types(
|
||||||
r#"
|
r#"
|
||||||
|
|
@ -116,6 +115,7 @@ fn foo() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[ignore = "FIXME(next-solver): TAIT support was removed, need to rework it to work with `#[define_opaque]`"]
|
||||||
#[test]
|
#[test]
|
||||||
fn type_alias_impl_trait_simple() {
|
fn type_alias_impl_trait_simple() {
|
||||||
check_no_mismatches(
|
check_no_mismatches(
|
||||||
|
|
@ -135,9 +135,6 @@ static ALIAS: AliasTy = {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
// FIXME(next-solver): This should emit type mismatch error but leaving it for now
|
|
||||||
// as we should fully migrate into next-solver without chalk-ir and TAIT should be
|
|
||||||
// reworked on r-a to handle `#[define_opaque(T)]`
|
|
||||||
check_infer_with_mismatches(
|
check_infer_with_mismatches(
|
||||||
r#"
|
r#"
|
||||||
trait Trait {}
|
trait Trait {}
|
||||||
|
|
|
||||||
|
|
@ -725,7 +725,7 @@ fn issue_4885() {
|
||||||
138..146 'bar(key)': impl Future<Output = <K as Foo<R>>::Bar>
|
138..146 'bar(key)': impl Future<Output = <K as Foo<R>>::Bar>
|
||||||
142..145 'key': &'? K
|
142..145 'key': &'? K
|
||||||
162..165 'key': &'? K
|
162..165 'key': &'? K
|
||||||
224..227 '{ }': ()
|
224..227 '{ }': impl Future<Output = <K as Foo<R>>::Bar>
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -2506,3 +2506,19 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_inside_block() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
mod my_mod {
|
||||||
|
pub type Bool = bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _: my_mod::Bool;
|
||||||
|
// ^ bool
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ impl<'a> IntoIterator for &'a Grid {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
150..154 'self': &'a Grid
|
150..154 'self': &'a Grid
|
||||||
174..181 '{ }': impl Iterator<Item = &'a ()>
|
174..181 '{ }': <&'a Grid as IntoIterator>::IntoIter
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1211,7 +1211,7 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
29..33 'self': &'? Self
|
29..33 'self': &'? Self
|
||||||
54..58 'self': &'? Self
|
54..58 'self': &'? Self
|
||||||
98..100 '{}': ()
|
98..100 '{}': impl Trait<u64>
|
||||||
110..111 'x': impl Trait<u64>
|
110..111 'x': impl Trait<u64>
|
||||||
130..131 'y': &'? impl Trait<u64>
|
130..131 'y': &'? impl Trait<u64>
|
||||||
151..268 '{ ...2(); }': ()
|
151..268 '{ ...2(); }': ()
|
||||||
|
|
@ -1373,11 +1373,11 @@ fn test() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
49..53 'self': &'? mut Self
|
49..53 'self': &'? mut Self
|
||||||
101..105 'self': &'? Self
|
101..105 'self': &'? Self
|
||||||
184..195 '{ loop {} }': ({unknown}, {unknown})
|
184..195 '{ loop {} }': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
|
||||||
186..193 'loop {}': !
|
186..193 'loop {}': !
|
||||||
191..193 '{}': ()
|
191..193 '{}': ()
|
||||||
206..207 't': T
|
206..207 't': T
|
||||||
268..279 '{ loop {} }': ({unknown}, {unknown})
|
268..279 '{ loop {} }': (impl Iterator<Item = impl Trait<T>>, impl Trait<T>)
|
||||||
270..277 'loop {}': !
|
270..277 'loop {}': !
|
||||||
275..277 '{}': ()
|
275..277 '{}': ()
|
||||||
291..413 '{ ...o(); }': ()
|
291..413 '{ ...o(); }': ()
|
||||||
|
|
@ -1419,7 +1419,7 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
134..165 '{ ...(C)) }': (impl FnOnce(&'? str, T), Bar<u8>)
|
134..165 '{ ...(C)) }': (impl FnOnce(&'? str, T), impl Trait<u8>)
|
||||||
140..163 '(|inpu...ar(C))': (impl FnOnce(&'? str, T), Bar<u8>)
|
140..163 '(|inpu...ar(C))': (impl FnOnce(&'? str, T), Bar<u8>)
|
||||||
141..154 '|input, t| {}': impl FnOnce(&'? str, T)
|
141..154 '|input, t| {}': impl FnOnce(&'? str, T)
|
||||||
142..147 'input': &'? str
|
142..147 'input': &'? str
|
||||||
|
|
@ -1441,7 +1441,7 @@ fn return_pos_impl_trait_in_projection() {
|
||||||
trait Future { type Output; }
|
trait Future { type Output; }
|
||||||
impl Future for () { type Output = i32; }
|
impl Future for () { type Output = i32; }
|
||||||
type Foo<F> = (<F as Future>::Output, F);
|
type Foo<F> = (<F as Future>::Output, F);
|
||||||
fn foo<X>() -> Foo<impl Future<Output = ()>> {
|
fn foo<X>() -> Foo<impl Future<Output = i32>> {
|
||||||
(0, ())
|
(0, ())
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
|
|
||||||
|
|
@ -107,24 +107,26 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>(
|
||||||
infer_ctxt: &InferCtxt<'db>,
|
infer_ctxt: &InferCtxt<'db>,
|
||||||
goal: Canonical<'db, Goal<'db, Predicate<'db>>>,
|
goal: Canonical<'db, Goal<'db, Predicate<'db>>>,
|
||||||
) -> NextTraitSolveResult {
|
) -> NextTraitSolveResult {
|
||||||
let context = SolverContext(infer_ctxt.clone());
|
infer_ctxt.probe(|_| {
|
||||||
|
let context = <&SolverContext<'db>>::from(infer_ctxt);
|
||||||
|
|
||||||
tracing::info!(?goal);
|
tracing::info!(?goal);
|
||||||
|
|
||||||
let (goal, var_values) = context.instantiate_canonical(&goal);
|
let (goal, var_values) = context.instantiate_canonical(&goal);
|
||||||
tracing::info!(?var_values);
|
tracing::info!(?var_values);
|
||||||
|
|
||||||
let res = context.evaluate_root_goal(goal, Span::dummy(), None);
|
let res = context.evaluate_root_goal(goal, Span::dummy(), None);
|
||||||
|
|
||||||
let res = res.map(|r| (r.has_changed, r.certainty));
|
let res = res.map(|r| (r.has_changed, r.certainty));
|
||||||
|
|
||||||
tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
|
tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(_) => NextTraitSolveResult::NoSolution,
|
Err(_) => NextTraitSolveResult::NoSolution,
|
||||||
Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain,
|
Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain,
|
||||||
Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain,
|
Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solve a trait goal using next trait solver.
|
/// Solve a trait goal using next trait solver.
|
||||||
|
|
|
||||||
|
|
@ -5136,10 +5136,7 @@ impl<'db> Type<'db> {
|
||||||
AliasTy::new(interner, alias.id.into(), args),
|
AliasTy::new(interner, alias.id.into(), args),
|
||||||
);
|
);
|
||||||
|
|
||||||
// FIXME(next-solver): This needs to be `PostAnalysis`, but this currently causes errors due to our incorrect
|
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||||
// handling of opaques. `non_body_analysis()` will also cause errors (from not revealing opaques inside their
|
|
||||||
// defining places), so we choose between two bad options.
|
|
||||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
|
||||||
let ty = structurally_normalize_ty(&infcx, projection, self.env.clone());
|
let ty = structurally_normalize_ty(&infcx, projection, self.env.clone());
|
||||||
if ty.is_ty_error() { None } else { Some(self.derived(ty)) }
|
if ty.is_ty_error() { None } else { Some(self.derived(ty)) }
|
||||||
}
|
}
|
||||||
|
|
@ -5758,8 +5755,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue {
|
pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue {
|
||||||
let interner = DbInterner::new_with(db, Some(self.env.krate), self.env.block);
|
let interner = DbInterner::new_with(db, Some(self.env.krate), self.env.block);
|
||||||
// FIXME: This should be `PostAnalysis` I believe.
|
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
|
||||||
hir_ty::drop::has_drop_glue(&infcx, self.ty, self.env.clone())
|
hir_ty::drop::has_drop_glue(&infcx, self.ty, self.env.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2105,6 +2105,22 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
parent = parent_;
|
parent = parent_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn impl_generated_from_derive(&self, impl_: Impl) -> Option<Adt> {
|
||||||
|
let source = hir_def::src::HasSource::ast_ptr(&impl_.id.loc(self.db), self.db);
|
||||||
|
let mut file_id = source.file_id;
|
||||||
|
let adt_ast_id = loop {
|
||||||
|
let macro_call = file_id.macro_file()?;
|
||||||
|
match macro_call.loc(self.db).kind {
|
||||||
|
hir_expand::MacroCallKind::Derive { ast_id, .. } => break ast_id,
|
||||||
|
hir_expand::MacroCallKind::FnLike { ast_id, .. } => file_id = ast_id.file_id,
|
||||||
|
hir_expand::MacroCallKind::Attr { ast_id, .. } => file_id = ast_id.file_id,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let adt_source = adt_ast_id.to_in_file_node(self.db);
|
||||||
|
self.cache(adt_source.value.syntax().ancestors().last().unwrap(), adt_source.file_id);
|
||||||
|
ToDef::to_def(self, adt_source.as_ref())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME This can't be the best way to do this
|
// FIXME This can't be the best way to do this
|
||||||
|
|
|
||||||
|
|
@ -1594,14 +1594,12 @@ fn resolve_hir_path_(
|
||||||
Some(unresolved) => resolver
|
Some(unresolved) => resolver
|
||||||
.generic_def()
|
.generic_def()
|
||||||
.and_then(|def| {
|
.and_then(|def| {
|
||||||
hir_ty::attach_db(db, || {
|
hir_ty::associated_type_shorthand_candidates(
|
||||||
hir_ty::associated_type_shorthand_candidates(
|
db,
|
||||||
db,
|
def,
|
||||||
def,
|
res.in_type_ns()?,
|
||||||
res.in_type_ns()?,
|
|name, _| name == unresolved.name,
|
||||||
|name, _| name == unresolved.name,
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.map(TypeAlias::from)
|
.map(TypeAlias::from)
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,259 @@
|
||||||
|
use ide_db::assists::AssistId;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use syntax::{
|
||||||
|
AstNode, T,
|
||||||
|
algo::previous_non_trivia_token,
|
||||||
|
ast::{
|
||||||
|
self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit, make,
|
||||||
|
syntax_factory::SyntaxFactory,
|
||||||
|
},
|
||||||
|
syntax_editor::{Element, Position},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::assist_context::{AssistContext, Assists};
|
||||||
|
|
||||||
|
// Assist: convert_range_for_to_while
|
||||||
|
//
|
||||||
|
// Convert for each range into while loop.
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// fn foo() {
|
||||||
|
// $0for i in 3..7 {
|
||||||
|
// foo(i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// ->
|
||||||
|
// ```
|
||||||
|
// fn foo() {
|
||||||
|
// let mut i = 3;
|
||||||
|
// while i < 7 {
|
||||||
|
// foo(i);
|
||||||
|
// i += 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
|
let for_kw = ctx.find_token_syntax_at_offset(T![for])?;
|
||||||
|
let for_ = ast::ForExpr::cast(for_kw.parent()?)?;
|
||||||
|
let ast::Pat::IdentPat(pat) = for_.pat()? else { return None };
|
||||||
|
let iterable = for_.iterable()?;
|
||||||
|
let (start, end, step, inclusive) = extract_range(&iterable)?;
|
||||||
|
let name = pat.name()?;
|
||||||
|
let body = for_.loop_body()?;
|
||||||
|
let last = previous_non_trivia_token(body.stmt_list()?.r_curly_token()?)?;
|
||||||
|
|
||||||
|
let description = if end.is_some() {
|
||||||
|
"Replace with while expression"
|
||||||
|
} else {
|
||||||
|
"Replace with loop expression"
|
||||||
|
};
|
||||||
|
acc.add(
|
||||||
|
AssistId::refactor("convert_range_for_to_while"),
|
||||||
|
description,
|
||||||
|
for_.syntax().text_range(),
|
||||||
|
|builder| {
|
||||||
|
let mut edit = builder.make_editor(for_.syntax());
|
||||||
|
let make = SyntaxFactory::with_mappings();
|
||||||
|
|
||||||
|
let indent = for_.indent_level();
|
||||||
|
let pat = make.ident_pat(pat.ref_token().is_some(), true, name.clone());
|
||||||
|
let let_stmt = make.let_stmt(pat.into(), None, Some(start));
|
||||||
|
edit.insert_all(
|
||||||
|
Position::before(for_.syntax()),
|
||||||
|
vec![
|
||||||
|
let_stmt.syntax().syntax_element(),
|
||||||
|
make.whitespace(&format!("\n{}", indent)).syntax_element(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut elements = vec![];
|
||||||
|
|
||||||
|
let var_expr = make.expr_path(make.ident_path(&name.text()));
|
||||||
|
let op = ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
|
||||||
|
ordering: ast::Ordering::Less,
|
||||||
|
strict: !inclusive,
|
||||||
|
});
|
||||||
|
if let Some(end) = end {
|
||||||
|
elements.extend([
|
||||||
|
make.token(T![while]).syntax_element(),
|
||||||
|
make.whitespace(" ").syntax_element(),
|
||||||
|
make.expr_bin(var_expr.clone(), op, end).syntax().syntax_element(),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
elements.push(make.token(T![loop]).syntax_element());
|
||||||
|
}
|
||||||
|
|
||||||
|
edit.replace_all(
|
||||||
|
for_kw.syntax_element()..=iterable.syntax().syntax_element(),
|
||||||
|
elements,
|
||||||
|
);
|
||||||
|
|
||||||
|
let op = ast::BinaryOp::Assignment { op: Some(ast::ArithOp::Add) };
|
||||||
|
edit.insert_all(
|
||||||
|
Position::after(last),
|
||||||
|
vec![
|
||||||
|
make.whitespace(&format!("\n{}", indent + 1)).syntax_element(),
|
||||||
|
make.expr_bin(var_expr, op, step).syntax().syntax_element(),
|
||||||
|
make.token(T![;]).syntax_element(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
edit.add_mappings(make.finish_with_mappings());
|
||||||
|
builder.add_file_edits(ctx.vfs_file_id(), edit);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option<ast::Expr>, ast::Expr, bool)> {
|
||||||
|
Some(match iterable {
|
||||||
|
ast::Expr::ParenExpr(expr) => extract_range(&expr.expr()?)?,
|
||||||
|
ast::Expr::RangeExpr(range) => {
|
||||||
|
let inclusive = range.op_kind()? == ast::RangeOp::Inclusive;
|
||||||
|
(range.start()?, range.end(), make::expr_literal("1").into(), inclusive)
|
||||||
|
}
|
||||||
|
ast::Expr::MethodCallExpr(call) if call.name_ref()?.text() == "step_by" => {
|
||||||
|
let [step] = call.arg_list()?.args().collect_array()?;
|
||||||
|
let (start, end, _, inclusive) = extract_range(&call.receiver()?)?;
|
||||||
|
(start, end, step, inclusive)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_range_for_to_while() {
|
||||||
|
check_assist(
|
||||||
|
convert_range_for_to_while,
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
$0for i in 3..7 {
|
||||||
|
foo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
let mut i = 3;
|
||||||
|
while i < 7 {
|
||||||
|
foo(i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_range_for_to_while_no_end_bound() {
|
||||||
|
check_assist(
|
||||||
|
convert_range_for_to_while,
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
$0for i in 3.. {
|
||||||
|
foo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
let mut i = 3;
|
||||||
|
loop {
|
||||||
|
foo(i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_range_for_to_while_with_mut_binding() {
|
||||||
|
check_assist(
|
||||||
|
convert_range_for_to_while,
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
$0for mut i in 3..7 {
|
||||||
|
foo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
let mut i = 3;
|
||||||
|
while i < 7 {
|
||||||
|
foo(i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_range_for_to_while_with_label() {
|
||||||
|
check_assist(
|
||||||
|
convert_range_for_to_while,
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
'a: $0for mut i in 3..7 {
|
||||||
|
foo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
let mut i = 3;
|
||||||
|
'a: while i < 7 {
|
||||||
|
foo(i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_range_for_to_while_step_by() {
|
||||||
|
check_assist(
|
||||||
|
convert_range_for_to_while,
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
$0for mut i in (3..7).step_by(2) {
|
||||||
|
foo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
let mut i = 3;
|
||||||
|
while i < 7 {
|
||||||
|
foo(i);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_range_for_to_while_not_applicable_non_range() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
convert_range_for_to_while,
|
||||||
|
"
|
||||||
|
fn foo() {
|
||||||
|
let ident = 3..7;
|
||||||
|
$0for mut i in ident {
|
||||||
|
foo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ use syntax::{
|
||||||
AstNode,
|
AstNode,
|
||||||
ast::{
|
ast::{
|
||||||
self, AssocItem, BlockExpr, GenericParam, HasAttrs, HasGenericParams, HasName,
|
self, AssocItem, BlockExpr, GenericParam, HasAttrs, HasGenericParams, HasName,
|
||||||
HasTypeBounds, HasVisibility, edit_in_place::Indent, make,
|
HasTypeBounds, HasVisibility, edit::AstNodeEdit, make,
|
||||||
},
|
},
|
||||||
syntax_editor::Position,
|
syntax_editor::Position,
|
||||||
};
|
};
|
||||||
|
|
@ -75,7 +75,7 @@ pub(crate) fn generate_blanket_trait_impl(
|
||||||
|builder| {
|
|builder| {
|
||||||
let mut edit = builder.make_editor(traitd.syntax());
|
let mut edit = builder.make_editor(traitd.syntax());
|
||||||
let namety = make::ty_path(make::path_from_text(&name.text()));
|
let namety = make::ty_path(make::path_from_text(&name.text()));
|
||||||
let trait_where_clause = traitd.where_clause().map(|it| it.clone_for_update());
|
let trait_where_clause = traitd.where_clause().map(|it| it.reset_indent());
|
||||||
let bounds = traitd.type_bound_list().and_then(exlucde_sized);
|
let bounds = traitd.type_bound_list().and_then(exlucde_sized);
|
||||||
let is_unsafe = traitd.unsafe_token().is_some();
|
let is_unsafe = traitd.unsafe_token().is_some();
|
||||||
let thisname = this_name(&traitd);
|
let thisname = this_name(&traitd);
|
||||||
|
|
@ -90,10 +90,6 @@ pub(crate) fn generate_blanket_trait_impl(
|
||||||
let trait_gen_args =
|
let trait_gen_args =
|
||||||
traitd.generic_param_list().map(|param_list| param_list.to_generic_args());
|
traitd.generic_param_list().map(|param_list| param_list.to_generic_args());
|
||||||
|
|
||||||
if let Some(ref where_clause) = trait_where_clause {
|
|
||||||
where_clause.reindent_to(0.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let impl_ = make::impl_trait(
|
let impl_ = make::impl_trait(
|
||||||
cfg_attrs(&traitd),
|
cfg_attrs(&traitd),
|
||||||
is_unsafe,
|
is_unsafe,
|
||||||
|
|
@ -112,20 +108,19 @@ pub(crate) fn generate_blanket_trait_impl(
|
||||||
|
|
||||||
if let Some(trait_assoc_list) = traitd.assoc_item_list() {
|
if let Some(trait_assoc_list) = traitd.assoc_item_list() {
|
||||||
let assoc_item_list = impl_.get_or_create_assoc_item_list();
|
let assoc_item_list = impl_.get_or_create_assoc_item_list();
|
||||||
for method in trait_assoc_list.assoc_items() {
|
for item in trait_assoc_list.assoc_items() {
|
||||||
let AssocItem::Fn(method) = method else {
|
let item = match item {
|
||||||
continue;
|
ast::AssocItem::Fn(method) if method.body().is_none() => {
|
||||||
|
todo_fn(&method, ctx.config).into()
|
||||||
|
}
|
||||||
|
ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item,
|
||||||
|
_ => continue,
|
||||||
};
|
};
|
||||||
if method.body().is_some() {
|
assoc_item_list.add_item(item.reset_indent().indent(1.into()));
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let f = todo_fn(&method, ctx.config).clone_for_update();
|
|
||||||
f.indent(1.into());
|
|
||||||
assoc_item_list.add_item(AssocItem::Fn(f));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_.indent(indent);
|
let impl_ = impl_.indent(indent);
|
||||||
|
|
||||||
edit.insert_all(
|
edit.insert_all(
|
||||||
Position::after(traitd.syntax()),
|
Position::after(traitd.syntax()),
|
||||||
|
|
@ -506,6 +501,41 @@ impl<I: Iterator + ?Sized> Foo for $0I {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gen_blanket_other_assoc_items() {
|
||||||
|
check_assist(
|
||||||
|
generate_blanket_trait_impl,
|
||||||
|
r#"
|
||||||
|
trait $0Foo {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
const N: usize;
|
||||||
|
|
||||||
|
fn foo(&self);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
trait Foo {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
const N: usize;
|
||||||
|
|
||||||
|
fn foo(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Foo for $0T {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
const N: usize;
|
||||||
|
|
||||||
|
fn foo(&self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gen_blanket_indent() {
|
fn test_gen_blanket_indent() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
@ -739,6 +769,49 @@ mod foo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_blanket_trait_impl,
|
||||||
|
r#"
|
||||||
|
mod foo {
|
||||||
|
mod bar {
|
||||||
|
trait $0Foo {
|
||||||
|
type Item: Bar<
|
||||||
|
Self,
|
||||||
|
>;
|
||||||
|
|
||||||
|
const N: Baz<
|
||||||
|
Self,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
mod foo {
|
||||||
|
mod bar {
|
||||||
|
trait Foo {
|
||||||
|
type Item: Bar<
|
||||||
|
Self,
|
||||||
|
>;
|
||||||
|
|
||||||
|
const N: Baz<
|
||||||
|
Self,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Foo for $0T {
|
||||||
|
type Item: Bar<
|
||||||
|
Self,
|
||||||
|
>;
|
||||||
|
|
||||||
|
const N: Baz<
|
||||||
|
Self,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
@ -824,6 +897,8 @@ impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1
|
||||||
where
|
where
|
||||||
Self::Owned: Default,
|
Self::Owned: Default,
|
||||||
{
|
{
|
||||||
|
type X: Sync;
|
||||||
|
|
||||||
fn foo(&self, x: Self::X) -> T {
|
fn foo(&self, x: Self::X) -> T {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -871,6 +946,8 @@ where
|
||||||
Self: ToOwned,
|
Self: ToOwned,
|
||||||
Self::Owned: Default,
|
Self::Owned: Default,
|
||||||
{
|
{
|
||||||
|
type X: Sync;
|
||||||
|
|
||||||
fn foo(&self, x: Self::X) -> T {
|
fn foo(&self, x: Self::X) -> T {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -906,6 +983,8 @@ trait Foo<T: Send> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send, T1: ?Sized> Foo<T> for $0T1 {
|
impl<T: Send, T1: ?Sized> Foo<T> for $0T1 {
|
||||||
|
type X: Sync;
|
||||||
|
|
||||||
fn foo(&self, x: Self::X) -> T {
|
fn foo(&self, x: Self::X) -> T {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -941,6 +1020,8 @@ trait Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> Foo for $0T {
|
impl<T: ?Sized> Foo for $0T {
|
||||||
|
type X: Sync;
|
||||||
|
|
||||||
fn foo(&self, x: Self::X) -> i32 {
|
fn foo(&self, x: Self::X) -> i32 {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use either::Either;
|
||||||
use ide_db::syntax_helpers::suggest_name;
|
use ide_db::syntax_helpers::suggest_name;
|
||||||
use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory};
|
use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory};
|
||||||
|
|
||||||
|
|
@ -24,9 +25,9 @@ pub(crate) fn replace_is_method_with_if_let_method(
|
||||||
acc: &mut Assists,
|
acc: &mut Assists,
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let if_expr = ctx.find_node_at_offset::<ast::IfExpr>()?;
|
let has_cond = ctx.find_node_at_offset::<Either<ast::IfExpr, ast::WhileExpr>>()?;
|
||||||
|
|
||||||
let cond = if_expr.condition()?;
|
let cond = either::for_both!(&has_cond, it => it.condition())?;
|
||||||
let cond = cover_let_chain(cond, ctx.selection_trimmed())?;
|
let cond = cover_let_chain(cond, ctx.selection_trimmed())?;
|
||||||
let call_expr = match cond {
|
let call_expr = match cond {
|
||||||
ast::Expr::MethodCallExpr(call) => call,
|
ast::Expr::MethodCallExpr(call) => call,
|
||||||
|
|
@ -39,7 +40,7 @@ pub(crate) fn replace_is_method_with_if_let_method(
|
||||||
let receiver = call_expr.receiver()?;
|
let receiver = call_expr.receiver()?;
|
||||||
|
|
||||||
let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals(
|
let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals(
|
||||||
ctx.sema.scope(if_expr.syntax()),
|
ctx.sema.scope(has_cond.syntax()),
|
||||||
);
|
);
|
||||||
let var_name = if let ast::Expr::PathExpr(path_expr) = receiver.clone() {
|
let var_name = if let ast::Expr::PathExpr(path_expr) = receiver.clone() {
|
||||||
name_generator.suggest_name(&path_expr.path()?.to_string())
|
name_generator.suggest_name(&path_expr.path()?.to_string())
|
||||||
|
|
@ -48,9 +49,9 @@ pub(crate) fn replace_is_method_with_if_let_method(
|
||||||
};
|
};
|
||||||
|
|
||||||
let (assist_id, message, text) = if name_ref.text() == "is_some" {
|
let (assist_id, message, text) = if name_ref.text() == "is_some" {
|
||||||
("replace_is_some_with_if_let_some", "Replace `is_some` with `if let Some`", "Some")
|
("replace_is_some_with_if_let_some", "Replace `is_some` with `let Some`", "Some")
|
||||||
} else {
|
} else {
|
||||||
("replace_is_ok_with_if_let_ok", "Replace `is_ok` with `if let Ok`", "Ok")
|
("replace_is_ok_with_if_let_ok", "Replace `is_ok` with `let Ok`", "Ok")
|
||||||
};
|
};
|
||||||
|
|
||||||
acc.add(
|
acc.add(
|
||||||
|
|
@ -250,6 +251,25 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn replace_is_some_with_while_let_some() {
|
||||||
|
check_assist(
|
||||||
|
replace_is_method_with_if_let_method,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let mut x = Some(1);
|
||||||
|
while x.is_som$0e() { x = None }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let mut x = Some(1);
|
||||||
|
while let Some(${0:x1}) = x { x = None }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn replace_is_some_with_if_let_some_not_applicable_after_l_curly() {
|
fn replace_is_some_with_if_let_some_not_applicable_after_l_curly() {
|
||||||
check_assist_not_applicable(
|
check_assist_not_applicable(
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ mod handlers {
|
||||||
mod convert_match_to_let_else;
|
mod convert_match_to_let_else;
|
||||||
mod convert_named_struct_to_tuple_struct;
|
mod convert_named_struct_to_tuple_struct;
|
||||||
mod convert_nested_function_to_closure;
|
mod convert_nested_function_to_closure;
|
||||||
|
mod convert_range_for_to_while;
|
||||||
mod convert_to_guarded_return;
|
mod convert_to_guarded_return;
|
||||||
mod convert_tuple_return_type_to_struct;
|
mod convert_tuple_return_type_to_struct;
|
||||||
mod convert_tuple_struct_to_named_struct;
|
mod convert_tuple_struct_to_named_struct;
|
||||||
|
|
@ -268,6 +269,7 @@ mod handlers {
|
||||||
convert_match_to_let_else::convert_match_to_let_else,
|
convert_match_to_let_else::convert_match_to_let_else,
|
||||||
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
|
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
|
||||||
convert_nested_function_to_closure::convert_nested_function_to_closure,
|
convert_nested_function_to_closure::convert_nested_function_to_closure,
|
||||||
|
convert_range_for_to_while::convert_range_for_to_while,
|
||||||
convert_to_guarded_return::convert_to_guarded_return,
|
convert_to_guarded_return::convert_to_guarded_return,
|
||||||
convert_tuple_return_type_to_struct::convert_tuple_return_type_to_struct,
|
convert_tuple_return_type_to_struct::convert_tuple_return_type_to_struct,
|
||||||
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
|
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
|
||||||
|
|
|
||||||
|
|
@ -731,6 +731,29 @@ fn main() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doctest_convert_range_for_to_while() {
|
||||||
|
check_doc_test(
|
||||||
|
"convert_range_for_to_while",
|
||||||
|
r#####"
|
||||||
|
fn foo() {
|
||||||
|
$0for i in 3..7 {
|
||||||
|
foo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#####,
|
||||||
|
r#####"
|
||||||
|
fn foo() {
|
||||||
|
let mut i = 3;
|
||||||
|
while i < 7 {
|
||||||
|
foo(i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#####,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn doctest_convert_to_guarded_return() {
|
fn doctest_convert_to_guarded_return() {
|
||||||
check_doc_test(
|
check_doc_test(
|
||||||
|
|
|
||||||
|
|
@ -531,6 +531,146 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_completion_in_format() {
|
||||||
|
check_edit(
|
||||||
|
"if",
|
||||||
|
r#"
|
||||||
|
//- minicore: fmt
|
||||||
|
fn main() {
|
||||||
|
format_args!("{}", $0);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
format_args!("{}", if $1 {
|
||||||
|
$2
|
||||||
|
} else {
|
||||||
|
$0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_edit(
|
||||||
|
"if",
|
||||||
|
r#"
|
||||||
|
//- minicore: fmt
|
||||||
|
fn main() {
|
||||||
|
format_args!("{}", if$0);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
format_args!("{}", if $1 {
|
||||||
|
$2
|
||||||
|
} else {
|
||||||
|
$0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_completion_in_value_expected_expressions() {
|
||||||
|
check_edit(
|
||||||
|
"if",
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
2 + $0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
2 + if $1 {
|
||||||
|
$2
|
||||||
|
} else {
|
||||||
|
$0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_edit(
|
||||||
|
"if",
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
-$0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
-if $1 {
|
||||||
|
$2
|
||||||
|
} else {
|
||||||
|
$0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_edit(
|
||||||
|
"if",
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
return $0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
return if $1 {
|
||||||
|
$2
|
||||||
|
} else {
|
||||||
|
$0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_edit(
|
||||||
|
"if",
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
break $0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
break if $1 {
|
||||||
|
$2
|
||||||
|
} else {
|
||||||
|
$0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_edit(
|
||||||
|
"if",
|
||||||
|
r#"
|
||||||
|
struct Foo { x: i32 }
|
||||||
|
fn main() {
|
||||||
|
Foo { x: $0 }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo { x: i32 }
|
||||||
|
fn main() {
|
||||||
|
Foo { x: if $1 {
|
||||||
|
$2
|
||||||
|
} else {
|
||||||
|
$0
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_let_in_block() {
|
fn completes_let_in_block() {
|
||||||
check_edit(
|
check_edit(
|
||||||
|
|
|
||||||
|
|
@ -1012,6 +1012,25 @@ fn classify_name_ref<'db>(
|
||||||
.and_then(|next| next.first_token())
|
.and_then(|next| next.first_token())
|
||||||
.is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW)
|
.is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW)
|
||||||
};
|
};
|
||||||
|
let is_in_value = |it: &SyntaxNode| {
|
||||||
|
let Some(node) = it.parent() else { return false };
|
||||||
|
let kind = node.kind();
|
||||||
|
ast::LetStmt::can_cast(kind)
|
||||||
|
|| ast::ArgList::can_cast(kind)
|
||||||
|
|| ast::ArrayExpr::can_cast(kind)
|
||||||
|
|| ast::ParenExpr::can_cast(kind)
|
||||||
|
|| ast::BreakExpr::can_cast(kind)
|
||||||
|
|| ast::ReturnExpr::can_cast(kind)
|
||||||
|
|| ast::PrefixExpr::can_cast(kind)
|
||||||
|
|| ast::FormatArgsArg::can_cast(kind)
|
||||||
|
|| ast::RecordExprField::can_cast(kind)
|
||||||
|
|| ast::BinExpr::cast(node.clone())
|
||||||
|
.and_then(|expr| expr.rhs())
|
||||||
|
.is_some_and(|expr| expr.syntax() == it)
|
||||||
|
|| ast::IndexExpr::cast(node)
|
||||||
|
.and_then(|expr| expr.index())
|
||||||
|
.is_some_and(|expr| expr.syntax() == it)
|
||||||
|
};
|
||||||
|
|
||||||
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
|
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
|
||||||
// ex. trait Foo $0 {}
|
// ex. trait Foo $0 {}
|
||||||
|
|
@ -1307,7 +1326,7 @@ fn classify_name_ref<'db>(
|
||||||
.and_then(ast::LetStmt::cast)
|
.and_then(ast::LetStmt::cast)
|
||||||
.is_some_and(|it| it.semicolon_token().is_none())
|
.is_some_and(|it| it.semicolon_token().is_none())
|
||||||
|| after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw;
|
|| after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw;
|
||||||
let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some();
|
let in_value = is_in_value(it);
|
||||||
let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax())
|
let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax())
|
||||||
.and_then(Either::left);
|
.and_then(Either::left);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1953,3 +1953,25 @@ fn foo() {
|
||||||
expect![""],
|
expect![""],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_matches_with_qualifier() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- /foo.rs crate:foo
|
||||||
|
pub mod env {
|
||||||
|
pub fn var() {}
|
||||||
|
pub fn _var() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /bar.rs crate:bar deps:foo
|
||||||
|
fn main() {
|
||||||
|
env::var$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn _var() (use foo::env) fn()
|
||||||
|
fn var() (use foo::env) fn()
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Look up accessible paths for items.
|
//! Look up accessible paths for items.
|
||||||
|
|
||||||
use std::ops::ControlFlow;
|
use std::{convert::Infallible, ops::ControlFlow};
|
||||||
|
|
||||||
use hir::{
|
use hir::{
|
||||||
AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, FindPathConfig, HasCrate,
|
AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, FindPathConfig, HasCrate,
|
||||||
|
|
@ -9,6 +9,7 @@ use hir::{
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode, SyntaxNode,
|
AstNode, SyntaxNode,
|
||||||
ast::{self, HasName, make},
|
ast::{self, HasName, make},
|
||||||
|
|
@ -416,7 +417,7 @@ fn path_applicable_imports(
|
||||||
NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
|
NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
|
||||||
AssocSearchMode::Exclude,
|
AssocSearchMode::Exclude,
|
||||||
)
|
)
|
||||||
.filter_map(|(item, do_not_complete)| {
|
.flat_map(|(item, do_not_complete)| {
|
||||||
// we found imports for `first_qsegment`, now we need to filter these imports by whether
|
// we found imports for `first_qsegment`, now we need to filter these imports by whether
|
||||||
// they result in resolving the rest of the path successfully
|
// they result in resolving the rest of the path successfully
|
||||||
validate_resolvable(
|
validate_resolvable(
|
||||||
|
|
@ -446,10 +447,10 @@ fn validate_resolvable(
|
||||||
resolved_qualifier: ItemInNs,
|
resolved_qualifier: ItemInNs,
|
||||||
unresolved_qualifier: &[Name],
|
unresolved_qualifier: &[Name],
|
||||||
complete_in_flyimport: CompleteInFlyimport,
|
complete_in_flyimport: CompleteInFlyimport,
|
||||||
) -> Option<LocatedImport> {
|
) -> SmallVec<[LocatedImport; 1]> {
|
||||||
let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
|
let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
|
||||||
|
|
||||||
let qualifier = {
|
let qualifier = (|| {
|
||||||
let mut adjusted_resolved_qualifier = resolved_qualifier;
|
let mut adjusted_resolved_qualifier = resolved_qualifier;
|
||||||
if !unresolved_qualifier.is_empty() {
|
if !unresolved_qualifier.is_empty() {
|
||||||
match resolved_qualifier {
|
match resolved_qualifier {
|
||||||
|
|
@ -464,69 +465,80 @@ fn validate_resolvable(
|
||||||
}
|
}
|
||||||
|
|
||||||
match adjusted_resolved_qualifier {
|
match adjusted_resolved_qualifier {
|
||||||
ItemInNs::Types(def) => def,
|
ItemInNs::Types(def) => Some(def),
|
||||||
_ => return None,
|
_ => None,
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
let import_path_candidate = mod_path(resolved_qualifier)?;
|
let Some(qualifier) = qualifier else { return SmallVec::new() };
|
||||||
|
let Some(import_path_candidate) = mod_path(resolved_qualifier) else { return SmallVec::new() };
|
||||||
|
let mut result = SmallVec::new();
|
||||||
let ty = match qualifier {
|
let ty = match qualifier {
|
||||||
ModuleDef::Module(module) => {
|
ModuleDef::Module(module) => {
|
||||||
return items_locator::items_with_name_in_module(
|
items_locator::items_with_name_in_module::<Infallible>(
|
||||||
db,
|
db,
|
||||||
module,
|
module,
|
||||||
candidate.clone(),
|
candidate.clone(),
|
||||||
AssocSearchMode::Exclude,
|
AssocSearchMode::Exclude,
|
||||||
|it| match scope_filter(it) {
|
|item| {
|
||||||
true => ControlFlow::Break(it),
|
if scope_filter(item) {
|
||||||
false => ControlFlow::Continue(()),
|
result.push(LocatedImport::new(
|
||||||
|
import_path_candidate.clone(),
|
||||||
|
resolved_qualifier,
|
||||||
|
item,
|
||||||
|
complete_in_flyimport,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
.map(|item| {
|
return result;
|
||||||
LocatedImport::new(
|
|
||||||
import_path_candidate,
|
|
||||||
resolved_qualifier,
|
|
||||||
item,
|
|
||||||
complete_in_flyimport,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// FIXME
|
// FIXME
|
||||||
ModuleDef::Trait(_) => return None,
|
ModuleDef::Trait(_) => return SmallVec::new(),
|
||||||
ModuleDef::TypeAlias(alias) => alias.ty(db),
|
ModuleDef::TypeAlias(alias) => alias.ty(db),
|
||||||
ModuleDef::BuiltinType(builtin) => builtin.ty(db),
|
ModuleDef::BuiltinType(builtin) => builtin.ty(db),
|
||||||
ModuleDef::Adt(adt) => adt.ty(db),
|
ModuleDef::Adt(adt) => adt.ty(db),
|
||||||
_ => return None,
|
_ => return SmallVec::new(),
|
||||||
};
|
};
|
||||||
ty.iterate_path_candidates(db, scope, &FxHashSet::default(), None, None, |assoc| {
|
ty.iterate_path_candidates::<Infallible>(
|
||||||
// FIXME: Support extra trait imports
|
db,
|
||||||
if assoc.container_or_implemented_trait(db).is_some() {
|
scope,
|
||||||
return None;
|
&FxHashSet::default(),
|
||||||
}
|
None,
|
||||||
let name = assoc.name(db)?;
|
None,
|
||||||
let is_match = match candidate {
|
|assoc| {
|
||||||
NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
|
// FIXME: Support extra trait imports
|
||||||
NameToImport::Prefix(text, false) => {
|
if assoc.container_or_implemented_trait(db).is_some() {
|
||||||
name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
|
return None;
|
||||||
name_char.eq_ignore_ascii_case(&candidate_char)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
NameToImport::Exact(text, true) => name.as_str() == text,
|
let name = assoc.name(db)?;
|
||||||
NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
|
let is_match = match candidate {
|
||||||
NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
|
NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
|
||||||
NameToImport::Fuzzy(text, false) => text
|
NameToImport::Prefix(text, false) => {
|
||||||
.chars()
|
name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
|
||||||
.all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
|
name_char.eq_ignore_ascii_case(&candidate_char)
|
||||||
};
|
})
|
||||||
if !is_match {
|
}
|
||||||
return None;
|
NameToImport::Exact(text, true) => name.as_str() == text,
|
||||||
}
|
NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
|
||||||
Some(LocatedImport::new(
|
NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
|
||||||
import_path_candidate.clone(),
|
NameToImport::Fuzzy(text, false) => text.chars().all(|c| {
|
||||||
resolved_qualifier,
|
name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))
|
||||||
assoc_to_item(assoc),
|
}),
|
||||||
complete_in_flyimport,
|
};
|
||||||
))
|
if !is_match {
|
||||||
})
|
return None;
|
||||||
|
}
|
||||||
|
result.push(LocatedImport::new(
|
||||||
|
import_path_candidate.clone(),
|
||||||
|
resolved_qualifier,
|
||||||
|
assoc_to_item(assoc),
|
||||||
|
complete_in_flyimport,
|
||||||
|
));
|
||||||
|
None
|
||||||
|
},
|
||||||
|
);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
|
pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
|
||||||
|
|
|
||||||
|
|
@ -387,12 +387,14 @@ impl Definition {
|
||||||
return SearchScope::reverse_dependencies(db, module.krate());
|
return SearchScope::reverse_dependencies(db, module.krate());
|
||||||
}
|
}
|
||||||
|
|
||||||
let vis = self.visibility(db);
|
if let Some(vis) = self.visibility(db) {
|
||||||
if let Some(Visibility::Public) = vis {
|
return match vis {
|
||||||
return SearchScope::reverse_dependencies(db, module.krate());
|
Visibility::Module(module, _) => {
|
||||||
}
|
SearchScope::module_and_children(db, module.into())
|
||||||
if let Some(Visibility::Module(module, _)) = vis {
|
}
|
||||||
return SearchScope::module_and_children(db, module.into());
|
Visibility::PubCrate(krate) => SearchScope::krate(db, krate.into()),
|
||||||
|
Visibility::Public => SearchScope::reverse_dependencies(db, module.krate()),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = match module_source {
|
let range = match module_source {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use syntax::{AstNode, TextRange, ast::HasName};
|
||||||
use crate::{
|
use crate::{
|
||||||
NavigationTarget, RunnableKind,
|
NavigationTarget, RunnableKind,
|
||||||
annotations::fn_references::find_all_methods,
|
annotations::fn_references::find_all_methods,
|
||||||
goto_implementation::goto_implementation,
|
goto_implementation::{GotoImplementationConfig, goto_implementation},
|
||||||
navigation_target,
|
navigation_target,
|
||||||
references::{FindAllRefsConfig, find_all_refs},
|
references::{FindAllRefsConfig, find_all_refs},
|
||||||
runnables::{Runnable, runnables},
|
runnables::{Runnable, runnables},
|
||||||
|
|
@ -44,6 +44,7 @@ pub struct AnnotationConfig<'a> {
|
||||||
pub annotate_method_references: bool,
|
pub annotate_method_references: bool,
|
||||||
pub annotate_enum_variant_references: bool,
|
pub annotate_enum_variant_references: bool,
|
||||||
pub location: AnnotationLocation,
|
pub location: AnnotationLocation,
|
||||||
|
pub filter_adjacent_derive_implementations: bool,
|
||||||
pub minicore: MiniCore<'a>,
|
pub minicore: MiniCore<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,7 +205,12 @@ pub(crate) fn resolve_annotation(
|
||||||
) -> Annotation {
|
) -> Annotation {
|
||||||
match annotation.kind {
|
match annotation.kind {
|
||||||
AnnotationKind::HasImpls { pos, ref mut data } => {
|
AnnotationKind::HasImpls { pos, ref mut data } => {
|
||||||
*data = goto_implementation(db, pos).map(|range| range.info);
|
let goto_implementation_config = GotoImplementationConfig {
|
||||||
|
filter_adjacent_derive_implementations: config
|
||||||
|
.filter_adjacent_derive_implementations,
|
||||||
|
};
|
||||||
|
*data =
|
||||||
|
goto_implementation(db, &goto_implementation_config, pos).map(|range| range.info);
|
||||||
}
|
}
|
||||||
AnnotationKind::HasReferences { pos, ref mut data } => {
|
AnnotationKind::HasReferences { pos, ref mut data } => {
|
||||||
*data = find_all_refs(
|
*data = find_all_refs(
|
||||||
|
|
@ -253,6 +259,7 @@ mod tests {
|
||||||
annotate_enum_variant_references: true,
|
annotate_enum_variant_references: true,
|
||||||
location: AnnotationLocation::AboveName,
|
location: AnnotationLocation::AboveName,
|
||||||
minicore: MiniCore::default(),
|
minicore: MiniCore::default(),
|
||||||
|
filter_adjacent_derive_implementations: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn check_with_config(
|
fn check_with_config(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ use syntax::{AstNode, SyntaxKind::*, T, ast};
|
||||||
|
|
||||||
use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
|
use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
|
||||||
|
|
||||||
|
pub struct GotoImplementationConfig {
|
||||||
|
pub filter_adjacent_derive_implementations: bool,
|
||||||
|
}
|
||||||
|
|
||||||
// Feature: Go to Implementation
|
// Feature: Go to Implementation
|
||||||
//
|
//
|
||||||
// Navigates to the impl items of types.
|
// Navigates to the impl items of types.
|
||||||
|
|
@ -19,6 +23,7 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
|
||||||
// 
|
// 
|
||||||
pub(crate) fn goto_implementation(
|
pub(crate) fn goto_implementation(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
|
config: &GotoImplementationConfig,
|
||||||
FilePosition { file_id, offset }: FilePosition,
|
FilePosition { file_id, offset }: FilePosition,
|
||||||
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
|
|
@ -55,7 +60,19 @@ pub(crate) fn goto_implementation(
|
||||||
.and_then(|def| {
|
.and_then(|def| {
|
||||||
let navs = match def {
|
let navs = match def {
|
||||||
Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
|
Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
|
||||||
Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
|
Definition::Adt(adt) => {
|
||||||
|
let mut impls = Impl::all_for_type(db, adt.ty(sema.db));
|
||||||
|
if config.filter_adjacent_derive_implementations {
|
||||||
|
impls.retain(|impl_| {
|
||||||
|
sema.impl_generated_from_derive(*impl_) != Some(adt)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
impls
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|imp| imp.try_to_nav(&sema))
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
|
Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
|
||||||
Definition::BuiltinType(builtin) => {
|
Definition::BuiltinType(builtin) => {
|
||||||
impls_for_ty(&sema, builtin.ty(sema.db))
|
impls_for_ty(&sema, builtin.ty(sema.db))
|
||||||
|
|
@ -125,12 +142,24 @@ mod tests {
|
||||||
use ide_db::FileRange;
|
use ide_db::FileRange;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::fixture;
|
use crate::{GotoImplementationConfig, fixture};
|
||||||
|
|
||||||
|
const TEST_CONFIG: &GotoImplementationConfig =
|
||||||
|
&GotoImplementationConfig { filter_adjacent_derive_implementations: false };
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||||
|
check_with_config(TEST_CONFIG, ra_fixture);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn check_with_config(
|
||||||
|
config: &GotoImplementationConfig,
|
||||||
|
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||||
|
) {
|
||||||
let (analysis, position, expected) = fixture::annotations(ra_fixture);
|
let (analysis, position, expected) = fixture::annotations(ra_fixture);
|
||||||
|
|
||||||
let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
|
let navs = analysis.goto_implementation(config, position).unwrap().unwrap().info;
|
||||||
|
|
||||||
let cmp = |frange: &FileRange| (frange.file_id, frange.range.start());
|
let cmp = |frange: &FileRange| (frange.file_id, frange.range.start());
|
||||||
|
|
||||||
|
|
@ -416,4 +445,22 @@ fn test() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filter_adjacent_derives() {
|
||||||
|
check_with_config(
|
||||||
|
&GotoImplementationConfig { filter_adjacent_derive_implementations: true },
|
||||||
|
r#"
|
||||||
|
//- minicore: clone, copy, derive
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Foo$0;
|
||||||
|
|
||||||
|
trait Bar {}
|
||||||
|
|
||||||
|
impl Bar for Foo {}
|
||||||
|
// ^^^
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10809,7 +10809,7 @@ type Foo$0 = impl Sized;
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
needs Drop
|
no Drop
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ pub use crate::{
|
||||||
file_structure::{FileStructureConfig, StructureNode, StructureNodeKind},
|
file_structure::{FileStructureConfig, StructureNode, StructureNodeKind},
|
||||||
folding_ranges::{Fold, FoldKind},
|
folding_ranges::{Fold, FoldKind},
|
||||||
goto_definition::GotoDefinitionConfig,
|
goto_definition::GotoDefinitionConfig,
|
||||||
|
goto_implementation::GotoImplementationConfig,
|
||||||
highlight_related::{HighlightRelatedConfig, HighlightedRange},
|
highlight_related::{HighlightRelatedConfig, HighlightedRange},
|
||||||
hover::{
|
hover::{
|
||||||
HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
|
HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
|
||||||
|
|
@ -106,7 +107,7 @@ pub use crate::{
|
||||||
move_item::Direction,
|
move_item::Direction,
|
||||||
navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
|
navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
|
||||||
references::{FindAllRefsConfig, ReferenceSearchResult},
|
references::{FindAllRefsConfig, ReferenceSearchResult},
|
||||||
rename::RenameError,
|
rename::{RenameConfig, RenameError},
|
||||||
runnables::{Runnable, RunnableKind, TestId, UpdateTest},
|
runnables::{Runnable, RunnableKind, TestId, UpdateTest},
|
||||||
signature_help::SignatureHelp,
|
signature_help::SignatureHelp,
|
||||||
static_index::{
|
static_index::{
|
||||||
|
|
@ -537,9 +538,10 @@ impl Analysis {
|
||||||
/// Returns the impls from the symbol at `position`.
|
/// Returns the impls from the symbol at `position`.
|
||||||
pub fn goto_implementation(
|
pub fn goto_implementation(
|
||||||
&self,
|
&self,
|
||||||
|
config: &GotoImplementationConfig,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
|
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
|
||||||
self.with_db(|db| goto_implementation::goto_implementation(db, position))
|
self.with_db(|db| goto_implementation::goto_implementation(db, config, position))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type definitions for the symbol at `position`.
|
/// Returns the type definitions for the symbol at `position`.
|
||||||
|
|
@ -830,8 +832,9 @@ impl Analysis {
|
||||||
&self,
|
&self,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
new_name: &str,
|
new_name: &str,
|
||||||
|
config: &RenameConfig,
|
||||||
) -> Cancellable<Result<SourceChange, RenameError>> {
|
) -> Cancellable<Result<SourceChange, RenameError>> {
|
||||||
self.with_db(|db| rename::rename(db, position, new_name))
|
self.with_db(|db| rename::rename(db, position, new_name, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare_rename(
|
pub fn prepare_rename(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
//! tests. This module also implements a couple of magic tricks, like renaming
|
//! tests. This module also implements a couple of magic tricks, like renaming
|
||||||
//! `self` and to `self` (to switch between associated function and method).
|
//! `self` and to `self` (to switch between associated function and method).
|
||||||
|
|
||||||
use hir::{AsAssocItem, InFile, Name, Semantics, sym};
|
use hir::{AsAssocItem, FindPathConfig, HasContainer, HirDisplay, InFile, Name, Semantics, sym};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
FileId, FileRange, RootDatabase,
|
FileId, FileRange, RootDatabase,
|
||||||
defs::{Definition, NameClass, NameRefClass},
|
defs::{Definition, NameClass, NameRefClass},
|
||||||
|
|
@ -27,6 +27,23 @@ pub use ide_db::rename::RenameError;
|
||||||
|
|
||||||
type RenameResult<T> = Result<T, RenameError>;
|
type RenameResult<T> = Result<T, RenameError>;
|
||||||
|
|
||||||
|
pub struct RenameConfig {
|
||||||
|
pub prefer_no_std: bool,
|
||||||
|
pub prefer_prelude: bool,
|
||||||
|
pub prefer_absolute: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenameConfig {
|
||||||
|
fn find_path_config(&self) -> FindPathConfig {
|
||||||
|
FindPathConfig {
|
||||||
|
prefer_no_std: self.prefer_no_std,
|
||||||
|
prefer_prelude: self.prefer_prelude,
|
||||||
|
prefer_absolute: self.prefer_absolute,
|
||||||
|
allow_unstable: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This is similar to `collect::<Result<Vec<_>, _>>`, but unlike it, it succeeds if there is *any* `Ok` item.
|
/// This is similar to `collect::<Result<Vec<_>, _>>`, but unlike it, it succeeds if there is *any* `Ok` item.
|
||||||
fn ok_if_any<T, E>(iter: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
|
fn ok_if_any<T, E>(iter: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
|
||||||
let mut err = None;
|
let mut err = None;
|
||||||
|
|
@ -100,6 +117,7 @@ pub(crate) fn rename(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
new_name: &str,
|
new_name: &str,
|
||||||
|
config: &RenameConfig,
|
||||||
) -> RenameResult<SourceChange> {
|
) -> RenameResult<SourceChange> {
|
||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let file_id = sema
|
let file_id = sema
|
||||||
|
|
@ -158,7 +176,14 @@ pub(crate) fn rename(
|
||||||
if let Definition::Local(local) = def {
|
if let Definition::Local(local) = def {
|
||||||
if let Some(self_param) = local.as_self_param(sema.db) {
|
if let Some(self_param) = local.as_self_param(sema.db) {
|
||||||
cov_mark::hit!(rename_self_to_param);
|
cov_mark::hit!(rename_self_to_param);
|
||||||
return rename_self_to_param(&sema, local, self_param, &new_name, kind);
|
return rename_self_to_param(
|
||||||
|
&sema,
|
||||||
|
local,
|
||||||
|
self_param,
|
||||||
|
&new_name,
|
||||||
|
kind,
|
||||||
|
config.find_path_config(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if kind == IdentifierKind::LowercaseSelf {
|
if kind == IdentifierKind::LowercaseSelf {
|
||||||
cov_mark::hit!(rename_to_self);
|
cov_mark::hit!(rename_to_self);
|
||||||
|
|
@ -360,7 +385,7 @@ fn transform_assoc_fn_into_method_call(
|
||||||
f: hir::Function,
|
f: hir::Function,
|
||||||
) {
|
) {
|
||||||
let calls = Definition::Function(f).usages(sema).all();
|
let calls = Definition::Function(f).usages(sema).all();
|
||||||
for (file_id, calls) in calls {
|
for (_file_id, calls) in calls {
|
||||||
for call in calls {
|
for call in calls {
|
||||||
let Some(fn_name) = call.name.as_name_ref() else { continue };
|
let Some(fn_name) = call.name.as_name_ref() else { continue };
|
||||||
let Some(path) = fn_name.syntax().parent().and_then(ast::PathSegment::cast) else {
|
let Some(path) = fn_name.syntax().parent().and_then(ast::PathSegment::cast) else {
|
||||||
|
|
@ -409,6 +434,12 @@ fn transform_assoc_fn_into_method_call(
|
||||||
.unwrap_or_else(|| arg_list.syntax().text_range().end()),
|
.unwrap_or_else(|| arg_list.syntax().text_range().end()),
|
||||||
};
|
};
|
||||||
let replace_range = TextRange::new(replace_start, replace_end);
|
let replace_range = TextRange::new(replace_start, replace_end);
|
||||||
|
let macro_file = sema.hir_file_for(fn_name.syntax());
|
||||||
|
let Some((replace_range, _)) =
|
||||||
|
InFile::new(macro_file, replace_range).original_node_file_range_opt(sema.db)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else {
|
let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -426,8 +457,8 @@ fn transform_assoc_fn_into_method_call(
|
||||||
replacement.push('(');
|
replacement.push('(');
|
||||||
|
|
||||||
source_change.insert_source_edit(
|
source_change.insert_source_edit(
|
||||||
file_id.file_id(sema.db),
|
replace_range.file_id.file_id(sema.db),
|
||||||
TextEdit::replace(replace_range, replacement),
|
TextEdit::replace(replace_range.range, replacement),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -514,12 +545,189 @@ fn rename_to_self(
|
||||||
Ok(source_change)
|
Ok(source_change)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum CallReceiverAdjust {
|
||||||
|
Deref,
|
||||||
|
Ref,
|
||||||
|
RefMut,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_to_assoc_fn_call_self_adjust(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
self_arg: &ast::Expr,
|
||||||
|
) -> CallReceiverAdjust {
|
||||||
|
let mut result = CallReceiverAdjust::None;
|
||||||
|
let self_adjust = sema.expr_adjustments(self_arg);
|
||||||
|
if let Some(self_adjust) = self_adjust {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < self_adjust.len() {
|
||||||
|
if matches!(self_adjust[i].kind, hir::Adjust::Deref(..))
|
||||||
|
&& matches!(
|
||||||
|
self_adjust.get(i + 1),
|
||||||
|
Some(hir::Adjustment { kind: hir::Adjust::Borrow(..), .. })
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Deref then ref (reborrow), skip them.
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self_adjust[i].kind {
|
||||||
|
hir::Adjust::Deref(_) if result == CallReceiverAdjust::None => {
|
||||||
|
// Autoref takes precedence over deref, because if given a `&Type` the compiler will deref
|
||||||
|
// it automatically.
|
||||||
|
result = CallReceiverAdjust::Deref;
|
||||||
|
}
|
||||||
|
hir::Adjust::Borrow(hir::AutoBorrow::Ref(mutability)) => {
|
||||||
|
match (result, mutability) {
|
||||||
|
(CallReceiverAdjust::RefMut, hir::Mutability::Shared) => {}
|
||||||
|
(_, hir::Mutability::Mut) => result = CallReceiverAdjust::RefMut,
|
||||||
|
(_, hir::Mutability::Shared) => result = CallReceiverAdjust::Ref,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform_method_call_into_assoc_fn(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
source_change: &mut SourceChange,
|
||||||
|
f: hir::Function,
|
||||||
|
find_path_config: FindPathConfig,
|
||||||
|
) {
|
||||||
|
let calls = Definition::Function(f).usages(sema).all();
|
||||||
|
for (_file_id, calls) in calls {
|
||||||
|
for call in calls {
|
||||||
|
let Some(fn_name) = call.name.as_name_ref() else { continue };
|
||||||
|
let Some(method_call) = fn_name.syntax().parent().and_then(ast::MethodCallExpr::cast)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(mut self_arg) = method_call.receiver() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(scope) = sema.scope(fn_name.syntax()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let self_adjust = method_to_assoc_fn_call_self_adjust(sema, &self_arg);
|
||||||
|
|
||||||
|
// Strip parentheses, function arguments have higher precedence than any operator.
|
||||||
|
while let ast::Expr::ParenExpr(it) = &self_arg {
|
||||||
|
self_arg = match it.expr() {
|
||||||
|
Some(it) => it,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let needs_comma = method_call.arg_list().is_some_and(|it| it.args().next().is_some());
|
||||||
|
|
||||||
|
let self_needs_parens = self_adjust != CallReceiverAdjust::None
|
||||||
|
&& self_arg.precedence().needs_parentheses_in(ExprPrecedence::Prefix);
|
||||||
|
|
||||||
|
let replace_start = method_call.syntax().text_range().start();
|
||||||
|
let replace_end = method_call
|
||||||
|
.arg_list()
|
||||||
|
.and_then(|it| it.l_paren_token())
|
||||||
|
.map(|it| it.text_range().end())
|
||||||
|
.unwrap_or_else(|| method_call.syntax().text_range().end());
|
||||||
|
let replace_range = TextRange::new(replace_start, replace_end);
|
||||||
|
let macro_file = sema.hir_file_for(fn_name.syntax());
|
||||||
|
let Some((replace_range, _)) =
|
||||||
|
InFile::new(macro_file, replace_range).original_node_file_range_opt(sema.db)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let fn_container_path = match f.container(sema.db) {
|
||||||
|
hir::ItemContainer::Trait(trait_) => {
|
||||||
|
// FIXME: We always put it as `Trait::function`. Is it better to use `Type::function` (but
|
||||||
|
// that could conflict with an inherent method)? Or maybe `<Type as Trait>::function`?
|
||||||
|
// Or let the user decide?
|
||||||
|
let Some(path) = scope.module().find_path(
|
||||||
|
sema.db,
|
||||||
|
hir::ItemInNs::Types(trait_.into()),
|
||||||
|
find_path_config,
|
||||||
|
) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
path.display(sema.db, replace_range.file_id.edition(sema.db)).to_string()
|
||||||
|
}
|
||||||
|
hir::ItemContainer::Impl(impl_) => {
|
||||||
|
let ty = impl_.self_ty(sema.db);
|
||||||
|
match ty.as_adt() {
|
||||||
|
Some(adt) => {
|
||||||
|
let Some(path) = scope.module().find_path(
|
||||||
|
sema.db,
|
||||||
|
hir::ItemInNs::Types(adt.into()),
|
||||||
|
find_path_config,
|
||||||
|
) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
path.display(sema.db, replace_range.file_id.edition(sema.db))
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let Ok(mut ty) =
|
||||||
|
ty.display_source_code(sema.db, scope.module().into(), false)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
ty.insert(0, '<');
|
||||||
|
ty.push('>');
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let mut replacement = String::new();
|
||||||
|
replacement.push_str(&fn_container_path);
|
||||||
|
replacement.push_str("::");
|
||||||
|
format_to!(replacement, "{fn_name}");
|
||||||
|
replacement.push('(');
|
||||||
|
replacement.push_str(match self_adjust {
|
||||||
|
CallReceiverAdjust::Deref => "*",
|
||||||
|
CallReceiverAdjust::Ref => "&",
|
||||||
|
CallReceiverAdjust::RefMut => "&mut ",
|
||||||
|
CallReceiverAdjust::None => "",
|
||||||
|
});
|
||||||
|
if self_needs_parens {
|
||||||
|
replacement.push('(');
|
||||||
|
}
|
||||||
|
replacement.push_str(macro_mapped_self.text(sema.db));
|
||||||
|
if self_needs_parens {
|
||||||
|
replacement.push(')');
|
||||||
|
}
|
||||||
|
if needs_comma {
|
||||||
|
replacement.push_str(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
source_change.insert_source_edit(
|
||||||
|
replace_range.file_id.file_id(sema.db),
|
||||||
|
TextEdit::replace(replace_range.range, replacement),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rename_self_to_param(
|
fn rename_self_to_param(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
local: hir::Local,
|
local: hir::Local,
|
||||||
self_param: hir::SelfParam,
|
self_param: hir::SelfParam,
|
||||||
new_name: &Name,
|
new_name: &Name,
|
||||||
identifier_kind: IdentifierKind,
|
identifier_kind: IdentifierKind,
|
||||||
|
find_path_config: FindPathConfig,
|
||||||
) -> RenameResult<SourceChange> {
|
) -> RenameResult<SourceChange> {
|
||||||
if identifier_kind == IdentifierKind::LowercaseSelf {
|
if identifier_kind == IdentifierKind::LowercaseSelf {
|
||||||
// Let's do nothing rather than complain.
|
// Let's do nothing rather than complain.
|
||||||
|
|
@ -527,6 +735,11 @@ fn rename_self_to_param(
|
||||||
return Ok(SourceChange::default());
|
return Ok(SourceChange::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fn_def = match local.parent(sema.db) {
|
||||||
|
hir::DefWithBody::Function(func) => func,
|
||||||
|
_ => bail!("Cannot rename local to self outside of function"),
|
||||||
|
};
|
||||||
|
|
||||||
let InFile { file_id, value: self_param } =
|
let InFile { file_id, value: self_param } =
|
||||||
sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
|
sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
|
||||||
|
|
||||||
|
|
@ -554,6 +767,7 @@ fn rename_self_to_param(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
|
transform_method_call_into_assoc_fn(sema, &mut source_change, fn_def, find_path_config);
|
||||||
Ok(source_change)
|
Ok(source_change)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -587,7 +801,10 @@ mod tests {
|
||||||
|
|
||||||
use crate::fixture;
|
use crate::fixture;
|
||||||
|
|
||||||
use super::{RangeInfo, RenameError};
|
use super::{RangeInfo, RenameConfig, RenameError};
|
||||||
|
|
||||||
|
const TEST_CONFIG: RenameConfig =
|
||||||
|
RenameConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false };
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check(
|
fn check(
|
||||||
|
|
@ -603,7 +820,7 @@ mod tests {
|
||||||
panic!("Prepare rename to '{new_name}' was failed: {err}")
|
panic!("Prepare rename to '{new_name}' was failed: {err}")
|
||||||
}
|
}
|
||||||
let rename_result = analysis
|
let rename_result = analysis
|
||||||
.rename(position, new_name)
|
.rename(position, new_name, &TEST_CONFIG)
|
||||||
.unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}"));
|
.unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}"));
|
||||||
match rename_result {
|
match rename_result {
|
||||||
Ok(source_change) => {
|
Ok(source_change) => {
|
||||||
|
|
@ -635,7 +852,7 @@ mod tests {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check_conflicts(new_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
fn check_conflicts(new_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||||
let (analysis, position, conflicts) = fixture::annotations(ra_fixture);
|
let (analysis, position, conflicts) = fixture::annotations(ra_fixture);
|
||||||
let source_change = analysis.rename(position, new_name).unwrap().unwrap();
|
let source_change = analysis.rename(position, new_name, &TEST_CONFIG).unwrap().unwrap();
|
||||||
let expected_conflicts = conflicts
|
let expected_conflicts = conflicts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(file_range, _)| (file_range.file_id, file_range.range))
|
.map(|(file_range, _)| (file_range.file_id, file_range.range))
|
||||||
|
|
@ -662,8 +879,10 @@ mod tests {
|
||||||
expect: Expect,
|
expect: Expect,
|
||||||
) {
|
) {
|
||||||
let (analysis, position) = fixture::position(ra_fixture);
|
let (analysis, position) = fixture::position(ra_fixture);
|
||||||
let source_change =
|
let source_change = analysis
|
||||||
analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
|
.rename(position, new_name, &TEST_CONFIG)
|
||||||
|
.unwrap()
|
||||||
|
.expect("Expect returned a RenameError");
|
||||||
expect.assert_eq(&filter_expect(source_change))
|
expect.assert_eq(&filter_expect(source_change))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3585,6 +3804,117 @@ impl Foo {
|
||||||
|
|
||||||
fn bar(v: Foo) {
|
fn bar(v: Foo) {
|
||||||
v.foo(123);
|
v.foo(123);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_to_self_callers_in_macro() {
|
||||||
|
check(
|
||||||
|
"self",
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn foo(th$0is: &Self, v: i32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! m { ($it:expr) => { $it } }
|
||||||
|
fn bar(v: Foo) {
|
||||||
|
m!(Foo::foo(&v, 123));
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn foo(&self, v: i32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! m { ($it:expr) => { $it } }
|
||||||
|
fn bar(v: Foo) {
|
||||||
|
m!(v.foo( 123));
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_from_self_callers() {
|
||||||
|
check(
|
||||||
|
"this",
|
||||||
|
r#"
|
||||||
|
//- minicore: add
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn foo(&sel$0f) {}
|
||||||
|
}
|
||||||
|
impl core::ops::Add for Foo {
|
||||||
|
type Output = Foo;
|
||||||
|
|
||||||
|
fn add(self, _rhs: Self) -> Self::Output {
|
||||||
|
Foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(v: &Foo) {
|
||||||
|
v.foo();
|
||||||
|
(Foo + Foo).foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
mod baz {
|
||||||
|
fn baz(v: super::Foo) {
|
||||||
|
v.foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn foo(this: &Self) {}
|
||||||
|
}
|
||||||
|
impl core::ops::Add for Foo {
|
||||||
|
type Output = Foo;
|
||||||
|
|
||||||
|
fn add(self, _rhs: Self) -> Self::Output {
|
||||||
|
Foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(v: &Foo) {
|
||||||
|
Foo::foo(v);
|
||||||
|
Foo::foo(&(Foo + Foo));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod baz {
|
||||||
|
fn baz(v: super::Foo) {
|
||||||
|
crate::Foo::foo(&v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
// Multiple args:
|
||||||
|
check(
|
||||||
|
"this",
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn foo(&sel$0f, _v: i32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
Foo.foo(1);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn foo(this: &Self, _v: i32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
Foo::foo(&Foo, 1);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,9 @@ fn signature_help_for_call(
|
||||||
match callable.kind() {
|
match callable.kind() {
|
||||||
hir::CallableKind::Function(func) => {
|
hir::CallableKind::Function(func) => {
|
||||||
res.doc = func.docs(db);
|
res.doc = func.docs(db);
|
||||||
|
if func.is_async(db) {
|
||||||
|
format_to!(res.signature, "async ");
|
||||||
|
}
|
||||||
format_to!(res.signature, "fn {}", func.name(db).display(db, edition));
|
format_to!(res.signature, "fn {}", func.name(db).display(db, edition));
|
||||||
|
|
||||||
let generic_params = GenericDef::Function(func)
|
let generic_params = GenericDef::Function(func)
|
||||||
|
|
@ -283,13 +286,16 @@ fn signature_help_for_call(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match callable.kind() {
|
match callable.kind() {
|
||||||
hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
|
hir::CallableKind::Function(func) => render(func.async_ret_type(db).unwrap_or_else(|| {
|
||||||
render(func.ret_type(db))
|
if callable.return_type().contains_unknown() {
|
||||||
|
func.ret_type(db)
|
||||||
|
} else {
|
||||||
|
callable.return_type()
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
hir::CallableKind::Closure(_) | hir::CallableKind::FnPtr | hir::CallableKind::FnImpl(_) => {
|
||||||
|
render(callable.return_type())
|
||||||
}
|
}
|
||||||
hir::CallableKind::Function(_)
|
|
||||||
| hir::CallableKind::Closure(_)
|
|
||||||
| hir::CallableKind::FnPtr
|
|
||||||
| hir::CallableKind::FnImpl(_) => render(callable.return_type()),
|
|
||||||
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
|
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
|
||||||
}
|
}
|
||||||
Some(res)
|
Some(res)
|
||||||
|
|
@ -751,13 +757,7 @@ mod tests {
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||||
let fixture = format!(
|
let (db, position) = position(ra_fixture);
|
||||||
r#"
|
|
||||||
//- minicore: sized, fn
|
|
||||||
{ra_fixture}
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
let (db, position) = position(&fixture);
|
|
||||||
let sig_help = hir::attach_db(&db, || crate::signature_help::signature_help(&db, position));
|
let sig_help = hir::attach_db(&db, || crate::signature_help::signature_help(&db, position));
|
||||||
let actual = match sig_help {
|
let actual = match sig_help {
|
||||||
Some(sig_help) => {
|
Some(sig_help) => {
|
||||||
|
|
@ -795,6 +795,7 @@ mod tests {
|
||||||
fn test_fn_signature_two_args() {
|
fn test_fn_signature_two_args() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(x: u32, y: u32) -> u32 {x + y}
|
fn foo(x: u32, y: u32) -> u32 {x + y}
|
||||||
fn bar() { foo($03, ); }
|
fn bar() { foo($03, ); }
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -805,6 +806,7 @@ fn bar() { foo($03, ); }
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(x: u32, y: u32) -> u32 {x + y}
|
fn foo(x: u32, y: u32) -> u32 {x + y}
|
||||||
fn bar() { foo(3$0, ); }
|
fn bar() { foo(3$0, ); }
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -815,6 +817,7 @@ fn bar() { foo(3$0, ); }
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(x: u32, y: u32) -> u32 {x + y}
|
fn foo(x: u32, y: u32) -> u32 {x + y}
|
||||||
fn bar() { foo(3,$0 ); }
|
fn bar() { foo(3,$0 ); }
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -825,6 +828,7 @@ fn bar() { foo(3,$0 ); }
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(x: u32, y: u32) -> u32 {x + y}
|
fn foo(x: u32, y: u32) -> u32 {x + y}
|
||||||
fn bar() { foo(3, $0); }
|
fn bar() { foo(3, $0); }
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -839,6 +843,7 @@ fn bar() { foo(3, $0); }
|
||||||
fn test_fn_signature_two_args_empty() {
|
fn test_fn_signature_two_args_empty() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(x: u32, y: u32) -> u32 {x + y}
|
fn foo(x: u32, y: u32) -> u32 {x + y}
|
||||||
fn bar() { foo($0); }
|
fn bar() { foo($0); }
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -853,6 +858,7 @@ fn bar() { foo($0); }
|
||||||
fn test_fn_signature_two_args_first_generics() {
|
fn test_fn_signature_two_args_first_generics() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
|
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
|
||||||
where T: Copy + Display, U: Debug
|
where T: Copy + Display, U: Debug
|
||||||
{ x + y }
|
{ x + y }
|
||||||
|
|
@ -870,6 +876,7 @@ fn bar() { foo($03, ); }
|
||||||
fn test_fn_signature_no_params() {
|
fn test_fn_signature_no_params() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo<T>() -> T where T: Copy + Display {}
|
fn foo<T>() -> T where T: Copy + Display {}
|
||||||
fn bar() { foo($0); }
|
fn bar() { foo($0); }
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -883,6 +890,7 @@ fn bar() { foo($0); }
|
||||||
fn test_fn_signature_for_impl() {
|
fn test_fn_signature_for_impl() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct F;
|
struct F;
|
||||||
impl F { pub fn new() { } }
|
impl F { pub fn new() { } }
|
||||||
fn bar() {
|
fn bar() {
|
||||||
|
|
@ -899,6 +907,7 @@ fn bar() {
|
||||||
fn test_fn_signature_for_method_self() {
|
fn test_fn_signature_for_method_self() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
impl S { pub fn do_it(&self) {} }
|
impl S { pub fn do_it(&self) {} }
|
||||||
|
|
||||||
|
|
@ -917,6 +926,7 @@ fn bar() {
|
||||||
fn test_fn_signature_for_method_with_arg() {
|
fn test_fn_signature_for_method_with_arg() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
impl S {
|
impl S {
|
||||||
fn foo(&self, x: i32) {}
|
fn foo(&self, x: i32) {}
|
||||||
|
|
@ -935,6 +945,7 @@ fn main() { S.foo($0); }
|
||||||
fn test_fn_signature_for_generic_method() {
|
fn test_fn_signature_for_generic_method() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S<T>(T);
|
struct S<T>(T);
|
||||||
impl<T> S<T> {
|
impl<T> S<T> {
|
||||||
fn foo(&self, x: T) {}
|
fn foo(&self, x: T) {}
|
||||||
|
|
@ -953,6 +964,7 @@ fn main() { S(1u32).foo($0); }
|
||||||
fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
|
fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
impl S {
|
impl S {
|
||||||
fn foo(&self, x: i32) {}
|
fn foo(&self, x: i32) {}
|
||||||
|
|
@ -971,6 +983,7 @@ fn main() { S::foo($0); }
|
||||||
fn test_fn_signature_with_docs_simple() {
|
fn test_fn_signature_with_docs_simple() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// test
|
/// test
|
||||||
// non-doc-comment
|
// non-doc-comment
|
||||||
fn foo(j: u32) -> u32 {
|
fn foo(j: u32) -> u32 {
|
||||||
|
|
@ -994,6 +1007,7 @@ fn bar() {
|
||||||
fn test_fn_signature_with_docs() {
|
fn test_fn_signature_with_docs() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// Adds one to the number given.
|
/// Adds one to the number given.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
|
@ -1031,6 +1045,7 @@ pub fn r#do() {
|
||||||
fn test_fn_signature_with_docs_impl() {
|
fn test_fn_signature_with_docs_impl() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct addr;
|
struct addr;
|
||||||
impl addr {
|
impl addr {
|
||||||
/// Adds one to the number given.
|
/// Adds one to the number given.
|
||||||
|
|
@ -1073,6 +1088,7 @@ pub fn do_it() {
|
||||||
fn test_fn_signature_with_docs_from_actix() {
|
fn test_fn_signature_with_docs_from_actix() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Actor {
|
trait Actor {
|
||||||
/// Actor execution context type
|
/// Actor execution context type
|
||||||
type Context;
|
type Context;
|
||||||
|
|
@ -1106,6 +1122,7 @@ fn foo(mut r: impl WriteHandler<()>) {
|
||||||
fn call_info_bad_offset() {
|
fn call_info_bad_offset() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(x: u32, y: u32) -> u32 {x + y}
|
fn foo(x: u32, y: u32) -> u32 {x + y}
|
||||||
fn bar() { foo $0 (3, ); }
|
fn bar() { foo $0 (3, ); }
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -1117,6 +1134,7 @@ fn bar() { foo $0 (3, ); }
|
||||||
fn outside_of_arg_list() {
|
fn outside_of_arg_list() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(a: u8) {}
|
fn foo(a: u8) {}
|
||||||
fn f() {
|
fn f() {
|
||||||
foo(123)$0
|
foo(123)$0
|
||||||
|
|
@ -1126,6 +1144,7 @@ fn f() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo<T>(a: u8) {}
|
fn foo<T>(a: u8) {}
|
||||||
fn f() {
|
fn f() {
|
||||||
foo::<u32>$0()
|
foo::<u32>$0()
|
||||||
|
|
@ -1135,6 +1154,7 @@ fn f() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(a: u8) -> u8 {a}
|
fn foo(a: u8) -> u8 {a}
|
||||||
fn bar(a: u8) -> u8 {a}
|
fn bar(a: u8) -> u8 {a}
|
||||||
fn f() {
|
fn f() {
|
||||||
|
|
@ -1148,6 +1168,7 @@ fn f() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct Vec<T>(T);
|
struct Vec<T>(T);
|
||||||
struct Vec2<T>(T);
|
struct Vec2<T>(T);
|
||||||
fn f() {
|
fn f() {
|
||||||
|
|
@ -1165,6 +1186,7 @@ fn f() {
|
||||||
fn test_nested_method_in_lambda() {
|
fn test_nested_method_in_lambda() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct Foo;
|
struct Foo;
|
||||||
impl Foo { fn bar(&self, _: u32) { } }
|
impl Foo { fn bar(&self, _: u32) { } }
|
||||||
|
|
||||||
|
|
@ -1186,6 +1208,7 @@ fn main() {
|
||||||
fn works_for_tuple_structs() {
|
fn works_for_tuple_structs() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32);
|
struct S(u32, i32);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1205,6 +1228,7 @@ fn main() {
|
||||||
fn tuple_struct_pat() {
|
fn tuple_struct_pat() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32);
|
struct S(u32, i32);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1224,6 +1248,7 @@ fn main() {
|
||||||
fn tuple_struct_pat_rest() {
|
fn tuple_struct_pat_rest() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32, f32, u16);
|
struct S(u32, i32, f32, u16);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1239,6 +1264,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32, f32, u16, u8);
|
struct S(u32, i32, f32, u16, u8);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1254,6 +1280,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32, f32, u16);
|
struct S(u32, i32, f32, u16);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1269,6 +1296,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32, f32, u16, u8);
|
struct S(u32, i32, f32, u16, u8);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1284,6 +1312,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32, f32, u16);
|
struct S(u32, i32, f32, u16);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1299,6 +1328,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// A cool tuple struct
|
/// A cool tuple struct
|
||||||
struct S(u32, i32, f32, u16);
|
struct S(u32, i32, f32, u16);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1318,6 +1348,7 @@ fn main() {
|
||||||
fn generic_struct() {
|
fn generic_struct() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S<T>(T);
|
struct S<T>(T);
|
||||||
fn main() {
|
fn main() {
|
||||||
let s = S($0);
|
let s = S($0);
|
||||||
|
|
@ -1334,6 +1365,7 @@ fn main() {
|
||||||
fn works_for_enum_variants() {
|
fn works_for_enum_variants() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
enum E {
|
enum E {
|
||||||
/// A Variant
|
/// A Variant
|
||||||
A(i32),
|
A(i32),
|
||||||
|
|
@ -1360,6 +1392,7 @@ fn main() {
|
||||||
fn cant_call_struct_record() {
|
fn cant_call_struct_record() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S { x: u32, y: i32 }
|
struct S { x: u32, y: i32 }
|
||||||
fn main() {
|
fn main() {
|
||||||
let s = S($0);
|
let s = S($0);
|
||||||
|
|
@ -1373,6 +1406,7 @@ fn main() {
|
||||||
fn cant_call_enum_record() {
|
fn cant_call_enum_record() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
enum E {
|
enum E {
|
||||||
/// A Variant
|
/// A Variant
|
||||||
A(i32),
|
A(i32),
|
||||||
|
|
@ -1394,6 +1428,7 @@ fn main() {
|
||||||
fn fn_signature_for_call_in_macro() {
|
fn fn_signature_for_call_in_macro() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
|
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
|
||||||
fn foo() { }
|
fn foo() { }
|
||||||
id! {
|
id! {
|
||||||
|
|
@ -1410,6 +1445,7 @@ id! {
|
||||||
fn fn_signature_for_method_call_defined_in_macro() {
|
fn fn_signature_for_method_call_defined_in_macro() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
|
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
|
||||||
struct S;
|
struct S;
|
||||||
id! {
|
id! {
|
||||||
|
|
@ -1429,6 +1465,7 @@ fn test() { S.foo($0); }
|
||||||
fn call_info_for_lambdas() {
|
fn call_info_for_lambdas() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
fn foo(s: S) -> i32 { 92 }
|
fn foo(s: S) -> i32 { 92 }
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1443,6 +1480,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
fn foo(s: S) -> i32 { 92 }
|
fn foo(s: S) -> i32 { 92 }
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1456,6 +1494,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
fn foo(s: S) -> i32 { 92 }
|
fn foo(s: S) -> i32 { 92 }
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1474,6 +1513,7 @@ fn main() {
|
||||||
fn call_info_for_fn_def_over_reference() {
|
fn call_info_for_fn_def_over_reference() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
fn foo(s: S) -> i32 { 92 }
|
fn foo(s: S) -> i32 { 92 }
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -1492,6 +1532,7 @@ fn main() {
|
||||||
fn call_info_for_fn_ptr() {
|
fn call_info_for_fn_ptr() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main(f: fn(i32, f64) -> char) {
|
fn main(f: fn(i32, f64) -> char) {
|
||||||
f(0, $0)
|
f(0, $0)
|
||||||
}
|
}
|
||||||
|
|
@ -1507,6 +1548,7 @@ fn main(f: fn(i32, f64) -> char) {
|
||||||
fn call_info_for_fn_impl() {
|
fn call_info_for_fn_impl() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
impl core::ops::FnOnce<(i32, f64)> for S {
|
impl core::ops::FnOnce<(i32, f64)> for S {
|
||||||
type Output = char;
|
type Output = char;
|
||||||
|
|
@ -1524,6 +1566,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
impl core::ops::FnOnce<(i32, f64)> for S {
|
impl core::ops::FnOnce<(i32, f64)> for S {
|
||||||
type Output = char;
|
type Output = char;
|
||||||
|
|
@ -1541,6 +1584,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
impl core::ops::FnOnce<(i32, f64)> for S {
|
impl core::ops::FnOnce<(i32, f64)> for S {
|
||||||
type Output = char;
|
type Output = char;
|
||||||
|
|
@ -1556,6 +1600,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
impl core::ops::FnOnce<(i32, f64)> for S {
|
impl core::ops::FnOnce<(i32, f64)> for S {
|
||||||
type Output = char;
|
type Output = char;
|
||||||
|
|
@ -1576,6 +1621,7 @@ fn main() {
|
||||||
fn call_info_for_unclosed_call() {
|
fn call_info_for_unclosed_call() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(foo: u32, bar: u32) {}
|
fn foo(foo: u32, bar: u32) {}
|
||||||
fn main() {
|
fn main() {
|
||||||
foo($0
|
foo($0
|
||||||
|
|
@ -1588,6 +1634,7 @@ fn main() {
|
||||||
// check with surrounding space
|
// check with surrounding space
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn foo(foo: u32, bar: u32) {}
|
fn foo(foo: u32, bar: u32) {}
|
||||||
fn main() {
|
fn main() {
|
||||||
foo( $0
|
foo( $0
|
||||||
|
|
@ -1603,6 +1650,7 @@ fn main() {
|
||||||
fn test_multiline_argument() {
|
fn test_multiline_argument() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn callee(a: u8, b: u8) {}
|
fn callee(a: u8, b: u8) {}
|
||||||
fn main() {
|
fn main() {
|
||||||
callee(match 0 {
|
callee(match 0 {
|
||||||
|
|
@ -1613,6 +1661,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn callee(a: u8, b: u8) {}
|
fn callee(a: u8, b: u8) {}
|
||||||
fn main() {
|
fn main() {
|
||||||
callee(match 0 {
|
callee(match 0 {
|
||||||
|
|
@ -1626,6 +1675,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn callee(a: u8, b: u8) {}
|
fn callee(a: u8, b: u8) {}
|
||||||
fn main() {
|
fn main() {
|
||||||
callee($0match 0 {
|
callee($0match 0 {
|
||||||
|
|
@ -1643,6 +1693,7 @@ fn main() {
|
||||||
fn test_generics_simple() {
|
fn test_generics_simple() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// Option docs.
|
/// Option docs.
|
||||||
enum Option<T> {
|
enum Option<T> {
|
||||||
Some(T),
|
Some(T),
|
||||||
|
|
@ -1666,6 +1717,7 @@ fn f() {
|
||||||
fn test_generics_on_variant() {
|
fn test_generics_on_variant() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
/// Option docs.
|
/// Option docs.
|
||||||
enum Option<T> {
|
enum Option<T> {
|
||||||
/// Some docs.
|
/// Some docs.
|
||||||
|
|
@ -1693,6 +1745,7 @@ fn f() {
|
||||||
fn test_lots_of_generics() {
|
fn test_lots_of_generics() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Tr<T> {}
|
trait Tr<T> {}
|
||||||
|
|
||||||
struct S<T>(T);
|
struct S<T>(T);
|
||||||
|
|
@ -1716,6 +1769,7 @@ fn f() {
|
||||||
fn test_generics_in_trait_ufcs() {
|
fn test_generics_in_trait_ufcs() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Tr {
|
trait Tr {
|
||||||
fn f<T: Tr, U>() {}
|
fn f<T: Tr, U>() {}
|
||||||
}
|
}
|
||||||
|
|
@ -1739,6 +1793,7 @@ fn f() {
|
||||||
fn test_generics_in_method_call() {
|
fn test_generics_in_method_call() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S;
|
struct S;
|
||||||
|
|
||||||
impl S {
|
impl S {
|
||||||
|
|
@ -1760,6 +1815,7 @@ fn f() {
|
||||||
fn test_generic_param_in_method_call() {
|
fn test_generic_param_in_method_call() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct Foo;
|
struct Foo;
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn test<V>(&mut self, val: V) {}
|
fn test<V>(&mut self, val: V) {}
|
||||||
|
|
@ -1779,6 +1835,7 @@ fn sup() {
|
||||||
fn test_generic_kinds() {
|
fn test_generic_kinds() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn callee<'a, const A: u8, T, const C: u8>() {}
|
fn callee<'a, const A: u8, T, const C: u8>() {}
|
||||||
|
|
||||||
fn f() {
|
fn f() {
|
||||||
|
|
@ -1792,6 +1849,7 @@ fn f() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn callee<'a, const A: u8, T, const C: u8>() {}
|
fn callee<'a, const A: u8, T, const C: u8>() {}
|
||||||
|
|
||||||
fn f() {
|
fn f() {
|
||||||
|
|
@ -1809,6 +1867,7 @@ fn f() {
|
||||||
fn test_trait_assoc_types() {
|
fn test_trait_assoc_types() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Trait<'a, T> {
|
trait Trait<'a, T> {
|
||||||
type Assoc;
|
type Assoc;
|
||||||
}
|
}
|
||||||
|
|
@ -1821,6 +1880,7 @@ fn f() -> impl Trait<(), $0
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Iterator {
|
trait Iterator {
|
||||||
type Item;
|
type Item;
|
||||||
}
|
}
|
||||||
|
|
@ -1833,6 +1893,7 @@ fn f() -> impl Iterator<$0
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Iterator {
|
trait Iterator {
|
||||||
type Item;
|
type Item;
|
||||||
}
|
}
|
||||||
|
|
@ -1845,6 +1906,7 @@ fn f() -> impl Iterator<Item = $0
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Tr {
|
trait Tr {
|
||||||
type A;
|
type A;
|
||||||
type B;
|
type B;
|
||||||
|
|
@ -1858,6 +1920,7 @@ fn f() -> impl Tr<$0
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Tr {
|
trait Tr {
|
||||||
type A;
|
type A;
|
||||||
type B;
|
type B;
|
||||||
|
|
@ -1871,6 +1934,7 @@ fn f() -> impl Tr<B$0
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Tr {
|
trait Tr {
|
||||||
type A;
|
type A;
|
||||||
type B;
|
type B;
|
||||||
|
|
@ -1884,6 +1948,7 @@ fn f() -> impl Tr<B = $0
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Tr {
|
trait Tr {
|
||||||
type A;
|
type A;
|
||||||
type B;
|
type B;
|
||||||
|
|
@ -1901,6 +1966,7 @@ fn f() -> impl Tr<B = (), $0
|
||||||
fn test_supertrait_assoc() {
|
fn test_supertrait_assoc() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Super {
|
trait Super {
|
||||||
type SuperTy;
|
type SuperTy;
|
||||||
}
|
}
|
||||||
|
|
@ -1920,6 +1986,7 @@ fn f() -> impl Sub<$0
|
||||||
fn no_assoc_types_outside_type_bounds() {
|
fn no_assoc_types_outside_type_bounds() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Tr<T> {
|
trait Tr<T> {
|
||||||
type Assoc;
|
type Assoc;
|
||||||
}
|
}
|
||||||
|
|
@ -1938,6 +2005,7 @@ impl Tr<$0
|
||||||
// FIXME: Substitute type vars in impl trait (`U` -> `i8`)
|
// FIXME: Substitute type vars in impl trait (`U` -> `i8`)
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
trait Trait<T> {}
|
trait Trait<T> {}
|
||||||
struct Wrap<T>(T);
|
struct Wrap<T>(T);
|
||||||
fn foo<U>(x: Wrap<impl Trait<U>>) {}
|
fn foo<U>(x: Wrap<impl Trait<U>>) {}
|
||||||
|
|
@ -1956,6 +2024,7 @@ fn f() {
|
||||||
fn fully_qualified_syntax() {
|
fn fully_qualified_syntax() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn f() {
|
fn f() {
|
||||||
trait A { fn foo(&self, other: Self); }
|
trait A { fn foo(&self, other: Self); }
|
||||||
A::foo(&self$0, other);
|
A::foo(&self$0, other);
|
||||||
|
|
@ -1972,6 +2041,7 @@ fn f() {
|
||||||
fn help_for_generic_call() {
|
fn help_for_generic_call() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
|
fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
|
||||||
f($0)
|
f($0)
|
||||||
}
|
}
|
||||||
|
|
@ -1983,6 +2053,7 @@ fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn f<T, F: FnMut(&T, u16) -> &T>(f: F) {
|
fn f<T, F: FnMut(&T, u16) -> &T>(f: F) {
|
||||||
f($0)
|
f($0)
|
||||||
}
|
}
|
||||||
|
|
@ -1996,8 +2067,15 @@ fn f<T, F: FnMut(&T, u16) -> &T>(f: F) {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn regression_13579() {
|
fn regression_13579() {
|
||||||
|
// FIXME(next-solver): There should be signature help available here.
|
||||||
|
// The reason it is not is because of a trait solver bug. Since `Error` is not provided
|
||||||
|
// nor it can be inferred, it becomes an error type. The bug is that the solver ignores
|
||||||
|
// predicates on error types, and they do not guide infer vars, not allowing us to infer
|
||||||
|
// that `take`'s return type is callable.
|
||||||
|
// https://github.com/rust-lang/rust/pull/146602 should fix the solver bug.
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn f() {
|
fn f() {
|
||||||
take(2)($0);
|
take(2)($0);
|
||||||
}
|
}
|
||||||
|
|
@ -2008,9 +2086,7 @@ fn take<C, Error>(
|
||||||
move || count
|
move || count
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![""],
|
||||||
impl Fn() -> i32
|
|
||||||
"#]],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2018,6 +2094,7 @@ fn take<C, Error>(
|
||||||
fn record_literal() {
|
fn record_literal() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct Strukt<T, U = ()> {
|
struct Strukt<T, U = ()> {
|
||||||
t: T,
|
t: T,
|
||||||
u: U,
|
u: U,
|
||||||
|
|
@ -2041,6 +2118,7 @@ fn f() {
|
||||||
fn record_literal_nonexistent_field() {
|
fn record_literal_nonexistent_field() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct Strukt {
|
struct Strukt {
|
||||||
a: u8,
|
a: u8,
|
||||||
}
|
}
|
||||||
|
|
@ -2062,6 +2140,7 @@ fn f() {
|
||||||
fn tuple_variant_record_literal() {
|
fn tuple_variant_record_literal() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
enum Opt {
|
enum Opt {
|
||||||
Some(u8),
|
Some(u8),
|
||||||
}
|
}
|
||||||
|
|
@ -2076,6 +2155,7 @@ fn f() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
enum Opt {
|
enum Opt {
|
||||||
Some(u8),
|
Some(u8),
|
||||||
}
|
}
|
||||||
|
|
@ -2094,6 +2174,7 @@ fn f() {
|
||||||
fn record_literal_self() {
|
fn record_literal_self() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S { t: u8 }
|
struct S { t: u8 }
|
||||||
impl S {
|
impl S {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
|
|
@ -2112,6 +2193,7 @@ impl S {
|
||||||
fn record_pat() {
|
fn record_pat() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct Strukt<T, U = ()> {
|
struct Strukt<T, U = ()> {
|
||||||
t: T,
|
t: T,
|
||||||
u: U,
|
u: U,
|
||||||
|
|
@ -2135,6 +2217,7 @@ fn f() {
|
||||||
fn test_enum_in_nested_method_in_lambda() {
|
fn test_enum_in_nested_method_in_lambda() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
enum A {
|
enum A {
|
||||||
A,
|
A,
|
||||||
B
|
B
|
||||||
|
|
@ -2158,6 +2241,7 @@ fn main() {
|
||||||
fn test_tuple_expr_free() {
|
fn test_tuple_expr_free() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
(0$0, 1, 3);
|
(0$0, 1, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2169,6 +2253,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
($0 1, 3);
|
($0 1, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2180,6 +2265,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
(1, 3 $0);
|
(1, 3 $0);
|
||||||
}
|
}
|
||||||
|
|
@ -2191,6 +2277,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
(1, 3 $0,);
|
(1, 3 $0,);
|
||||||
}
|
}
|
||||||
|
|
@ -2206,6 +2293,7 @@ fn main() {
|
||||||
fn test_tuple_expr_expected() {
|
fn test_tuple_expr_expected() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let _: (&str, u32, u32)= ($0, 1, 3);
|
let _: (&str, u32, u32)= ($0, 1, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2218,6 +2306,7 @@ fn main() {
|
||||||
// FIXME: Should typeck report a 4-ary tuple for the expression here?
|
// FIXME: Should typeck report a 4-ary tuple for the expression here?
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let _: (&str, u32, u32, u32) = ($0, 1, 3);
|
let _: (&str, u32, u32, u32) = ($0, 1, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2229,6 +2318,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let _: (&str, u32, u32)= ($0, 1, 3, 5);
|
let _: (&str, u32, u32)= ($0, 1, 3, 5);
|
||||||
}
|
}
|
||||||
|
|
@ -2244,6 +2334,7 @@ fn main() {
|
||||||
fn test_tuple_pat_free() {
|
fn test_tuple_pat_free() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let ($0, 1, 3);
|
let ($0, 1, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2255,6 +2346,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (0$0, 1, 3);
|
let (0$0, 1, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2266,6 +2358,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let ($0 1, 3);
|
let ($0 1, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2277,6 +2370,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3 $0);
|
let (1, 3 $0);
|
||||||
}
|
}
|
||||||
|
|
@ -2288,6 +2382,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3 $0,);
|
let (1, 3 $0,);
|
||||||
}
|
}
|
||||||
|
|
@ -2299,6 +2394,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3 $0, ..);
|
let (1, 3 $0, ..);
|
||||||
}
|
}
|
||||||
|
|
@ -2310,6 +2406,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3, .., $0);
|
let (1, 3, .., $0);
|
||||||
}
|
}
|
||||||
|
|
@ -2326,6 +2423,7 @@ fn main() {
|
||||||
fn test_tuple_pat_expected() {
|
fn test_tuple_pat_expected() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (0$0, 1, 3): (i32, i32, i32);
|
let (0$0, 1, 3): (i32, i32, i32);
|
||||||
}
|
}
|
||||||
|
|
@ -2337,6 +2435,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let ($0, 1, 3): (i32, i32, i32);
|
let ($0, 1, 3): (i32, i32, i32);
|
||||||
}
|
}
|
||||||
|
|
@ -2348,6 +2447,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3 $0): (i32,);
|
let (1, 3 $0): (i32,);
|
||||||
}
|
}
|
||||||
|
|
@ -2359,6 +2459,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3 $0, ..): (i32, i32, i32, i32);
|
let (1, 3 $0, ..): (i32, i32, i32, i32);
|
||||||
}
|
}
|
||||||
|
|
@ -2370,6 +2471,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3, .., $0): (i32, i32, i32);
|
let (1, 3, .., $0): (i32, i32, i32);
|
||||||
}
|
}
|
||||||
|
|
@ -2384,6 +2486,7 @@ fn main() {
|
||||||
fn test_tuple_pat_expected_inferred() {
|
fn test_tuple_pat_expected_inferred() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (0$0, 1, 3) = (1, 2 ,3);
|
let (0$0, 1, 3) = (1, 2 ,3);
|
||||||
}
|
}
|
||||||
|
|
@ -2395,6 +2498,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let ($0 1, 3) = (1, 2, 3);
|
let ($0 1, 3) = (1, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2407,6 +2511,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3 $0) = (1,);
|
let (1, 3 $0) = (1,);
|
||||||
}
|
}
|
||||||
|
|
@ -2418,6 +2523,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3 $0, ..) = (1, 2, 3, 4);
|
let (1, 3 $0, ..) = (1, 2, 3, 4);
|
||||||
}
|
}
|
||||||
|
|
@ -2429,6 +2535,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
fn main() {
|
fn main() {
|
||||||
let (1, 3, .., $0) = (1, 2, 3);
|
let (1, 3, .., $0) = (1, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -2444,6 +2551,7 @@ fn main() {
|
||||||
fn test_tuple_generic_param() {
|
fn test_tuple_generic_param() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S<T>(T);
|
struct S<T>(T);
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -2461,6 +2569,7 @@ fn main() {
|
||||||
fn test_enum_generic_param() {
|
fn test_enum_generic_param() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
enum Option<T> {
|
enum Option<T> {
|
||||||
Some(T),
|
Some(T),
|
||||||
None,
|
None,
|
||||||
|
|
@ -2481,6 +2590,7 @@ fn main() {
|
||||||
fn test_enum_variant_generic_param() {
|
fn test_enum_variant_generic_param() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
enum Option<T> {
|
enum Option<T> {
|
||||||
Some(T),
|
Some(T),
|
||||||
None,
|
None,
|
||||||
|
|
@ -2501,6 +2611,7 @@ fn main() {
|
||||||
fn test_generic_arg_with_default() {
|
fn test_generic_arg_with_default() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S<T = u8> {
|
struct S<T = u8> {
|
||||||
field: T,
|
field: T,
|
||||||
}
|
}
|
||||||
|
|
@ -2517,6 +2628,7 @@ fn main() {
|
||||||
|
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: sized, fn
|
||||||
struct S<const C: u8 = 5> {
|
struct S<const C: u8 = 5> {
|
||||||
field: C,
|
field: C,
|
||||||
}
|
}
|
||||||
|
|
@ -2531,4 +2643,27 @@ fn main() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_async_function() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized, fn, future, result
|
||||||
|
pub async fn conn_mut<F, T>(f: F) -> Result<T, i32>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
Ok(f())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
conn_mut($0)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
async fn conn_mut<F: FnOnce() -> T, T>(f: F) -> Result<T, i32>
|
||||||
|
^^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,9 +114,9 @@ pub struct HighlightConfig<'a> {
|
||||||
// |-----------|--------------------------------|
|
// |-----------|--------------------------------|
|
||||||
// |operator| Emitted for general operators.|
|
// |operator| Emitted for general operators.|
|
||||||
// |arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.|
|
// |arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.|
|
||||||
// |bitwise| Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.|
|
// |bitwise| Emitted for the bitwise operators `\|`, `&`, `!`, `^`, `\|=`, `&=`, `^=`.|
|
||||||
// |comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.|
|
// |comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.|
|
||||||
// |logical| Emitted for the logical operators `||`, `&&`, `!`.|
|
// |logical| Emitted for the logical operators `\|\|`, `&&`, `!`.|
|
||||||
//
|
//
|
||||||
// - For punctuation:
|
// - For punctuation:
|
||||||
//
|
//
|
||||||
|
|
@ -172,20 +172,20 @@ pub struct HighlightConfig<'a> {
|
||||||
// |constant| Emitted for const.|
|
// |constant| Emitted for const.|
|
||||||
// |consuming| Emitted for locals that are being consumed when use in a function call.|
|
// |consuming| Emitted for locals that are being consumed when use in a function call.|
|
||||||
// |controlFlow| Emitted for control-flow related tokens, this includes th `?` operator.|
|
// |controlFlow| Emitted for control-flow related tokens, this includes th `?` operator.|
|
||||||
// |crateRoot| Emitted for crate names, like `serde` and `crate.|
|
// |crateRoot| Emitted for crate names, like `serde` and `crate`.|
|
||||||
// |declaration| Emitted for names of definitions, like `foo` in `fn foo(){}`.|
|
// |declaration| Emitted for names of definitions, like `foo` in `fn foo(){}`.|
|
||||||
// |defaultLibrary| Emitted for items from built-in crates (std, core, allc, test and proc_macro).|
|
// |defaultLibrary| Emitted for items from built-in crates (std, core, alloc, test and proc_macro).|
|
||||||
// |documentation| Emitted for documentation comment.|
|
// |documentation| Emitted for documentation comment.|
|
||||||
// |injected| Emitted for doc-string injected highlighting like rust source blocks in documentation.|
|
// |injected| Emitted for doc-string injected highlighting like rust source blocks in documentation.|
|
||||||
// |intraDocLink| Emitted for intra doc links in doc-string.|
|
// |intraDocLink| Emitted for intra doc links in doc-string.|
|
||||||
// |library| Emitted for items that are defined outside of the current crae.|
|
// |library| Emitted for items that are defined outside of the current crate.|
|
||||||
// |macro| Emitted for tokens inside macro call.|
|
// |macro| Emitted for tokens inside macro call.|
|
||||||
// |mutable| Emitted for mutable locals and statics as well as functions taking `&mut self`.|
|
// |mutable| Emitted for mutable locals and statics as well as functions taking `&mut self`.|
|
||||||
// |public| Emitted for items that are from the current crate and are `pub.|
|
// |public| Emitted for items that are from the current crate and are `pub`.|
|
||||||
// |reference| Emitted for locals behind a reference and functions taking self` by reference.|
|
// |reference| Emitted for locals behind a reference and functions taking `self` by reference.|
|
||||||
// |static| Emitted for "static" functions, also known as functions that d not take a `self` param, as well as statics and consts.|
|
// |static| Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts.|
|
||||||
// |trait| Emitted for associated trait item.|
|
// |trait| Emitted for associated trait item.|
|
||||||
// |unsafe| Emitted for unsafe operations, like unsafe function calls, as ell as the `unsafe` token.|
|
// |unsafe| Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token.|
|
||||||
//
|
//
|
||||||
// 
|
// 
|
||||||
// 
|
// 
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||||
<span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span>
|
<span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span>
|
||||||
<span class="keyword">fn</span> <span class="function declaration">func</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="module">y</span><span class="operator">::</span><span class="struct public">Bar</span><span class="parenthesis">)</span> <span class="brace">{</span>
|
<span class="keyword">fn</span> <span class="function declaration">func</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="module">y</span><span class="operator">::</span><span class="struct public">Bar</span><span class="parenthesis">)</span> <span class="brace">{</span>
|
||||||
<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span>
|
<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span>
|
||||||
<span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="unresolved_reference">usize</span><span class="angle">></span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span> <span class="brace">}</span>
|
<span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">></span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span> <span class="brace">}</span>
|
||||||
<span class="brace">}</span>
|
<span class="brace">}</span>
|
||||||
<span class="brace">}</span>
|
<span class="brace">}</span>
|
||||||
<span class="brace">}</span>
|
<span class="brace">}</span>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body { margin: 0; }
|
||||||
|
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
|
||||||
|
|
||||||
|
.lifetime { color: #DFAF8F; font-style: italic; }
|
||||||
|
.label { color: #DFAF8F; font-style: italic; }
|
||||||
|
.comment { color: #7F9F7F; }
|
||||||
|
.documentation { color: #629755; }
|
||||||
|
.intra_doc_link { font-style: italic; }
|
||||||
|
.injected { opacity: 0.65 ; }
|
||||||
|
.struct, .enum { color: #7CB8BB; }
|
||||||
|
.enum_variant { color: #BDE0F3; }
|
||||||
|
.string_literal { color: #CC9393; }
|
||||||
|
.field { color: #94BFF3; }
|
||||||
|
.function { color: #93E0E3; }
|
||||||
|
.parameter { color: #94BFF3; }
|
||||||
|
.text { color: #DCDCCC; }
|
||||||
|
.type { color: #7CB8BB; }
|
||||||
|
.builtin_type { color: #8CD0D3; }
|
||||||
|
.type_param { color: #DFAF8F; }
|
||||||
|
.attribute { color: #94BFF3; }
|
||||||
|
.numeric_literal { color: #BFEBBF; }
|
||||||
|
.bool_literal { color: #BFE6EB; }
|
||||||
|
.macro { color: #94BFF3; }
|
||||||
|
.proc_macro { color: #94BFF3; text-decoration: underline; }
|
||||||
|
.derive { color: #94BFF3; font-style: italic; }
|
||||||
|
.module { color: #AFD8AF; }
|
||||||
|
.value_param { color: #DCDCCC; }
|
||||||
|
.variable { color: #DCDCCC; }
|
||||||
|
.format_specifier { color: #CC696B; }
|
||||||
|
.mutable { text-decoration: underline; }
|
||||||
|
.escape_sequence { color: #94BFF3; }
|
||||||
|
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||||
|
.control { font-style: italic; }
|
||||||
|
.reference { font-style: italic; font-weight: bold; }
|
||||||
|
.const { font-weight: bolder; }
|
||||||
|
.unsafe { color: #BC8383; }
|
||||||
|
|
||||||
|
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
|
||||||
|
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
|
||||||
|
</style>
|
||||||
|
<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
|
||||||
|
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span>"{} {}, {} (подозрение на спам: {:.2}%)"б<span class="parenthesis">)</span><span class="semicolon">;</span>
|
||||||
|
<span class="brace">}</span></code></pre>
|
||||||
|
|
@ -1497,3 +1497,17 @@ fn main() {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regression_20952() {
|
||||||
|
check_highlighting(
|
||||||
|
r#"
|
||||||
|
//- minicore: fmt
|
||||||
|
fn main() {
|
||||||
|
format_args!("{} {}, {} (подозрение на спам: {:.2}%)"б);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect_file!["./test_data/regression_20952.html"],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,17 @@ pub(crate) mod entry {
|
||||||
|
|
||||||
pub(crate) fn source_file(p: &mut Parser<'_>) {
|
pub(crate) fn source_file(p: &mut Parser<'_>) {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
|
// test frontmatter
|
||||||
|
// #!/usr/bin/env cargo
|
||||||
|
//
|
||||||
|
// ---
|
||||||
|
// [dependencies]
|
||||||
|
// clap = { version = "4.2", features = ["derive"] }
|
||||||
|
// ---
|
||||||
|
//
|
||||||
|
// fn main() {}
|
||||||
p.eat(SHEBANG);
|
p.eat(SHEBANG);
|
||||||
|
p.eat(FRONTMATTER);
|
||||||
items::mod_contents(p, false);
|
items::mod_contents(p, false);
|
||||||
m.complete(p, SOURCE_FILE);
|
m.complete(p, SOURCE_FILE);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,12 @@ fn closure_expr(p: &mut Parser<'_>) -> CompletedMarker {
|
||||||
}
|
}
|
||||||
params::param_list_closure(p);
|
params::param_list_closure(p);
|
||||||
if opt_ret_type(p) {
|
if opt_ret_type(p) {
|
||||||
|
// test_err closure_ret_recovery
|
||||||
|
// fn foo() { || -> A> { let x = 1; } }
|
||||||
|
while p.at(T![>]) {
|
||||||
|
// recover from unbalanced return type brackets
|
||||||
|
p.err_and_bump("expected a curly brace");
|
||||||
|
}
|
||||||
// test lambda_ret_block
|
// test lambda_ret_block
|
||||||
// fn main() { || -> i32 { 92 }(); }
|
// fn main() { || -> i32 { 92 }(); }
|
||||||
block_expr(p);
|
block_expr(p);
|
||||||
|
|
|
||||||
|
|
@ -424,6 +424,14 @@ fn fn_(p: &mut Parser<'_>, m: Marker) {
|
||||||
// fn bar() -> () {}
|
// fn bar() -> () {}
|
||||||
opt_ret_type(p);
|
opt_ret_type(p);
|
||||||
|
|
||||||
|
// test_err fn_ret_recovery
|
||||||
|
// fn foo() -> A>]) { let x = 1; }
|
||||||
|
// fn foo() -> A>]) where T: Copy { let x = 1; }
|
||||||
|
while p.at(T![')']) | p.at(T![']']) | p.at(T![>]) {
|
||||||
|
// recover from unbalanced return type brackets
|
||||||
|
p.err_and_bump("expected a curly brace");
|
||||||
|
}
|
||||||
|
|
||||||
// test function_where_clause
|
// test function_where_clause
|
||||||
// fn foo<T>() where T: Copy {}
|
// fn foo<T>() where T: Copy {}
|
||||||
generic_params::opt_where_clause(p);
|
generic_params::opt_where_clause(p);
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,8 @@ mod ok {
|
||||||
#[test]
|
#[test]
|
||||||
fn for_type() { run_and_expect_no_errors("test_data/parser/inline/ok/for_type.rs"); }
|
fn for_type() { run_and_expect_no_errors("test_data/parser/inline/ok/for_type.rs"); }
|
||||||
#[test]
|
#[test]
|
||||||
|
fn frontmatter() { run_and_expect_no_errors("test_data/parser/inline/ok/frontmatter.rs"); }
|
||||||
|
#[test]
|
||||||
fn full_range_expr() {
|
fn full_range_expr() {
|
||||||
run_and_expect_no_errors("test_data/parser/inline/ok/full_range_expr.rs");
|
run_and_expect_no_errors("test_data/parser/inline/ok/full_range_expr.rs");
|
||||||
}
|
}
|
||||||
|
|
@ -749,6 +751,10 @@ mod err {
|
||||||
#[test]
|
#[test]
|
||||||
fn bad_asm_expr() { run_and_expect_errors("test_data/parser/inline/err/bad_asm_expr.rs"); }
|
fn bad_asm_expr() { run_and_expect_errors("test_data/parser/inline/err/bad_asm_expr.rs"); }
|
||||||
#[test]
|
#[test]
|
||||||
|
fn closure_ret_recovery() {
|
||||||
|
run_and_expect_errors("test_data/parser/inline/err/closure_ret_recovery.rs");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
fn comma_after_default_values_syntax() {
|
fn comma_after_default_values_syntax() {
|
||||||
run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs");
|
run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs");
|
||||||
}
|
}
|
||||||
|
|
@ -773,6 +779,10 @@ mod err {
|
||||||
run_and_expect_errors("test_data/parser/inline/err/fn_pointer_type_missing_fn.rs");
|
run_and_expect_errors("test_data/parser/inline/err/fn_pointer_type_missing_fn.rs");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
fn fn_ret_recovery() {
|
||||||
|
run_and_expect_errors("test_data/parser/inline/err/fn_ret_recovery.rs");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
fn gen_fn() {
|
fn gen_fn() {
|
||||||
run_and_expect_errors_with_edition(
|
run_and_expect_errors_with_edition(
|
||||||
"test_data/parser/inline/err/gen_fn.rs",
|
"test_data/parser/inline/err/gen_fn.rs",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
SOURCE_FILE
|
||||||
|
FN
|
||||||
|
FN_KW "fn"
|
||||||
|
WHITESPACE " "
|
||||||
|
NAME
|
||||||
|
IDENT "foo"
|
||||||
|
PARAM_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
WHITESPACE " "
|
||||||
|
CLOSURE_EXPR
|
||||||
|
PARAM_LIST
|
||||||
|
PIPE "|"
|
||||||
|
PIPE "|"
|
||||||
|
WHITESPACE " "
|
||||||
|
RET_TYPE
|
||||||
|
THIN_ARROW "->"
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_TYPE
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "A"
|
||||||
|
ERROR
|
||||||
|
R_ANGLE ">"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
WHITESPACE " "
|
||||||
|
LET_STMT
|
||||||
|
LET_KW "let"
|
||||||
|
WHITESPACE " "
|
||||||
|
IDENT_PAT
|
||||||
|
NAME
|
||||||
|
IDENT "x"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
LITERAL
|
||||||
|
INT_NUMBER "1"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE " "
|
||||||
|
R_CURLY "}"
|
||||||
|
WHITESPACE " "
|
||||||
|
R_CURLY "}"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
error 18: expected a curly brace
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fn foo() { || -> A> { let x = 1; } }
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
SOURCE_FILE
|
||||||
|
FN
|
||||||
|
FN_KW "fn"
|
||||||
|
WHITESPACE " "
|
||||||
|
NAME
|
||||||
|
IDENT "foo"
|
||||||
|
PARAM_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
RET_TYPE
|
||||||
|
THIN_ARROW "->"
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_TYPE
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "A"
|
||||||
|
ERROR
|
||||||
|
R_ANGLE ">"
|
||||||
|
ERROR
|
||||||
|
R_BRACK "]"
|
||||||
|
ERROR
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
WHITESPACE " "
|
||||||
|
LET_STMT
|
||||||
|
LET_KW "let"
|
||||||
|
WHITESPACE " "
|
||||||
|
IDENT_PAT
|
||||||
|
NAME
|
||||||
|
IDENT "x"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
LITERAL
|
||||||
|
INT_NUMBER "1"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE " "
|
||||||
|
R_CURLY "}"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
FN
|
||||||
|
FN_KW "fn"
|
||||||
|
WHITESPACE " "
|
||||||
|
NAME
|
||||||
|
IDENT "foo"
|
||||||
|
PARAM_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
RET_TYPE
|
||||||
|
THIN_ARROW "->"
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_TYPE
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "A"
|
||||||
|
ERROR
|
||||||
|
R_ANGLE ">"
|
||||||
|
ERROR
|
||||||
|
R_BRACK "]"
|
||||||
|
ERROR
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
WHERE_CLAUSE
|
||||||
|
WHERE_KW "where"
|
||||||
|
WHITESPACE " "
|
||||||
|
WHERE_PRED
|
||||||
|
PATH_TYPE
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "T"
|
||||||
|
COLON ":"
|
||||||
|
WHITESPACE " "
|
||||||
|
TYPE_BOUND_LIST
|
||||||
|
TYPE_BOUND
|
||||||
|
PATH_TYPE
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "Copy"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
WHITESPACE " "
|
||||||
|
LET_STMT
|
||||||
|
LET_KW "let"
|
||||||
|
WHITESPACE " "
|
||||||
|
IDENT_PAT
|
||||||
|
NAME
|
||||||
|
IDENT "x"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
LITERAL
|
||||||
|
INT_NUMBER "1"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE " "
|
||||||
|
R_CURLY "}"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
error 13: expected a curly brace
|
||||||
|
error 14: expected a curly brace
|
||||||
|
error 15: expected a curly brace
|
||||||
|
error 45: expected a curly brace
|
||||||
|
error 46: expected a curly brace
|
||||||
|
error 47: expected a curly brace
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fn foo() -> A>]) { let x = 1; }
|
||||||
|
fn foo() -> A>]) where T: Copy { let x = 1; }
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
SOURCE_FILE
|
||||||
|
SHEBANG "#!/usr/bin/env cargo\n"
|
||||||
|
FRONTMATTER "\n---\n[dependencies]\nclap = { version = \"4.2\", features = [\"derive\"] }\n---\n"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
FN
|
||||||
|
FN_KW "fn"
|
||||||
|
WHITESPACE " "
|
||||||
|
NAME
|
||||||
|
IDENT "main"
|
||||||
|
PARAM_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
R_CURLY "}"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env cargo
|
||||||
|
|
||||||
|
---
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.2", features = ["derive"] }
|
||||||
|
---
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
@ -326,7 +326,7 @@ fn test_fn_like_macro_clone_literals() {
|
||||||
PUNCH , [alone] 1
|
PUNCH , [alone] 1
|
||||||
LITERAL Str hello bridge 1
|
LITERAL Str hello bridge 1
|
||||||
PUNCH , [alone] 1
|
PUNCH , [alone] 1
|
||||||
LITERAL Str suffixedsuffix 1
|
LITERAL Err(()) "suffixed"suffix 1
|
||||||
PUNCH , [alone] 1
|
PUNCH , [alone] 1
|
||||||
LITERAL StrRaw(2) raw 1
|
LITERAL StrRaw(2) raw 1
|
||||||
PUNCH , [alone] 1
|
PUNCH , [alone] 1
|
||||||
|
|
@ -372,7 +372,7 @@ fn test_fn_like_macro_clone_literals() {
|
||||||
PUNCH , [alone] 42:Root[0000, 0]@27..28#ROOT2024
|
PUNCH , [alone] 42:Root[0000, 0]@27..28#ROOT2024
|
||||||
LITERAL Str hello bridge 42:Root[0000, 0]@29..43#ROOT2024
|
LITERAL Str hello bridge 42:Root[0000, 0]@29..43#ROOT2024
|
||||||
PUNCH , [alone] 42:Root[0000, 0]@43..44#ROOT2024
|
PUNCH , [alone] 42:Root[0000, 0]@43..44#ROOT2024
|
||||||
LITERAL Str suffixedsuffix 42:Root[0000, 0]@45..61#ROOT2024
|
LITERAL Err(()) "suffixed"suffix 42:Root[0000, 0]@45..61#ROOT2024
|
||||||
PUNCH , [alone] 42:Root[0000, 0]@61..62#ROOT2024
|
PUNCH , [alone] 42:Root[0000, 0]@61..62#ROOT2024
|
||||||
LITERAL StrRaw(2) raw 42:Root[0000, 0]@63..73#ROOT2024
|
LITERAL StrRaw(2) raw 42:Root[0000, 0]@63..73#ROOT2024
|
||||||
PUNCH , [alone] 42:Root[0000, 0]@73..74#ROOT2024
|
PUNCH , [alone] 42:Root[0000, 0]@73..74#ROOT2024
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ impl WorkspaceBuildScripts {
|
||||||
config,
|
config,
|
||||||
&allowed_features,
|
&allowed_features,
|
||||||
workspace.manifest_path(),
|
workspace.manifest_path(),
|
||||||
|
workspace.target_directory().as_ref(),
|
||||||
current_dir,
|
current_dir,
|
||||||
sysroot,
|
sysroot,
|
||||||
toolchain,
|
toolchain,
|
||||||
|
|
@ -106,8 +107,9 @@ impl WorkspaceBuildScripts {
|
||||||
let (_guard, cmd) = Self::build_command(
|
let (_guard, cmd) = Self::build_command(
|
||||||
config,
|
config,
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
// This is not gonna be used anyways, so just construct a dummy here
|
// These are not gonna be used anyways, so just construct a dummy here
|
||||||
&ManifestPath::try_from(working_directory.clone()).unwrap(),
|
&ManifestPath::try_from(working_directory.clone()).unwrap(),
|
||||||
|
working_directory.as_ref(),
|
||||||
working_directory,
|
working_directory,
|
||||||
&Sysroot::empty(),
|
&Sysroot::empty(),
|
||||||
None,
|
None,
|
||||||
|
|
@ -430,6 +432,7 @@ impl WorkspaceBuildScripts {
|
||||||
config: &CargoConfig,
|
config: &CargoConfig,
|
||||||
allowed_features: &FxHashSet<String>,
|
allowed_features: &FxHashSet<String>,
|
||||||
manifest_path: &ManifestPath,
|
manifest_path: &ManifestPath,
|
||||||
|
target_dir: &Utf8Path,
|
||||||
current_dir: &AbsPath,
|
current_dir: &AbsPath,
|
||||||
sysroot: &Sysroot,
|
sysroot: &Sysroot,
|
||||||
toolchain: Option<&semver::Version>,
|
toolchain: Option<&semver::Version>,
|
||||||
|
|
@ -450,8 +453,9 @@ impl WorkspaceBuildScripts {
|
||||||
cmd.arg("--manifest-path");
|
cmd.arg("--manifest-path");
|
||||||
cmd.arg(manifest_path);
|
cmd.arg(manifest_path);
|
||||||
|
|
||||||
if let Some(target_dir) = &config.target_dir {
|
if let Some(target_dir) = config.target_dir_config.target_dir(Some(target_dir)) {
|
||||||
cmd.arg("--target-dir").arg(target_dir);
|
cmd.arg("--target-dir");
|
||||||
|
cmd.arg(target_dir.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(target) = &config.target {
|
if let Some(target) = &config.target {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
//! See [`CargoWorkspace`].
|
//! See [`CargoWorkspace`].
|
||||||
|
|
||||||
use std::ops;
|
use std::{borrow::Cow, ops, str::from_utf8};
|
||||||
use std::str::from_utf8;
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use base_db::Env;
|
use base_db::Env;
|
||||||
|
|
@ -95,6 +94,29 @@ impl Default for CargoFeatures {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub enum TargetDirectoryConfig {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
UseSubdirectory,
|
||||||
|
Directory(Utf8PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetDirectoryConfig {
|
||||||
|
pub fn target_dir<'a>(
|
||||||
|
&'a self,
|
||||||
|
ws_target_dir: Option<&'a Utf8Path>,
|
||||||
|
) -> Option<Cow<'a, Utf8Path>> {
|
||||||
|
match self {
|
||||||
|
TargetDirectoryConfig::None => None,
|
||||||
|
TargetDirectoryConfig::UseSubdirectory => {
|
||||||
|
Some(Cow::Owned(ws_target_dir?.join("rust-analyzer")))
|
||||||
|
}
|
||||||
|
TargetDirectoryConfig::Directory(dir) => Some(Cow::Borrowed(dir)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, PartialEq, Eq)]
|
#[derive(Default, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct CargoConfig {
|
pub struct CargoConfig {
|
||||||
/// Whether to pass `--all-targets` to cargo invocations.
|
/// Whether to pass `--all-targets` to cargo invocations.
|
||||||
|
|
@ -121,7 +143,7 @@ pub struct CargoConfig {
|
||||||
pub extra_env: FxHashMap<String, Option<String>>,
|
pub extra_env: FxHashMap<String, Option<String>>,
|
||||||
pub invocation_strategy: InvocationStrategy,
|
pub invocation_strategy: InvocationStrategy,
|
||||||
/// Optional path to use instead of `target` when building
|
/// Optional path to use instead of `target` when building
|
||||||
pub target_dir: Option<Utf8PathBuf>,
|
pub target_dir_config: TargetDirectoryConfig,
|
||||||
/// Gate `#[test]` behind `#[cfg(test)]`
|
/// Gate `#[test]` behind `#[cfg(test)]`
|
||||||
pub set_test: bool,
|
pub set_test: bool,
|
||||||
/// Load the project without any dependencies
|
/// Load the project without any dependencies
|
||||||
|
|
@ -715,21 +737,15 @@ impl FetchMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> {
|
|
||||||
self.no_deps_result.as_ref().ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Executes the metadata-fetching command.
|
/// Executes the metadata-fetching command.
|
||||||
///
|
///
|
||||||
/// A successful result may still contain a metadata error if the full fetch failed,
|
/// A successful result may still contain a metadata error if the full fetch failed,
|
||||||
/// but the fallback `--no-deps` pre-fetch succeeded during command construction.
|
/// but the fallback `--no-deps` pre-fetch succeeded during command construction.
|
||||||
pub(crate) fn exec(
|
pub(crate) fn exec(
|
||||||
self,
|
self,
|
||||||
target_dir: &Utf8Path,
|
|
||||||
locked: bool,
|
locked: bool,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
||||||
_ = target_dir;
|
|
||||||
let Self {
|
let Self {
|
||||||
mut command,
|
mut command,
|
||||||
manifest_path: _,
|
manifest_path: _,
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ pub use crate::{
|
||||||
build_dependencies::{ProcMacroDylibPath, WorkspaceBuildScripts},
|
build_dependencies::{ProcMacroDylibPath, WorkspaceBuildScripts},
|
||||||
cargo_workspace::{
|
cargo_workspace::{
|
||||||
CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData,
|
CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData,
|
||||||
PackageDependency, RustLibSource, Target, TargetData, TargetKind,
|
PackageDependency, RustLibSource, Target, TargetData, TargetDirectoryConfig, TargetKind,
|
||||||
},
|
},
|
||||||
manifest_path::ManifestPath,
|
manifest_path::ManifestPath,
|
||||||
project_json::{ProjectJson, ProjectJsonData},
|
project_json::{ProjectJson, ProjectJsonData},
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use std::{env, fs, ops::Not, path::Path, process::Command};
|
||||||
|
|
||||||
use anyhow::{Result, format_err};
|
use anyhow::{Result, format_err};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use toolchain::{Tool, probe_for_binary};
|
use toolchain::{Tool, probe_for_binary};
|
||||||
|
|
@ -219,7 +219,6 @@ impl Sysroot {
|
||||||
&self,
|
&self,
|
||||||
sysroot_source_config: &RustSourceWorkspaceConfig,
|
sysroot_source_config: &RustSourceWorkspaceConfig,
|
||||||
no_deps: bool,
|
no_deps: bool,
|
||||||
target_dir: &Utf8Path,
|
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
) -> Option<RustLibSrcWorkspace> {
|
) -> Option<RustLibSrcWorkspace> {
|
||||||
assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
|
assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
|
||||||
|
|
@ -233,7 +232,6 @@ impl Sysroot {
|
||||||
match self.load_library_via_cargo(
|
match self.load_library_via_cargo(
|
||||||
&library_manifest,
|
&library_manifest,
|
||||||
src_root,
|
src_root,
|
||||||
target_dir,
|
|
||||||
cargo_config,
|
cargo_config,
|
||||||
no_deps,
|
no_deps,
|
||||||
progress,
|
progress,
|
||||||
|
|
@ -328,7 +326,6 @@ impl Sysroot {
|
||||||
&self,
|
&self,
|
||||||
library_manifest: &ManifestPath,
|
library_manifest: &ManifestPath,
|
||||||
current_dir: &AbsPath,
|
current_dir: &AbsPath,
|
||||||
target_dir: &Utf8Path,
|
|
||||||
cargo_config: &CargoMetadataConfig,
|
cargo_config: &CargoMetadataConfig,
|
||||||
no_deps: bool,
|
no_deps: bool,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
|
|
@ -345,7 +342,7 @@ impl Sysroot {
|
||||||
let locked = true;
|
let locked = true;
|
||||||
let (mut res, err) =
|
let (mut res, err) =
|
||||||
FetchMetadata::new(library_manifest, current_dir, &cargo_config, self, no_deps)
|
FetchMetadata::new(library_manifest, current_dir, &cargo_config, self, no_deps)
|
||||||
.exec(target_dir, locked, progress)?;
|
.exec(locked, progress)?;
|
||||||
|
|
||||||
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
|
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
|
||||||
// This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
|
// This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
|
||||||
|
|
|
||||||
|
|
@ -238,12 +238,8 @@ fn smoke_test_real_sysroot_cargo() {
|
||||||
);
|
);
|
||||||
let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
|
let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
|
||||||
std::fs::create_dir_all(&cwd).unwrap();
|
std::fs::create_dir_all(&cwd).unwrap();
|
||||||
let loaded_sysroot = sysroot.load_workspace(
|
let loaded_sysroot =
|
||||||
&RustSourceWorkspaceConfig::default_cargo(),
|
sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), false, &|_| ());
|
||||||
false,
|
|
||||||
&Utf8PathBuf::default(),
|
|
||||||
&|_| (),
|
|
||||||
);
|
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
sysroot.set_workspace(loaded_sysroot);
|
sysroot.set_workspace(loaded_sysroot);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use span::{Edition, FileId};
|
use span::{Edition, FileId};
|
||||||
use toolchain::{NO_RUSTUP_AUTO_INSTALL_ENV, Tool};
|
use toolchain::Tool;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
@ -295,11 +295,6 @@ impl ProjectWorkspace {
|
||||||
&sysroot,
|
&sysroot,
|
||||||
*no_deps,
|
*no_deps,
|
||||||
);
|
);
|
||||||
let target_dir = config
|
|
||||||
.target_dir
|
|
||||||
.clone()
|
|
||||||
.or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
|
|
||||||
.unwrap_or_else(|| workspace_dir.join("target").into());
|
|
||||||
|
|
||||||
// We spawn a bunch of processes to query various information about the workspace's
|
// We spawn a bunch of processes to query various information about the workspace's
|
||||||
// toolchain and sysroot
|
// toolchain and sysroot
|
||||||
|
|
@ -345,7 +340,7 @@ impl ProjectWorkspace {
|
||||||
},
|
},
|
||||||
&sysroot,
|
&sysroot,
|
||||||
*no_deps,
|
*no_deps,
|
||||||
).exec(&target_dir, true, progress) {
|
).exec(true, progress) {
|
||||||
Ok((meta, _error)) => {
|
Ok((meta, _error)) => {
|
||||||
let workspace = CargoWorkspace::new(
|
let workspace = CargoWorkspace::new(
|
||||||
meta,
|
meta,
|
||||||
|
|
@ -374,16 +369,16 @@ impl ProjectWorkspace {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let cargo_metadata = s.spawn(|| fetch_metadata.exec(&target_dir, false, progress));
|
let cargo_metadata = s.spawn(|| fetch_metadata.exec(false, progress));
|
||||||
let loaded_sysroot = s.spawn(|| {
|
let loaded_sysroot = s.spawn(|| {
|
||||||
sysroot.load_workspace(
|
sysroot.load_workspace(
|
||||||
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
||||||
config,
|
config,
|
||||||
|
workspace_dir,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
&target_dir,
|
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
@ -463,12 +458,6 @@ impl ProjectWorkspace {
|
||||||
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
|
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
|
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
|
||||||
let project_root = project_json.project_root();
|
|
||||||
let target_dir = config
|
|
||||||
.target_dir
|
|
||||||
.clone()
|
|
||||||
.or_else(|| cargo_target_dir(project_json.manifest()?, &config.extra_env, &sysroot))
|
|
||||||
.unwrap_or_else(|| project_root.join("target").into());
|
|
||||||
|
|
||||||
// We spawn a bunch of processes to query various information about the workspace's
|
// We spawn a bunch of processes to query various information about the workspace's
|
||||||
// toolchain and sysroot
|
// toolchain and sysroot
|
||||||
|
|
@ -486,18 +475,17 @@ impl ProjectWorkspace {
|
||||||
sysroot.load_workspace(
|
sysroot.load_workspace(
|
||||||
&RustSourceWorkspaceConfig::Json(*sysroot_project),
|
&RustSourceWorkspaceConfig::Json(*sysroot_project),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
&target_dir,
|
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
sysroot.load_workspace(
|
sysroot.load_workspace(
|
||||||
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
||||||
config,
|
config,
|
||||||
|
project_json.project_root(),
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
&target_dir,
|
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -545,20 +533,15 @@ impl ProjectWorkspace {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
|
let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
|
||||||
let target_data = target_data::get(query_config, None, &config.extra_env);
|
let target_data = target_data::get(query_config, None, &config.extra_env);
|
||||||
let target_dir = config
|
|
||||||
.target_dir
|
|
||||||
.clone()
|
|
||||||
.or_else(|| cargo_target_dir(detached_file, &config.extra_env, &sysroot))
|
|
||||||
.unwrap_or_else(|| dir.join("target").into());
|
|
||||||
|
|
||||||
let loaded_sysroot = sysroot.load_workspace(
|
let loaded_sysroot = sysroot.load_workspace(
|
||||||
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
||||||
config,
|
config,
|
||||||
|
dir,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
&target_dir,
|
|
||||||
&|_| (),
|
&|_| (),
|
||||||
);
|
);
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
|
|
@ -579,21 +562,15 @@ impl ProjectWorkspace {
|
||||||
&sysroot,
|
&sysroot,
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
);
|
);
|
||||||
let target_dir = config
|
let cargo_script = fetch_metadata.exec(false, &|_| ()).ok().map(|(ws, error)| {
|
||||||
.target_dir
|
let cargo_config_extra_env =
|
||||||
.clone()
|
cargo_config_env(detached_file, &config_file, &config.extra_env);
|
||||||
.or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
|
(
|
||||||
.unwrap_or_else(|| dir.join("target").into());
|
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
|
||||||
let cargo_script =
|
WorkspaceBuildScripts::default(),
|
||||||
fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| {
|
error.map(Arc::new),
|
||||||
let cargo_config_extra_env =
|
)
|
||||||
cargo_config_env(detached_file, &config_file, &config.extra_env);
|
});
|
||||||
(
|
|
||||||
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
|
|
||||||
WorkspaceBuildScripts::default(),
|
|
||||||
error.map(Arc::new),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(ProjectWorkspace {
|
Ok(ProjectWorkspace {
|
||||||
kind: ProjectWorkspaceKind::DetachedFile {
|
kind: ProjectWorkspaceKind::DetachedFile {
|
||||||
|
|
@ -1890,37 +1867,32 @@ fn add_dep_inner(graph: &mut CrateGraphBuilder, from: CrateBuilderId, dep: Depen
|
||||||
|
|
||||||
fn sysroot_metadata_config(
|
fn sysroot_metadata_config(
|
||||||
config: &CargoConfig,
|
config: &CargoConfig,
|
||||||
|
current_dir: &AbsPath,
|
||||||
targets: &[String],
|
targets: &[String],
|
||||||
toolchain_version: Option<Version>,
|
toolchain_version: Option<Version>,
|
||||||
) -> CargoMetadataConfig {
|
) -> CargoMetadataConfig {
|
||||||
|
// We run `cargo metadata` on sysroot with sysroot dir as a working directory, but still pass
|
||||||
|
// the `targets` from the cargo config evaluated from the workspace's `current_dir`.
|
||||||
|
// So, we need to *canonicalize* those *might-be-relative-paths-to-custom-target-json-files*.
|
||||||
|
//
|
||||||
|
// See https://github.com/rust-lang/cargo/blob/f7acf448fc127df9a77c52cc2bba027790ac4931/src/cargo/core/compiler/compile_kind.rs#L171-L192
|
||||||
|
let targets = targets
|
||||||
|
.iter()
|
||||||
|
.map(|target| {
|
||||||
|
if target.ends_with(".json") {
|
||||||
|
current_dir.join(target).to_string()
|
||||||
|
} else {
|
||||||
|
target.to_owned()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
CargoMetadataConfig {
|
CargoMetadataConfig {
|
||||||
features: Default::default(),
|
features: Default::default(),
|
||||||
targets: targets.to_vec(),
|
targets,
|
||||||
extra_args: Default::default(),
|
extra_args: Default::default(),
|
||||||
extra_env: config.extra_env.clone(),
|
extra_env: config.extra_env.clone(),
|
||||||
toolchain_version,
|
toolchain_version,
|
||||||
kind: "sysroot",
|
kind: "sysroot",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cargo_target_dir(
|
|
||||||
manifest: &ManifestPath,
|
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
|
||||||
sysroot: &Sysroot,
|
|
||||||
) -> Option<Utf8PathBuf> {
|
|
||||||
let cargo = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
|
||||||
let mut meta = cargo_metadata::MetadataCommand::new();
|
|
||||||
meta.env(NO_RUSTUP_AUTO_INSTALL_ENV.0, NO_RUSTUP_AUTO_INSTALL_ENV.1);
|
|
||||||
meta.cargo_path(cargo.get_program());
|
|
||||||
meta.manifest_path(manifest);
|
|
||||||
// `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve.
|
|
||||||
// So we can use it to get `target_directory` before copying lockfiles
|
|
||||||
meta.no_deps();
|
|
||||||
let mut other_options = vec![];
|
|
||||||
if manifest.is_rust_manifest() {
|
|
||||||
meta.env("RUSTC_BOOTSTRAP", "1");
|
|
||||||
other_options.push("-Zscript".to_owned());
|
|
||||||
}
|
|
||||||
meta.other_options(other_options);
|
|
||||||
meta.exec().map(|m| m.target_directory).ok()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ tenthash = "1.1.0"
|
||||||
num_cpus = "1.17.0"
|
num_cpus = "1.17.0"
|
||||||
mimalloc = { version = "0.1.46", default-features = false, optional = true }
|
mimalloc = { version = "0.1.46", default-features = false, optional = true }
|
||||||
lsp-server.workspace = true
|
lsp-server.workspace = true
|
||||||
|
smallvec.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
tracing-tree.workspace = true
|
tracing-tree.workspace = true
|
||||||
|
|
@ -53,6 +54,7 @@ semver.workspace = true
|
||||||
memchr = "2.7.5"
|
memchr = "2.7.5"
|
||||||
cargo_metadata.workspace = true
|
cargo_metadata.workspace = true
|
||||||
process-wrap.workspace = true
|
process-wrap.workspace = true
|
||||||
|
dhat = { version = "0.3.3", optional = true }
|
||||||
|
|
||||||
cfg.workspace = true
|
cfg.workspace = true
|
||||||
hir-def.workspace = true
|
hir-def.workspace = true
|
||||||
|
|
@ -105,6 +107,7 @@ in-rust-tree = [
|
||||||
"hir-ty/in-rust-tree",
|
"hir-ty/in-rust-tree",
|
||||||
"load-cargo/in-rust-tree",
|
"load-cargo/in-rust-tree",
|
||||||
]
|
]
|
||||||
|
dhat = ["dep:dhat"]
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1214,6 +1214,7 @@ impl flags::AnalysisStats {
|
||||||
annotate_method_references: false,
|
annotate_method_references: false,
|
||||||
annotate_enum_variant_references: false,
|
annotate_enum_variant_references: false,
|
||||||
location: ide::AnnotationLocation::AboveName,
|
location: ide::AnnotationLocation::AboveName,
|
||||||
|
filter_adjacent_derive_implementations: false,
|
||||||
minicore: MiniCore::default(),
|
minicore: MiniCore::default(),
|
||||||
};
|
};
|
||||||
for &file_id in file_ids {
|
for &file_id in file_ids {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use hir::{ChangeWithProcMacros, Crate};
|
||||||
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
|
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
|
||||||
use ide_db::base_db;
|
use ide_db::base_db;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
use paths::Utf8PathBuf;
|
|
||||||
use profile::StopWatch;
|
use profile::StopWatch;
|
||||||
use project_model::toolchain_info::{QueryConfig, target_data};
|
use project_model::toolchain_info::{QueryConfig, target_data};
|
||||||
use project_model::{
|
use project_model::{
|
||||||
|
|
@ -75,12 +74,8 @@ impl Tester {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env);
|
let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env);
|
||||||
let loaded_sysroot = sysroot.load_workspace(
|
let loaded_sysroot =
|
||||||
&RustSourceWorkspaceConfig::default_cargo(),
|
sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), false, &|_| ());
|
||||||
false,
|
|
||||||
&Utf8PathBuf::default(),
|
|
||||||
&|_| (),
|
|
||||||
);
|
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
sysroot.set_workspace(loaded_sysroot);
|
sysroot.set_workspace(loaded_sysroot);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ use hir::Symbol;
|
||||||
use ide::{
|
use ide::{
|
||||||
AnnotationConfig, AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
|
AnnotationConfig, AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
|
||||||
CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, GotoDefinitionConfig,
|
CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, GotoDefinitionConfig,
|
||||||
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
|
GotoImplementationConfig, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat,
|
||||||
InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
|
InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig,
|
||||||
Snippet, SnippetScope, SourceRootId,
|
MemoryLayoutHoverRenderKind, RenameConfig, Snippet, SnippetScope, SourceRootId,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
MiniCore, SnippetCap,
|
MiniCore, SnippetCap,
|
||||||
|
|
@ -23,7 +23,7 @@ use itertools::{Either, Itertools};
|
||||||
use paths::{Utf8Path, Utf8PathBuf};
|
use paths::{Utf8Path, Utf8PathBuf};
|
||||||
use project_model::{
|
use project_model::{
|
||||||
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand,
|
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand,
|
||||||
ProjectManifest, RustLibSource,
|
ProjectManifest, RustLibSource, TargetDirectoryConfig,
|
||||||
};
|
};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
@ -98,6 +98,9 @@ config_data! {
|
||||||
/// Code's `files.watcherExclude`.
|
/// Code's `files.watcherExclude`.
|
||||||
files_exclude | files_excludeDirs: Vec<Utf8PathBuf> = vec![],
|
files_exclude | files_excludeDirs: Vec<Utf8PathBuf> = vec![],
|
||||||
|
|
||||||
|
/// If this is `true`, when "Goto Implementations" and in "Implementations" lens, are triggered on a `struct` or `enum` or `union`, we filter out trait implementations that originate from `derive`s above the type.
|
||||||
|
gotoImplementations_filterAdjacentDerives: bool = false,
|
||||||
|
|
||||||
/// Highlight related return values while the cursor is on any `match`, `if`, or match arm
|
/// Highlight related return values while the cursor is on any `match`, `if`, or match arm
|
||||||
/// arrow (`=>`).
|
/// arrow (`=>`).
|
||||||
highlightRelated_branchExitPoints_enable: bool = true,
|
highlightRelated_branchExitPoints_enable: bool = true,
|
||||||
|
|
@ -378,6 +381,12 @@ config_data! {
|
||||||
/// Internal config, path to proc-macro server executable.
|
/// Internal config, path to proc-macro server executable.
|
||||||
procMacro_server: Option<Utf8PathBuf> = None,
|
procMacro_server: Option<Utf8PathBuf> = None,
|
||||||
|
|
||||||
|
/// The path where to save memory profiling output.
|
||||||
|
///
|
||||||
|
/// **Note:** Memory profiling is not enabled by default in rust-analyzer builds, you need to build
|
||||||
|
/// from source for it.
|
||||||
|
profiling_memoryProfile: Option<Utf8PathBuf> = None,
|
||||||
|
|
||||||
/// Exclude imports from find-all-references.
|
/// Exclude imports from find-all-references.
|
||||||
references_excludeImports: bool = false,
|
references_excludeImports: bool = false,
|
||||||
|
|
||||||
|
|
@ -1413,6 +1422,7 @@ pub struct LensConfig {
|
||||||
|
|
||||||
// annotations
|
// annotations
|
||||||
pub location: AnnotationLocation,
|
pub location: AnnotationLocation,
|
||||||
|
pub filter_adjacent_derive_implementations: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
@ -1469,6 +1479,7 @@ impl LensConfig {
|
||||||
annotate_enum_variant_references: self.enum_variant_refs,
|
annotate_enum_variant_references: self.enum_variant_refs,
|
||||||
location: self.location.into(),
|
location: self.location.into(),
|
||||||
minicore,
|
minicore,
|
||||||
|
filter_adjacent_derive_implementations: self.filter_adjacent_derive_implementations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1705,6 +1716,14 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rename(&self, source_root: Option<SourceRootId>) -> RenameConfig {
|
||||||
|
RenameConfig {
|
||||||
|
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
|
||||||
|
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
|
||||||
|
prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn call_hierarchy<'a>(&self, minicore: MiniCore<'a>) -> CallHierarchyConfig<'a> {
|
pub fn call_hierarchy<'a>(&self, minicore: MiniCore<'a>) -> CallHierarchyConfig<'a> {
|
||||||
CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned(), minicore }
|
CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned(), minicore }
|
||||||
}
|
}
|
||||||
|
|
@ -2157,6 +2176,11 @@ impl Config {
|
||||||
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
|
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dhat_output_file(&self) -> Option<AbsPathBuf> {
|
||||||
|
let path = self.profiling_memoryProfile().clone()?;
|
||||||
|
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ignored_proc_macros(
|
pub fn ignored_proc_macros(
|
||||||
&self,
|
&self,
|
||||||
source_root: Option<SourceRootId>,
|
source_root: Option<SourceRootId>,
|
||||||
|
|
@ -2277,7 +2301,7 @@ impl Config {
|
||||||
run_build_script_command: self.cargo_buildScripts_overrideCommand(source_root).clone(),
|
run_build_script_command: self.cargo_buildScripts_overrideCommand(source_root).clone(),
|
||||||
extra_args: self.cargo_extraArgs(source_root).clone(),
|
extra_args: self.cargo_extraArgs(source_root).clone(),
|
||||||
extra_env: self.cargo_extraEnv(source_root).clone(),
|
extra_env: self.cargo_extraEnv(source_root).clone(),
|
||||||
target_dir: self.target_dir_from_config(source_root),
|
target_dir_config: self.target_dir_from_config(source_root),
|
||||||
set_test: *self.cfg_setTest(source_root),
|
set_test: *self.cfg_setTest(source_root),
|
||||||
no_deps: *self.cargo_noDeps(source_root),
|
no_deps: *self.cargo_noDeps(source_root),
|
||||||
}
|
}
|
||||||
|
|
@ -2365,7 +2389,7 @@ impl Config {
|
||||||
extra_args: self.extra_args(source_root).clone(),
|
extra_args: self.extra_args(source_root).clone(),
|
||||||
extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(),
|
extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(),
|
||||||
extra_env: self.extra_env(source_root).clone(),
|
extra_env: self.extra_env(source_root).clone(),
|
||||||
target_dir: self.target_dir_from_config(source_root),
|
target_dir_config: self.target_dir_from_config(source_root),
|
||||||
set_test: true,
|
set_test: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2423,7 +2447,7 @@ impl Config {
|
||||||
extra_args: self.check_extra_args(source_root),
|
extra_args: self.check_extra_args(source_root),
|
||||||
extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(),
|
extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(),
|
||||||
extra_env: self.check_extra_env(source_root),
|
extra_env: self.check_extra_env(source_root),
|
||||||
target_dir: self.target_dir_from_config(source_root),
|
target_dir_config: self.target_dir_from_config(source_root),
|
||||||
set_test: *self.cfg_setTest(source_root),
|
set_test: *self.cfg_setTest(source_root),
|
||||||
},
|
},
|
||||||
ansi_color_output: self.color_diagnostic_output(),
|
ansi_color_output: self.color_diagnostic_output(),
|
||||||
|
|
@ -2431,17 +2455,12 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_dir_from_config(&self, source_root: Option<SourceRootId>) -> Option<Utf8PathBuf> {
|
fn target_dir_from_config(&self, source_root: Option<SourceRootId>) -> TargetDirectoryConfig {
|
||||||
self.cargo_targetDir(source_root).as_ref().and_then(|target_dir| match target_dir {
|
match &self.cargo_targetDir(source_root) {
|
||||||
TargetDirectory::UseSubdirectory(true) => {
|
Some(TargetDirectory::UseSubdirectory(true)) => TargetDirectoryConfig::UseSubdirectory,
|
||||||
let env_var = env::var("CARGO_TARGET_DIR").ok();
|
Some(TargetDirectory::UseSubdirectory(false)) | None => TargetDirectoryConfig::None,
|
||||||
let mut path = Utf8PathBuf::from(env_var.as_deref().unwrap_or("target"));
|
Some(TargetDirectory::Directory(dir)) => TargetDirectoryConfig::Directory(dir.clone()),
|
||||||
path.push("rust-analyzer");
|
}
|
||||||
Some(path)
|
|
||||||
}
|
|
||||||
TargetDirectory::UseSubdirectory(false) => None,
|
|
||||||
TargetDirectory::Directory(dir) => Some(dir.clone()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_on_save(&self, source_root: Option<SourceRootId>) -> bool {
|
pub fn check_on_save(&self, source_root: Option<SourceRootId>) -> bool {
|
||||||
|
|
@ -2495,6 +2514,15 @@ impl Config {
|
||||||
refs_trait: *self.lens_enable() && *self.lens_references_trait_enable(),
|
refs_trait: *self.lens_enable() && *self.lens_references_trait_enable(),
|
||||||
enum_variant_refs: *self.lens_enable() && *self.lens_references_enumVariant_enable(),
|
enum_variant_refs: *self.lens_enable() && *self.lens_references_enumVariant_enable(),
|
||||||
location: *self.lens_location(),
|
location: *self.lens_location(),
|
||||||
|
filter_adjacent_derive_implementations: *self
|
||||||
|
.gotoImplementations_filterAdjacentDerives(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn goto_implementation(&self) -> GotoImplementationConfig {
|
||||||
|
GotoImplementationConfig {
|
||||||
|
filter_adjacent_derive_implementations: *self
|
||||||
|
.gotoImplementations_filterAdjacentDerives(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3958,7 +3986,7 @@ fn doc_comment_to_string(doc: &[&str]) -> String {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::fs;
|
use std::{borrow::Cow, fs};
|
||||||
|
|
||||||
use test_utils::{ensure_file_contents, project_root};
|
use test_utils::{ensure_file_contents, project_root};
|
||||||
|
|
||||||
|
|
@ -4093,9 +4121,13 @@ mod tests {
|
||||||
|
|
||||||
(config, _, _) = config.apply_change(change);
|
(config, _, _) = config.apply_change(change);
|
||||||
assert_eq!(config.cargo_targetDir(None), &None);
|
assert_eq!(config.cargo_targetDir(None), &None);
|
||||||
assert!(
|
assert!(matches!(
|
||||||
matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none())
|
config.flycheck(None),
|
||||||
);
|
FlycheckConfig::CargoCommand {
|
||||||
|
options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. },
|
||||||
|
..
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -4111,11 +4143,16 @@ mod tests {
|
||||||
(config, _, _) = config.apply_change(change);
|
(config, _, _) = config.apply_change(change);
|
||||||
|
|
||||||
assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true)));
|
assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true)));
|
||||||
let target =
|
let ws_target_dir =
|
||||||
Utf8PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned()));
|
Utf8PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned()));
|
||||||
assert!(
|
assert!(matches!(
|
||||||
matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(target.join("rust-analyzer")))
|
config.flycheck(None),
|
||||||
);
|
FlycheckConfig::CargoCommand {
|
||||||
|
options: CargoOptions { target_dir_config, .. },
|
||||||
|
..
|
||||||
|
} if target_dir_config.target_dir(Some(&ws_target_dir)).map(Cow::into_owned)
|
||||||
|
== Some(ws_target_dir.join("rust-analyzer"))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -4134,8 +4171,13 @@ mod tests {
|
||||||
config.cargo_targetDir(None),
|
config.cargo_targetDir(None),
|
||||||
&Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
|
&Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(matches!(
|
||||||
matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder")))
|
config.flycheck(None),
|
||||||
);
|
FlycheckConfig::CargoCommand {
|
||||||
|
options: CargoOptions { target_dir_config, .. },
|
||||||
|
..
|
||||||
|
} if target_dir_config.target_dir(None).map(Cow::into_owned)
|
||||||
|
== Some(Utf8PathBuf::from("other_folder"))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//! Book keeping for keeping diagnostics easily in sync with the client.
|
//! Book keeping for keeping diagnostics easily in sync with the client.
|
||||||
pub(crate) mod to_proto;
|
pub(crate) mod flycheck_to_proto;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
|
@ -8,6 +8,7 @@ use ide::FileId;
|
||||||
use ide_db::{FxHashMap, base_db::DbPanicContext};
|
use ide_db::{FxHashMap, base_db::DbPanicContext};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use stdx::iter_eq_by;
|
use stdx::iter_eq_by;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ pub(crate) struct DiagnosticCollection {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct Fix {
|
pub(crate) struct Fix {
|
||||||
// Fixes may be triggerable from multiple ranges.
|
// Fixes may be triggerable from multiple ranges.
|
||||||
pub(crate) ranges: Vec<lsp_types::Range>,
|
pub(crate) ranges: SmallVec<[lsp_types::Range; 1]>,
|
||||||
pub(crate) action: lsp_ext::CodeAction,
|
pub(crate) action: lsp_ext::CodeAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
use crate::flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
|
use crate::flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use vfs::{AbsPath, AbsPathBuf};
|
use vfs::{AbsPath, AbsPathBuf};
|
||||||
|
|
||||||
|
|
@ -18,12 +19,12 @@ use super::{DiagnosticsMapConfig, Fix};
|
||||||
fn diagnostic_severity(
|
fn diagnostic_severity(
|
||||||
config: &DiagnosticsMapConfig,
|
config: &DiagnosticsMapConfig,
|
||||||
level: crate::flycheck::DiagnosticLevel,
|
level: crate::flycheck::DiagnosticLevel,
|
||||||
code: Option<crate::flycheck::DiagnosticCode>,
|
code: Option<&crate::flycheck::DiagnosticCode>,
|
||||||
) -> Option<lsp_types::DiagnosticSeverity> {
|
) -> Option<lsp_types::DiagnosticSeverity> {
|
||||||
let res = match level {
|
let res = match level {
|
||||||
DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::ERROR,
|
DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::ERROR,
|
||||||
DiagnosticLevel::Error => lsp_types::DiagnosticSeverity::ERROR,
|
DiagnosticLevel::Error => lsp_types::DiagnosticSeverity::ERROR,
|
||||||
DiagnosticLevel::Warning => match &code {
|
DiagnosticLevel::Warning => match code {
|
||||||
// HACK: special case for `warnings` rustc lint.
|
// HACK: special case for `warnings` rustc lint.
|
||||||
Some(code)
|
Some(code)
|
||||||
if config.warnings_as_hint.iter().any(|lint| {
|
if config.warnings_as_hint.iter().any(|lint| {
|
||||||
|
|
@ -143,11 +144,11 @@ fn primary_location(
|
||||||
fn diagnostic_related_information(
|
fn diagnostic_related_information(
|
||||||
config: &DiagnosticsMapConfig,
|
config: &DiagnosticsMapConfig,
|
||||||
workspace_root: &AbsPath,
|
workspace_root: &AbsPath,
|
||||||
span: &DiagnosticSpan,
|
span: DiagnosticSpan,
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
) -> Option<lsp_types::DiagnosticRelatedInformation> {
|
) -> Option<lsp_types::DiagnosticRelatedInformation> {
|
||||||
let message = span.label.clone()?;
|
let location = location(config, workspace_root, &span, snap);
|
||||||
let location = location(config, workspace_root, span, snap);
|
let message = span.label?;
|
||||||
Some(lsp_types::DiagnosticRelatedInformation { location, message })
|
Some(lsp_types::DiagnosticRelatedInformation { location, message })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +185,7 @@ fn map_rust_child_diagnostic(
|
||||||
rd: &crate::flycheck::Diagnostic,
|
rd: &crate::flycheck::Diagnostic,
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
) -> MappedRustChildDiagnostic {
|
) -> MappedRustChildDiagnostic {
|
||||||
let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
|
let spans: SmallVec<[&DiagnosticSpan; 1]> = rd.spans.iter().filter(|s| s.is_primary).collect();
|
||||||
if spans.is_empty() {
|
if spans.is_empty() {
|
||||||
// `rustc` uses these spanless children as a way to print multi-line
|
// `rustc` uses these spanless children as a way to print multi-line
|
||||||
// messages
|
// messages
|
||||||
|
|
@ -227,42 +228,37 @@ fn map_rust_child_diagnostic(
|
||||||
message.push_str(&suggestions);
|
message.push_str(&suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if edit_map.is_empty() {
|
let suggested_fix = if edit_map.is_empty() {
|
||||||
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
|
None
|
||||||
related: lsp_types::DiagnosticRelatedInformation {
|
|
||||||
location: location(config, workspace_root, spans[0], snap),
|
|
||||||
message,
|
|
||||||
},
|
|
||||||
suggested_fix: None,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
|
Some(Box::new(Fix {
|
||||||
related: lsp_types::DiagnosticRelatedInformation {
|
ranges: spans
|
||||||
location: location(config, workspace_root, spans[0], snap),
|
.iter()
|
||||||
message: message.clone(),
|
.map(|&span| location(config, workspace_root, span, snap).range)
|
||||||
|
.collect(),
|
||||||
|
action: lsp_ext::CodeAction {
|
||||||
|
title: message.clone(),
|
||||||
|
group: None,
|
||||||
|
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
|
||||||
|
edit: Some(lsp_ext::SnippetWorkspaceEdit {
|
||||||
|
// FIXME: there's no good reason to use edit_map here....
|
||||||
|
changes: Some(edit_map),
|
||||||
|
document_changes: None,
|
||||||
|
change_annotations: None,
|
||||||
|
}),
|
||||||
|
is_preferred: Some(is_preferred),
|
||||||
|
data: None,
|
||||||
|
command: None,
|
||||||
},
|
},
|
||||||
suggested_fix: Some(Box::new(Fix {
|
}))
|
||||||
ranges: spans
|
};
|
||||||
.iter()
|
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
|
||||||
.map(|&span| location(config, workspace_root, span, snap).range)
|
related: lsp_types::DiagnosticRelatedInformation {
|
||||||
.collect(),
|
location: location(config, workspace_root, spans[0], snap),
|
||||||
action: lsp_ext::CodeAction {
|
message,
|
||||||
title: message,
|
},
|
||||||
group: None,
|
suggested_fix,
|
||||||
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
|
})
|
||||||
edit: Some(lsp_ext::SnippetWorkspaceEdit {
|
|
||||||
// FIXME: there's no good reason to use edit_map here....
|
|
||||||
changes: Some(edit_map),
|
|
||||||
document_changes: None,
|
|
||||||
change_annotations: None,
|
|
||||||
}),
|
|
||||||
is_preferred: Some(is_preferred),
|
|
||||||
data: None,
|
|
||||||
command: None,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -284,48 +280,56 @@ pub(crate) struct MappedRustDiagnostic {
|
||||||
/// If the diagnostic has no primary span this will return `None`
|
/// If the diagnostic has no primary span this will return `None`
|
||||||
pub(crate) fn map_rust_diagnostic_to_lsp(
|
pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
config: &DiagnosticsMapConfig,
|
config: &DiagnosticsMapConfig,
|
||||||
rd: &crate::flycheck::Diagnostic,
|
crate::flycheck::Diagnostic {
|
||||||
|
mut message,
|
||||||
|
code: diagnostic_code,
|
||||||
|
level,
|
||||||
|
spans,
|
||||||
|
children,
|
||||||
|
rendered,
|
||||||
|
..
|
||||||
|
}: crate::flycheck::Diagnostic,
|
||||||
workspace_root: &AbsPath,
|
workspace_root: &AbsPath,
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
) -> Vec<MappedRustDiagnostic> {
|
) -> Vec<MappedRustDiagnostic> {
|
||||||
let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
|
let (primary_spans, secondary_spans): (
|
||||||
|
SmallVec<[DiagnosticSpan; 1]>,
|
||||||
|
SmallVec<[DiagnosticSpan; 1]>,
|
||||||
|
) = spans.into_iter().partition(|s| s.is_primary);
|
||||||
if primary_spans.is_empty() {
|
if primary_spans.is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let severity = diagnostic_severity(config, rd.level, rd.code.clone());
|
let mut code = diagnostic_code.as_ref().map(|c| &*c.code);
|
||||||
|
|
||||||
let mut source = String::from("rustc");
|
if let Some(code_val) = code
|
||||||
let mut code = rd.code.as_ref().map(|c| c.code.clone());
|
|
||||||
|
|
||||||
if let Some(code_val) = &code
|
|
||||||
&& config.check_ignore.contains(code_val)
|
&& config.check_ignore.contains(code_val)
|
||||||
{
|
{
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(code_val) = &code {
|
let severity = diagnostic_severity(config, level, diagnostic_code.as_ref());
|
||||||
|
|
||||||
|
let mut source = "rustc";
|
||||||
|
if let Some(code_val) = code {
|
||||||
// See if this is an RFC #2103 scoped lint (e.g. from Clippy)
|
// See if this is an RFC #2103 scoped lint (e.g. from Clippy)
|
||||||
let scoped_code: Vec<&str> = code_val.split("::").collect();
|
if let Some((s, c)) = code_val.split("::").collect_tuple() {
|
||||||
if scoped_code.len() == 2 {
|
source = s;
|
||||||
source = String::from(scoped_code[0]);
|
code = Some(c);
|
||||||
code = Some(String::from(scoped_code[1]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut needs_primary_span_label = true;
|
let mut needs_primary_span_label = true;
|
||||||
let mut subdiagnostics = Vec::new();
|
let mut subdiagnostics = Vec::new();
|
||||||
let mut tags = Vec::new();
|
|
||||||
|
|
||||||
for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
|
for secondary_span in secondary_spans {
|
||||||
let related = diagnostic_related_information(config, workspace_root, secondary_span, snap);
|
let related = diagnostic_related_information(config, workspace_root, secondary_span, snap);
|
||||||
if let Some(related) = related {
|
if let Some(related) = related {
|
||||||
subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
|
subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut message = rd.message.clone();
|
for child in &children {
|
||||||
for child in &rd.children {
|
|
||||||
let child = map_rust_child_diagnostic(config, workspace_root, child, snap);
|
let child = map_rust_child_diagnostic(config, workspace_root, child, snap);
|
||||||
match child {
|
match child {
|
||||||
MappedRustChildDiagnostic::SubDiagnostic(sub) => {
|
MappedRustChildDiagnostic::SubDiagnostic(sub) => {
|
||||||
|
|
@ -340,155 +344,146 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let message = message;
|
||||||
|
|
||||||
if let Some(code) = &rd.code {
|
let mut tag = None;
|
||||||
let code = code.code.as_str();
|
if let Some(code) = &diagnostic_code {
|
||||||
if matches!(
|
match &*code.code {
|
||||||
code,
|
"dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
|
||||||
"dead_code"
|
| "unused_imports" | "unused_macros" | "unused_variables" => {
|
||||||
| "unknown_lints"
|
tag = Some(lsp_types::DiagnosticTag::UNNECESSARY);
|
||||||
| "unreachable_code"
|
}
|
||||||
| "unused_attributes"
|
"deprecated" => {
|
||||||
| "unused_imports"
|
tag = Some(lsp_types::DiagnosticTag::DEPRECATED);
|
||||||
| "unused_macros"
|
}
|
||||||
| "unused_variables"
|
_ => {}
|
||||||
) {
|
|
||||||
tags.push(lsp_types::DiagnosticTag::UNNECESSARY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(code, "deprecated") {
|
|
||||||
tags.push(lsp_types::DiagnosticTag::DEPRECATED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let code_description = match source.as_str() {
|
let code_description = match source {
|
||||||
"rustc" => rustc_code_description(code.as_deref()),
|
"rustc" => rustc_code_description(code),
|
||||||
"clippy" => clippy_code_description(code.as_deref()),
|
"clippy" => clippy_code_description(code),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
// Each primary diagnostic span may result in multiple LSP diagnostics.
|
||||||
|
let mut diagnostics = Vec::new();
|
||||||
|
|
||||||
primary_spans
|
for primary_span in primary_spans {
|
||||||
.iter()
|
let primary_location = primary_location(config, workspace_root, &primary_span, snap);
|
||||||
.flat_map(|primary_span| {
|
let message = {
|
||||||
let primary_location = primary_location(config, workspace_root, primary_span, snap);
|
let mut message = message.clone();
|
||||||
let message = {
|
if needs_primary_span_label && let Some(primary_span_label) = &primary_span.label {
|
||||||
let mut message = message.clone();
|
format_to!(message, "\n{}", primary_span_label);
|
||||||
if needs_primary_span_label && let Some(primary_span_label) = &primary_span.label {
|
}
|
||||||
format_to!(message, "\n{}", primary_span_label);
|
message
|
||||||
}
|
};
|
||||||
message
|
|
||||||
};
|
|
||||||
// Each primary diagnostic span may result in multiple LSP diagnostics.
|
|
||||||
let mut diagnostics = Vec::new();
|
|
||||||
|
|
||||||
let mut related_info_macro_calls = vec![];
|
let mut related_info_macro_calls = vec![];
|
||||||
|
|
||||||
// If error occurs from macro expansion, add related info pointing to
|
// If error occurs from macro expansion, add related info pointing to
|
||||||
// where the error originated
|
// where the error originated
|
||||||
// Also, we would generate an additional diagnostic, so that exact place of macro
|
// Also, we would generate an additional diagnostic, so that exact place of macro
|
||||||
// will be highlighted in the error origin place.
|
// will be highlighted in the error origin place.
|
||||||
let span_stack = std::iter::successors(Some(*primary_span), |span| {
|
let span_stack =
|
||||||
Some(&span.expansion.as_ref()?.span)
|
std::iter::successors(Some(&primary_span), |span| Some(&span.expansion.as_ref()?.span))
|
||||||
});
|
.skip(1);
|
||||||
for (i, span) in span_stack.enumerate() {
|
for (i, span) in span_stack.enumerate() {
|
||||||
if is_dummy_macro_file(&span.file_name) {
|
if is_dummy_macro_file(&span.file_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let secondary_location = location(config, workspace_root, span, snap);
|
||||||
// First span is the original diagnostic, others are macro call locations that
|
if secondary_location == primary_location {
|
||||||
// generated that code.
|
continue;
|
||||||
let is_in_macro_call = i != 0;
|
|
||||||
|
|
||||||
let secondary_location = location(config, workspace_root, span, snap);
|
|
||||||
if secondary_location == primary_location {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation {
|
|
||||||
location: secondary_location.clone(),
|
|
||||||
message: if is_in_macro_call {
|
|
||||||
"Error originated from macro call here".to_owned()
|
|
||||||
} else {
|
|
||||||
"Actual error occurred here".to_owned()
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
|
|
||||||
let information_for_additional_diagnostic =
|
|
||||||
vec![lsp_types::DiagnosticRelatedInformation {
|
|
||||||
location: primary_location.clone(),
|
|
||||||
message: "Exact error occurred here".to_owned(),
|
|
||||||
}];
|
|
||||||
|
|
||||||
let diagnostic = lsp_types::Diagnostic {
|
|
||||||
range: secondary_location.range,
|
|
||||||
// downgrade to hint if we're pointing at the macro
|
|
||||||
severity: Some(lsp_types::DiagnosticSeverity::HINT),
|
|
||||||
code: code.clone().map(lsp_types::NumberOrString::String),
|
|
||||||
code_description: code_description.clone(),
|
|
||||||
source: Some(source.clone()),
|
|
||||||
message: message.clone(),
|
|
||||||
related_information: Some(information_for_additional_diagnostic),
|
|
||||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
|
||||||
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
|
||||||
};
|
|
||||||
diagnostics.push(MappedRustDiagnostic {
|
|
||||||
url: secondary_location.uri,
|
|
||||||
diagnostic,
|
|
||||||
fix: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit the primary diagnostic.
|
// First span is the original diagnostic, others are macro call locations that
|
||||||
diagnostics.push(MappedRustDiagnostic {
|
// generated that code.
|
||||||
url: primary_location.uri.clone(),
|
let is_in_macro_call = i != 0;
|
||||||
diagnostic: lsp_types::Diagnostic {
|
|
||||||
range: primary_location.range,
|
related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation {
|
||||||
severity,
|
location: secondary_location.clone(),
|
||||||
code: code.clone().map(lsp_types::NumberOrString::String),
|
message: if is_in_macro_call {
|
||||||
code_description: code_description.clone(),
|
"Error originated from macro call here".to_owned()
|
||||||
source: Some(source.clone()),
|
} else {
|
||||||
message,
|
"Actual error occurred here".to_owned()
|
||||||
related_information: {
|
|
||||||
let info = related_info_macro_calls
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.chain(subdiagnostics.iter().map(|sub| sub.related.clone()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if info.is_empty() { None } else { Some(info) }
|
|
||||||
},
|
|
||||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
|
||||||
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
// For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
|
||||||
|
let information_for_additional_diagnostic =
|
||||||
|
vec![lsp_types::DiagnosticRelatedInformation {
|
||||||
|
location: primary_location.clone(),
|
||||||
|
message: "Exact error occurred here".to_owned(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
let diagnostic = lsp_types::Diagnostic {
|
||||||
|
range: secondary_location.range,
|
||||||
|
// downgrade to hint if we're pointing at the macro
|
||||||
|
severity: Some(lsp_types::DiagnosticSeverity::HINT),
|
||||||
|
code: code.map(ToOwned::to_owned).map(lsp_types::NumberOrString::String),
|
||||||
|
code_description: code_description.clone(),
|
||||||
|
source: Some(source.to_owned()),
|
||||||
|
message: message.clone(),
|
||||||
|
related_information: Some(information_for_additional_diagnostic),
|
||||||
|
tags: tag.clone().map(|tag| vec![tag]),
|
||||||
|
data: Some(serde_json::json!({ "rendered": rendered })),
|
||||||
|
};
|
||||||
|
diagnostics.push(MappedRustDiagnostic {
|
||||||
|
url: secondary_location.uri,
|
||||||
|
diagnostic,
|
||||||
fix: None,
|
fix: None,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Emit hint-level diagnostics for all `related_information` entries such as "help"s.
|
// Emit the primary diagnostic.
|
||||||
// This is useful because they will show up in the user's editor, unlike
|
diagnostics.push(MappedRustDiagnostic {
|
||||||
// `related_information`, which just produces hard-to-read links, at least in VS Code.
|
url: primary_location.uri.clone(),
|
||||||
let back_ref = lsp_types::DiagnosticRelatedInformation {
|
diagnostic: lsp_types::Diagnostic {
|
||||||
location: primary_location,
|
range: primary_location.range,
|
||||||
message: "original diagnostic".to_owned(),
|
severity,
|
||||||
};
|
code: code.map(ToOwned::to_owned).map(lsp_types::NumberOrString::String),
|
||||||
for sub in &subdiagnostics {
|
code_description: code_description.clone(),
|
||||||
diagnostics.push(MappedRustDiagnostic {
|
source: Some(source.to_owned()),
|
||||||
url: sub.related.location.uri.clone(),
|
message,
|
||||||
fix: sub.suggested_fix.clone(),
|
related_information: {
|
||||||
diagnostic: lsp_types::Diagnostic {
|
let info = related_info_macro_calls
|
||||||
range: sub.related.location.range,
|
.iter()
|
||||||
severity: Some(lsp_types::DiagnosticSeverity::HINT),
|
.cloned()
|
||||||
code: code.clone().map(lsp_types::NumberOrString::String),
|
.chain(subdiagnostics.iter().map(|sub| sub.related.clone()))
|
||||||
code_description: code_description.clone(),
|
.collect::<Vec<_>>();
|
||||||
source: Some(source.clone()),
|
if info.is_empty() { None } else { Some(info) }
|
||||||
message: sub.related.message.clone(),
|
},
|
||||||
related_information: Some(vec![back_ref.clone()]),
|
tags: tag.clone().map(|tag| vec![tag]),
|
||||||
tags: None, // don't apply modifiers again
|
data: Some(serde_json::json!({ "rendered": rendered })),
|
||||||
data: None,
|
},
|
||||||
},
|
fix: None,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
diagnostics
|
// Emit hint-level diagnostics for all `related_information` entries such as "help"s.
|
||||||
})
|
// This is useful because they will show up in the user's editor, unlike
|
||||||
.collect()
|
// `related_information`, which just produces hard-to-read links, at least in VS Code.
|
||||||
|
let back_ref = lsp_types::DiagnosticRelatedInformation {
|
||||||
|
location: primary_location,
|
||||||
|
message: "original diagnostic".to_owned(),
|
||||||
|
};
|
||||||
|
for sub in &subdiagnostics {
|
||||||
|
diagnostics.push(MappedRustDiagnostic {
|
||||||
|
url: sub.related.location.uri.clone(),
|
||||||
|
fix: sub.suggested_fix.clone(),
|
||||||
|
diagnostic: lsp_types::Diagnostic {
|
||||||
|
range: sub.related.location.range,
|
||||||
|
severity: Some(lsp_types::DiagnosticSeverity::HINT),
|
||||||
|
code: code.map(ToOwned::to_owned).map(lsp_types::NumberOrString::String),
|
||||||
|
code_description: code_description.clone(),
|
||||||
|
source: Some(source.to_owned()),
|
||||||
|
message: sub.related.message.clone(),
|
||||||
|
related_information: Some(vec![back_ref.clone()]),
|
||||||
|
tags: None, // don't apply modifiers again
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> {
|
fn rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> {
|
||||||
|
|
@ -545,7 +540,7 @@ mod tests {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let snap = state.snapshot();
|
let snap = state.snapshot();
|
||||||
let mut actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap);
|
let mut actual = map_rust_diagnostic_to_lsp(&config, diagnostic, workspace_root, &snap);
|
||||||
actual.iter_mut().for_each(|diag| diag.diagnostic.data = None);
|
actual.iter_mut().for_each(|diag| diag.diagnostic.data = None);
|
||||||
expect.assert_debug_eq(&actual)
|
expect.assert_debug_eq(&actual)
|
||||||
}
|
}
|
||||||
|
|
@ -191,7 +191,7 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
message: "Error originated from macro call here",
|
message: "Actual error occurred here",
|
||||||
},
|
},
|
||||||
DiagnosticRelatedInformation {
|
DiagnosticRelatedInformation {
|
||||||
location: Location {
|
location: Location {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use crossbeam_channel::{Receiver, Sender, select_biased, unbounded};
|
||||||
use ide_db::FxHashSet;
|
use ide_db::FxHashSet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
|
||||||
|
use project_model::TargetDirectoryConfig;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize as _;
|
use serde::Deserialize as _;
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
@ -46,7 +47,7 @@ pub(crate) struct CargoOptions {
|
||||||
pub(crate) extra_args: Vec<String>,
|
pub(crate) extra_args: Vec<String>,
|
||||||
pub(crate) extra_test_bin_args: Vec<String>,
|
pub(crate) extra_test_bin_args: Vec<String>,
|
||||||
pub(crate) extra_env: FxHashMap<String, Option<String>>,
|
pub(crate) extra_env: FxHashMap<String, Option<String>>,
|
||||||
pub(crate) target_dir: Option<Utf8PathBuf>,
|
pub(crate) target_dir_config: TargetDirectoryConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -58,7 +59,7 @@ pub(crate) enum Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CargoOptions {
|
impl CargoOptions {
|
||||||
pub(crate) fn apply_on_command(&self, cmd: &mut Command) {
|
pub(crate) fn apply_on_command(&self, cmd: &mut Command, ws_target_dir: Option<&Utf8Path>) {
|
||||||
for target in &self.target_tuples {
|
for target in &self.target_tuples {
|
||||||
cmd.args(["--target", target.as_str()]);
|
cmd.args(["--target", target.as_str()]);
|
||||||
}
|
}
|
||||||
|
|
@ -82,8 +83,8 @@ impl CargoOptions {
|
||||||
cmd.arg(self.features.join(" "));
|
cmd.arg(self.features.join(" "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(target_dir) = &self.target_dir {
|
if let Some(target_dir) = self.target_dir_config.target_dir(ws_target_dir) {
|
||||||
cmd.arg("--target-dir").arg(target_dir);
|
cmd.arg("--target-dir").arg(target_dir.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -158,6 +159,7 @@ impl FlycheckHandle {
|
||||||
sysroot_root: Option<AbsPathBuf>,
|
sysroot_root: Option<AbsPathBuf>,
|
||||||
workspace_root: AbsPathBuf,
|
workspace_root: AbsPathBuf,
|
||||||
manifest_path: Option<AbsPathBuf>,
|
manifest_path: Option<AbsPathBuf>,
|
||||||
|
ws_target_dir: Option<Utf8PathBuf>,
|
||||||
) -> FlycheckHandle {
|
) -> FlycheckHandle {
|
||||||
let actor = FlycheckActor::new(
|
let actor = FlycheckActor::new(
|
||||||
id,
|
id,
|
||||||
|
|
@ -167,6 +169,7 @@ impl FlycheckHandle {
|
||||||
sysroot_root,
|
sysroot_root,
|
||||||
workspace_root,
|
workspace_root,
|
||||||
manifest_path,
|
manifest_path,
|
||||||
|
ws_target_dir,
|
||||||
);
|
);
|
||||||
let (sender, receiver) = unbounded::<StateChange>();
|
let (sender, receiver) = unbounded::<StateChange>();
|
||||||
let thread =
|
let thread =
|
||||||
|
|
@ -314,6 +317,7 @@ struct FlycheckActor {
|
||||||
sender: Sender<FlycheckMessage>,
|
sender: Sender<FlycheckMessage>,
|
||||||
config: FlycheckConfig,
|
config: FlycheckConfig,
|
||||||
manifest_path: Option<AbsPathBuf>,
|
manifest_path: Option<AbsPathBuf>,
|
||||||
|
ws_target_dir: Option<Utf8PathBuf>,
|
||||||
/// Either the workspace root of the workspace we are flychecking,
|
/// Either the workspace root of the workspace we are flychecking,
|
||||||
/// or the project root of the project.
|
/// or the project root of the project.
|
||||||
root: Arc<AbsPathBuf>,
|
root: Arc<AbsPathBuf>,
|
||||||
|
|
@ -355,6 +359,7 @@ impl FlycheckActor {
|
||||||
sysroot_root: Option<AbsPathBuf>,
|
sysroot_root: Option<AbsPathBuf>,
|
||||||
workspace_root: AbsPathBuf,
|
workspace_root: AbsPathBuf,
|
||||||
manifest_path: Option<AbsPathBuf>,
|
manifest_path: Option<AbsPathBuf>,
|
||||||
|
ws_target_dir: Option<Utf8PathBuf>,
|
||||||
) -> FlycheckActor {
|
) -> FlycheckActor {
|
||||||
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
|
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
|
||||||
FlycheckActor {
|
FlycheckActor {
|
||||||
|
|
@ -366,6 +371,7 @@ impl FlycheckActor {
|
||||||
root: Arc::new(workspace_root),
|
root: Arc::new(workspace_root),
|
||||||
scope: FlycheckScope::Workspace,
|
scope: FlycheckScope::Workspace,
|
||||||
manifest_path,
|
manifest_path,
|
||||||
|
ws_target_dir,
|
||||||
command_handle: None,
|
command_handle: None,
|
||||||
command_receiver: None,
|
command_receiver: None,
|
||||||
diagnostics_cleared_for: Default::default(),
|
diagnostics_cleared_for: Default::default(),
|
||||||
|
|
@ -428,15 +434,32 @@ impl FlycheckActor {
|
||||||
CargoCheckParser,
|
CargoCheckParser,
|
||||||
sender,
|
sender,
|
||||||
match &self.config {
|
match &self.config {
|
||||||
FlycheckConfig::CargoCommand { options, .. } => Some(
|
FlycheckConfig::CargoCommand { options, .. } => {
|
||||||
options
|
let ws_target_dir =
|
||||||
.target_dir
|
self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path);
|
||||||
.as_deref()
|
let target_dir =
|
||||||
.unwrap_or(
|
options.target_dir_config.target_dir(ws_target_dir);
|
||||||
Utf8Path::new("target").join("rust-analyzer").as_path(),
|
|
||||||
)
|
// If `"rust-analyzer.cargo.targetDir": null`, we should use
|
||||||
.join(format!("flycheck{}", self.id)),
|
// workspace's target dir instead of hard-coded fallback.
|
||||||
),
|
let target_dir = target_dir.as_deref().or(ws_target_dir);
|
||||||
|
|
||||||
|
Some(
|
||||||
|
// As `CommandHandle::spawn`'s working directory is
|
||||||
|
// rust-analyzer's working directory, which might be different
|
||||||
|
// from the flycheck's working directory, we should canonicalize
|
||||||
|
// the output directory, otherwise we might write it into the
|
||||||
|
// wrong target dir.
|
||||||
|
// If `target_dir` is an absolute path, it will replace
|
||||||
|
// `self.root` and that's an intended behavior.
|
||||||
|
self.root
|
||||||
|
.join(target_dir.unwrap_or(
|
||||||
|
Utf8Path::new("target").join("rust-analyzer").as_path(),
|
||||||
|
))
|
||||||
|
.join(format!("flycheck{}", self.id))
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|
@ -672,7 +695,10 @@ impl FlycheckActor {
|
||||||
|
|
||||||
cmd.arg("--keep-going");
|
cmd.arg("--keep-going");
|
||||||
|
|
||||||
options.apply_on_command(&mut cmd);
|
options.apply_on_command(
|
||||||
|
&mut cmd,
|
||||||
|
self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path),
|
||||||
|
);
|
||||||
cmd.args(&options.extra_args);
|
cmd.args(&options.extra_args);
|
||||||
Some(cmd)
|
Some(cmd)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@ use triomphe::Arc;
|
||||||
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
|
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
|
config::{
|
||||||
|
ClientCommandsConfig, Config, HoverActionsConfig, RustfmtConfig, WorkspaceSymbolConfig,
|
||||||
|
},
|
||||||
diagnostics::convert_diagnostic,
|
diagnostics::convert_diagnostic,
|
||||||
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
|
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
|
||||||
line_index::LineEndings,
|
line_index::LineEndings,
|
||||||
|
|
@ -124,17 +126,35 @@ pub(crate) fn handle_analyzer_status(
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> anyhow::Result<String> {
|
pub(crate) fn handle_memory_usage(_state: &mut GlobalState, _: ()) -> anyhow::Result<String> {
|
||||||
let _p = tracing::info_span!("handle_memory_usage").entered();
|
let _p = tracing::info_span!("handle_memory_usage").entered();
|
||||||
let mem = state.analysis_host.per_query_memory_usage();
|
|
||||||
|
|
||||||
let mut out = String::new();
|
#[cfg(not(feature = "dhat"))]
|
||||||
for (name, bytes, entries) in mem {
|
{
|
||||||
format_to!(out, "{:>8} {:>6} {}\n", bytes, entries, name);
|
Err(anyhow::anyhow!(
|
||||||
|
"Memory profiling is not enabled for this build of rust-analyzer.\n\n\
|
||||||
|
To build rust-analyzer with profiling support, pass `--features dhat --profile dev-rel` to `cargo build`
|
||||||
|
when building from source, or pass `--enable-profiling` to `cargo xtask`."
|
||||||
|
))
|
||||||
|
}
|
||||||
|
#[cfg(feature = "dhat")]
|
||||||
|
{
|
||||||
|
if let Some(dhat_output_file) = _state.config.dhat_output_file() {
|
||||||
|
let mut profiler = crate::DHAT_PROFILER.lock().unwrap();
|
||||||
|
let old_profiler = profiler.take();
|
||||||
|
// Need to drop the old profiler before creating a new one.
|
||||||
|
drop(old_profiler);
|
||||||
|
*profiler = Some(dhat::Profiler::builder().file_name(&dhat_output_file).build());
|
||||||
|
Ok(format!(
|
||||||
|
"Memory profile was saved successfully to {dhat_output_file}.\n\n\
|
||||||
|
See https://docs.rs/dhat/latest/dhat/#viewing for how to inspect the profile."
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!(
|
||||||
|
"Please set `rust-analyzer.profiling.memoryProfile` to the path where you want to save the profile."
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
format_to!(out, "{:>8} Remaining\n", profile::memory_usage().allocated);
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_view_syntax_tree(
|
pub(crate) fn handle_view_syntax_tree(
|
||||||
|
|
@ -264,6 +284,7 @@ pub(crate) fn handle_run_test(
|
||||||
path,
|
path,
|
||||||
state.config.cargo_test_options(None),
|
state.config.cargo_test_options(None),
|
||||||
cargo.workspace_root(),
|
cargo.workspace_root(),
|
||||||
|
Some(cargo.target_directory().as_ref()),
|
||||||
target,
|
target,
|
||||||
state.test_run_sender.clone(),
|
state.test_run_sender.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -847,10 +868,11 @@ pub(crate) fn handle_goto_implementation(
|
||||||
let _p = tracing::info_span!("handle_goto_implementation").entered();
|
let _p = tracing::info_span!("handle_goto_implementation").entered();
|
||||||
let position =
|
let position =
|
||||||
try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
|
try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
|
||||||
let nav_info = match snap.analysis.goto_implementation(position)? {
|
let nav_info =
|
||||||
None => return Ok(None),
|
match snap.analysis.goto_implementation(&snap.config.goto_implementation(), position)? {
|
||||||
Some(it) => it,
|
None => return Ok(None),
|
||||||
};
|
Some(it) => it,
|
||||||
|
};
|
||||||
let src = FileRange { file_id: position.file_id, range: nav_info.range };
|
let src = FileRange { file_id: position.file_id, range: nav_info.range };
|
||||||
let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
|
let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
|
|
@ -1325,8 +1347,12 @@ pub(crate) fn handle_rename(
|
||||||
let _p = tracing::info_span!("handle_rename").entered();
|
let _p = tracing::info_span!("handle_rename").entered();
|
||||||
let position = try_default!(from_proto::file_position(&snap, params.text_document_position)?);
|
let position = try_default!(from_proto::file_position(&snap, params.text_document_position)?);
|
||||||
|
|
||||||
let mut change =
|
let source_root = snap.analysis.source_root_id(position.file_id).ok();
|
||||||
snap.analysis.rename(position, ¶ms.new_name)?.map_err(to_proto::rename_error)?;
|
let config = snap.config.rename(source_root);
|
||||||
|
let mut change = snap
|
||||||
|
.analysis
|
||||||
|
.rename(position, ¶ms.new_name, &config)?
|
||||||
|
.map_err(to_proto::rename_error)?;
|
||||||
|
|
||||||
// this is kind of a hack to prevent double edits from happening when moving files
|
// this is kind of a hack to prevent double edits from happening when moving files
|
||||||
// When a module gets renamed by renaming the mod declaration this causes the file to move
|
// When a module gets renamed by renaming the mod declaration this causes the file to move
|
||||||
|
|
@ -1459,13 +1485,14 @@ pub(crate) fn handle_code_action(
|
||||||
resolve,
|
resolve,
|
||||||
frange,
|
frange,
|
||||||
)?;
|
)?;
|
||||||
|
let client_commands = snap.config.client_commands();
|
||||||
for (index, assist) in assists.into_iter().enumerate() {
|
for (index, assist) in assists.into_iter().enumerate() {
|
||||||
let resolve_data = if code_action_resolve_cap {
|
let resolve_data = if code_action_resolve_cap {
|
||||||
Some((index, params.clone(), snap.file_version(file_id)))
|
Some((index, params.clone(), snap.file_version(file_id)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let code_action = to_proto::code_action(&snap, assist, resolve_data)?;
|
let code_action = to_proto::code_action(&snap, &client_commands, assist, resolve_data)?;
|
||||||
|
|
||||||
// Check if the client supports the necessary `ResourceOperation`s.
|
// Check if the client supports the necessary `ResourceOperation`s.
|
||||||
let changes = code_action.edit.as_ref().and_then(|it| it.document_changes.as_ref());
|
let changes = code_action.edit.as_ref().and_then(|it| it.document_changes.as_ref());
|
||||||
|
|
@ -1566,7 +1593,7 @@ pub(crate) fn handle_code_action_resolve(
|
||||||
))
|
))
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
let ca = to_proto::code_action(&snap, assist.clone(), None)?;
|
let ca = to_proto::code_action(&snap, &snap.config.client_commands(), assist.clone(), None)?;
|
||||||
code_action.edit = ca.edit;
|
code_action.edit = ca.edit;
|
||||||
code_action.command = ca.command;
|
code_action.command = ca.command;
|
||||||
|
|
||||||
|
|
@ -2130,10 +2157,15 @@ fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::Com
|
||||||
fn show_impl_command_link(
|
fn show_impl_command_link(
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
position: &FilePosition,
|
position: &FilePosition,
|
||||||
|
implementations: bool,
|
||||||
|
show_references: bool,
|
||||||
) -> Option<lsp_ext::CommandLinkGroup> {
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
||||||
if snap.config.hover_actions().implementations
|
if implementations
|
||||||
&& snap.config.client_commands().show_reference
|
&& show_references
|
||||||
&& let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None)
|
&& let Some(nav_data) = snap
|
||||||
|
.analysis
|
||||||
|
.goto_implementation(&snap.config.goto_implementation(), *position)
|
||||||
|
.unwrap_or(None)
|
||||||
{
|
{
|
||||||
let uri = to_proto::url(snap, position.file_id);
|
let uri = to_proto::url(snap, position.file_id);
|
||||||
let line_index = snap.file_line_index(position.file_id).ok()?;
|
let line_index = snap.file_line_index(position.file_id).ok()?;
|
||||||
|
|
@ -2157,9 +2189,11 @@ fn show_impl_command_link(
|
||||||
fn show_ref_command_link(
|
fn show_ref_command_link(
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
position: &FilePosition,
|
position: &FilePosition,
|
||||||
|
references: bool,
|
||||||
|
show_reference: bool,
|
||||||
) -> Option<lsp_ext::CommandLinkGroup> {
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
||||||
if snap.config.hover_actions().references
|
if references
|
||||||
&& snap.config.client_commands().show_reference
|
&& show_reference
|
||||||
&& let Some(ref_search_res) = snap
|
&& let Some(ref_search_res) = snap
|
||||||
.analysis
|
.analysis
|
||||||
.find_all_refs(
|
.find_all_refs(
|
||||||
|
|
@ -2194,8 +2228,9 @@ fn show_ref_command_link(
|
||||||
fn runnable_action_links(
|
fn runnable_action_links(
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
runnable: Runnable,
|
runnable: Runnable,
|
||||||
|
hover_actions_config: &HoverActionsConfig,
|
||||||
|
client_commands_config: &ClientCommandsConfig,
|
||||||
) -> Option<lsp_ext::CommandLinkGroup> {
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
||||||
let hover_actions_config = snap.config.hover_actions();
|
|
||||||
if !hover_actions_config.runnable() {
|
if !hover_actions_config.runnable() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -2205,7 +2240,6 @@ fn runnable_action_links(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let client_commands_config = snap.config.client_commands();
|
|
||||||
if !(client_commands_config.run_single || client_commands_config.debug_single) {
|
if !(client_commands_config.run_single || client_commands_config.debug_single) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -2240,11 +2274,10 @@ fn runnable_action_links(
|
||||||
fn goto_type_action_links(
|
fn goto_type_action_links(
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
nav_targets: &[HoverGotoTypeData],
|
nav_targets: &[HoverGotoTypeData],
|
||||||
|
hover_actions: &HoverActionsConfig,
|
||||||
|
client_commands: &ClientCommandsConfig,
|
||||||
) -> Option<lsp_ext::CommandLinkGroup> {
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
||||||
if !snap.config.hover_actions().goto_type_def
|
if !hover_actions.goto_type_def || nav_targets.is_empty() || !client_commands.goto_location {
|
||||||
|| nav_targets.is_empty()
|
|
||||||
|| !snap.config.client_commands().goto_location
|
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2264,13 +2297,29 @@ fn prepare_hover_actions(
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
actions: &[HoverAction],
|
actions: &[HoverAction],
|
||||||
) -> Vec<lsp_ext::CommandLinkGroup> {
|
) -> Vec<lsp_ext::CommandLinkGroup> {
|
||||||
|
let hover_actions = snap.config.hover_actions();
|
||||||
|
let client_commands = snap.config.client_commands();
|
||||||
actions
|
actions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|it| match it {
|
.filter_map(|it| match it {
|
||||||
HoverAction::Implementation(position) => show_impl_command_link(snap, position),
|
HoverAction::Implementation(position) => show_impl_command_link(
|
||||||
HoverAction::Reference(position) => show_ref_command_link(snap, position),
|
snap,
|
||||||
HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()),
|
position,
|
||||||
HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
|
hover_actions.implementations,
|
||||||
|
client_commands.show_reference,
|
||||||
|
),
|
||||||
|
HoverAction::Reference(position) => show_ref_command_link(
|
||||||
|
snap,
|
||||||
|
position,
|
||||||
|
hover_actions.references,
|
||||||
|
client_commands.show_reference,
|
||||||
|
),
|
||||||
|
HoverAction::Runnable(r) => {
|
||||||
|
runnable_action_links(snap, r.clone(), &hover_actions, &client_commands)
|
||||||
|
}
|
||||||
|
HoverAction::GoToType(targets) => {
|
||||||
|
goto_type_action_links(snap, targets, &hover_actions, &client_commands)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,3 +82,10 @@ macro_rules! try_default_ {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub(crate) use try_default_ as try_default;
|
pub(crate) use try_default_ as try_default;
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat")]
|
||||||
|
static DHAT_PROFILER: std::sync::Mutex<Option<dhat::Profiler>> = std::sync::Mutex::new(None);
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use serde_json::to_value;
|
||||||
use vfs::AbsPath;
|
use vfs::AbsPath;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{CallInfoConfig, Config},
|
config::{CallInfoConfig, ClientCommandsConfig, Config},
|
||||||
global_state::GlobalStateSnapshot,
|
global_state::GlobalStateSnapshot,
|
||||||
line_index::{LineEndings, LineIndex, PositionEncoding},
|
line_index::{LineEndings, LineIndex, PositionEncoding},
|
||||||
lsp::{
|
lsp::{
|
||||||
|
|
@ -258,10 +258,12 @@ pub(crate) fn completion_items(
|
||||||
|
|
||||||
let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default();
|
let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default();
|
||||||
let mut res = Vec::with_capacity(items.len());
|
let mut res = Vec::with_capacity(items.len());
|
||||||
|
let client_commands = config.client_commands();
|
||||||
for item in items {
|
for item in items {
|
||||||
completion_item(
|
completion_item(
|
||||||
&mut res,
|
&mut res,
|
||||||
config,
|
config,
|
||||||
|
&client_commands,
|
||||||
fields_to_resolve,
|
fields_to_resolve,
|
||||||
line_index,
|
line_index,
|
||||||
version,
|
version,
|
||||||
|
|
@ -283,6 +285,7 @@ pub(crate) fn completion_items(
|
||||||
fn completion_item(
|
fn completion_item(
|
||||||
acc: &mut Vec<lsp_types::CompletionItem>,
|
acc: &mut Vec<lsp_types::CompletionItem>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
|
client_commands: &ClientCommandsConfig,
|
||||||
fields_to_resolve: &CompletionFieldsToResolve,
|
fields_to_resolve: &CompletionFieldsToResolve,
|
||||||
line_index: &LineIndex,
|
line_index: &LineIndex,
|
||||||
version: Option<i32>,
|
version: Option<i32>,
|
||||||
|
|
@ -342,7 +345,7 @@ fn completion_item(
|
||||||
} else {
|
} else {
|
||||||
item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED])
|
item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED])
|
||||||
};
|
};
|
||||||
let command = if item.trigger_call_info && config.client_commands().trigger_parameter_hints {
|
let command = if item.trigger_call_info && client_commands.trigger_parameter_hints {
|
||||||
if fields_to_resolve.resolve_command {
|
if fields_to_resolve.resolve_command {
|
||||||
something_to_resolve |= true;
|
something_to_resolve |= true;
|
||||||
None
|
None
|
||||||
|
|
@ -1500,6 +1503,7 @@ pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind {
|
||||||
|
|
||||||
pub(crate) fn code_action(
|
pub(crate) fn code_action(
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
|
commands: &ClientCommandsConfig,
|
||||||
assist: Assist,
|
assist: Assist,
|
||||||
resolve_data: Option<(usize, lsp_types::CodeActionParams, Option<i32>)>,
|
resolve_data: Option<(usize, lsp_types::CodeActionParams, Option<i32>)>,
|
||||||
) -> Cancellable<lsp_ext::CodeAction> {
|
) -> Cancellable<lsp_ext::CodeAction> {
|
||||||
|
|
@ -1513,7 +1517,6 @@ pub(crate) fn code_action(
|
||||||
command: None,
|
command: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let commands = snap.config.client_commands();
|
|
||||||
res.command = match assist.command {
|
res.command = match assist.command {
|
||||||
Some(assists::Command::TriggerParameterHints) if commands.trigger_parameter_hints => {
|
Some(assists::Command::TriggerParameterHints) if commands.trigger_parameter_hints => {
|
||||||
Some(command::trigger_parameter_hints())
|
Some(command::trigger_parameter_hints())
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,14 @@ pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> {
|
||||||
SetThreadPriority(thread, thread_priority_above_normal);
|
SetThreadPriority(thread, thread_priority_above_normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat")]
|
||||||
|
{
|
||||||
|
if let Some(dhat_output_file) = config.dhat_output_file() {
|
||||||
|
*crate::DHAT_PROFILER.lock().unwrap() =
|
||||||
|
Some(dhat::Profiler::builder().file_name(&dhat_output_file).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GlobalState::new(connection.sender, config).run(connection.receiver)
|
GlobalState::new(connection.sender, config).run(connection.receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1023,9 +1031,9 @@ impl GlobalState {
|
||||||
package_id,
|
package_id,
|
||||||
} => {
|
} => {
|
||||||
let snap = self.snapshot();
|
let snap = self.snapshot();
|
||||||
let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
|
let diagnostics = crate::diagnostics::flycheck_to_proto::map_rust_diagnostic_to_lsp(
|
||||||
&self.config.diagnostics_map(None),
|
&self.config.diagnostics_map(None),
|
||||||
&diagnostic,
|
diagnostic,
|
||||||
&workspace_root,
|
&workspace_root,
|
||||||
&snap,
|
&snap,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use ide_db::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use load_cargo::{ProjectFolders, load_proc_macro};
|
use load_cargo::{ProjectFolders, load_proc_macro};
|
||||||
use lsp_types::FileSystemWatcher;
|
use lsp_types::FileSystemWatcher;
|
||||||
|
use paths::Utf8Path;
|
||||||
use proc_macro_api::ProcMacroClient;
|
use proc_macro_api::ProcMacroClient;
|
||||||
use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
|
use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
|
||||||
use stdx::{format_to, thread::ThreadIntent};
|
use stdx::{format_to, thread::ThreadIntent};
|
||||||
|
|
@ -876,6 +877,7 @@ impl GlobalState {
|
||||||
None,
|
None,
|
||||||
self.config.root_path().clone(),
|
self.config.root_path().clone(),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
crate::flycheck::InvocationStrategy::PerWorkspace => {
|
crate::flycheck::InvocationStrategy::PerWorkspace => {
|
||||||
|
|
@ -890,13 +892,17 @@ impl GlobalState {
|
||||||
| ProjectWorkspaceKind::DetachedFile {
|
| ProjectWorkspaceKind::DetachedFile {
|
||||||
cargo: Some((cargo, _, _)),
|
cargo: Some((cargo, _, _)),
|
||||||
..
|
..
|
||||||
} => (cargo.workspace_root(), Some(cargo.manifest_path())),
|
} => (
|
||||||
|
cargo.workspace_root(),
|
||||||
|
Some(cargo.manifest_path()),
|
||||||
|
Some(cargo.target_directory()),
|
||||||
|
),
|
||||||
ProjectWorkspaceKind::Json(project) => {
|
ProjectWorkspaceKind::Json(project) => {
|
||||||
// Enable flychecks for json projects if a custom flycheck command was supplied
|
// Enable flychecks for json projects if a custom flycheck command was supplied
|
||||||
// in the workspace configuration.
|
// in the workspace configuration.
|
||||||
match config {
|
match config {
|
||||||
FlycheckConfig::CustomCommand { .. } => {
|
FlycheckConfig::CustomCommand { .. } => {
|
||||||
(project.path(), None)
|
(project.path(), None, None)
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
|
|
@ -906,7 +912,7 @@ impl GlobalState {
|
||||||
ws.sysroot.root().map(ToOwned::to_owned),
|
ws.sysroot.root().map(ToOwned::to_owned),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.map(|(id, (root, manifest_path), sysroot_root)| {
|
.map(|(id, (root, manifest_path, target_dir), sysroot_root)| {
|
||||||
FlycheckHandle::spawn(
|
FlycheckHandle::spawn(
|
||||||
id,
|
id,
|
||||||
next_gen,
|
next_gen,
|
||||||
|
|
@ -915,6 +921,7 @@ impl GlobalState {
|
||||||
sysroot_root,
|
sysroot_root,
|
||||||
root.to_path_buf(),
|
root.to_path_buf(),
|
||||||
manifest_path.map(|it| it.to_path_buf()),
|
manifest_path.map(|it| it.to_path_buf()),
|
||||||
|
target_dir.map(|it| AsRef::<Utf8Path>::as_ref(it).to_path_buf()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
//! thread and report the result of each test in a channel.
|
//! thread and report the result of each test in a channel.
|
||||||
|
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use paths::AbsPath;
|
use paths::{AbsPath, Utf8Path};
|
||||||
use project_model::TargetKind;
|
use project_model::TargetKind;
|
||||||
use serde::Deserialize as _;
|
use serde::Deserialize as _;
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
@ -98,6 +98,7 @@ impl CargoTestHandle {
|
||||||
path: Option<&str>,
|
path: Option<&str>,
|
||||||
options: CargoOptions,
|
options: CargoOptions,
|
||||||
root: &AbsPath,
|
root: &AbsPath,
|
||||||
|
ws_target_dir: Option<&Utf8Path>,
|
||||||
test_target: TestTarget,
|
test_target: TestTarget,
|
||||||
sender: Sender<CargoTestMessage>,
|
sender: Sender<CargoTestMessage>,
|
||||||
) -> std::io::Result<Self> {
|
) -> std::io::Result<Self> {
|
||||||
|
|
@ -123,7 +124,7 @@ impl CargoTestHandle {
|
||||||
cmd.arg("--no-fail-fast");
|
cmd.arg("--no-fail-fast");
|
||||||
cmd.arg("--manifest-path");
|
cmd.arg("--manifest-path");
|
||||||
cmd.arg(root.join("Cargo.toml"));
|
cmd.arg(root.join("Cargo.toml"));
|
||||||
options.apply_on_command(&mut cmd);
|
options.apply_on_command(&mut cmd, ws_target_dir);
|
||||||
cmd.arg("--");
|
cmd.arg("--");
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
cmd.arg(path);
|
cmd.arg(path);
|
||||||
|
|
|
||||||
|
|
@ -1355,7 +1355,7 @@ pub mod tokens {
|
||||||
|
|
||||||
pub(super) static SOURCE_FILE: LazyLock<Parse<SourceFile>> = LazyLock::new(|| {
|
pub(super) static SOURCE_FILE: LazyLock<Parse<SourceFile>> = LazyLock::new(|| {
|
||||||
SourceFile::parse(
|
SourceFile::parse(
|
||||||
"use crate::foo; const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] })\n;\n\nunsafe impl A for B where: {}",
|
"use crate::foo; const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] }, while loop {} {})\n;\n\nunsafe impl A for B where: {}",
|
||||||
Edition::CURRENT,
|
Edition::CURRENT,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -644,6 +644,20 @@ impl SyntaxFactory {
|
||||||
ast
|
ast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr_loop(&self, body: ast::BlockExpr) -> ast::LoopExpr {
|
||||||
|
let ast::Expr::LoopExpr(ast) = make::expr_loop(body.clone()).clone_for_update() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(body.syntax().clone(), ast.loop_body().unwrap().syntax().clone());
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expr_while_loop(&self, condition: ast::Expr, body: ast::BlockExpr) -> ast::WhileExpr {
|
pub fn expr_while_loop(&self, condition: ast::Expr, body: ast::BlockExpr) -> ast::WhileExpr {
|
||||||
let ast = make::expr_while_loop(condition.clone(), body.clone()).clone_for_update();
|
let ast = make::expr_while_loop(condition.clone(), body.clone()).clone_for_update();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -622,6 +622,15 @@ where
|
||||||
let lit = &lit[start_offset..lit.len() - end_offset];
|
let lit = &lit[start_offset..lit.len() - end_offset];
|
||||||
let suffix = match suffix {
|
let suffix = match suffix {
|
||||||
"" | "_" => None,
|
"" | "_" => None,
|
||||||
|
// ill-suffixed literals
|
||||||
|
_ if !matches!(kind, LitKind::Integer | LitKind::Float | LitKind::Err(_)) => {
|
||||||
|
return Literal {
|
||||||
|
span,
|
||||||
|
symbol: Symbol::intern(text),
|
||||||
|
kind: LitKind::Err(()),
|
||||||
|
suffix: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
suffix => Some(Symbol::intern(suffix)),
|
suffix => Some(Symbol::intern(suffix)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,13 @@ Default: `"client"`
|
||||||
Controls file watching implementation.
|
Controls file watching implementation.
|
||||||
|
|
||||||
|
|
||||||
|
## rust-analyzer.gotoImplementations.filterAdjacentDerives {#gotoImplementations.filterAdjacentDerives}
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
If this is `true`, when "Goto Implementations" and in "Implementations" lens, are triggered on a `struct` or `enum` or `union`, we filter out trait implementations that originate from `derive`s above the type.
|
||||||
|
|
||||||
|
|
||||||
## rust-analyzer.highlightRelated.branchExitPoints.enable {#highlightRelated.branchExitPoints.enable}
|
## rust-analyzer.highlightRelated.branchExitPoints.enable {#highlightRelated.branchExitPoints.enable}
|
||||||
|
|
||||||
Default: `true`
|
Default: `true`
|
||||||
|
|
@ -1289,6 +1296,16 @@ Default: `null`
|
||||||
Internal config, path to proc-macro server executable.
|
Internal config, path to proc-macro server executable.
|
||||||
|
|
||||||
|
|
||||||
|
## rust-analyzer.profiling.memoryProfile {#profiling.memoryProfile}
|
||||||
|
|
||||||
|
Default: `null`
|
||||||
|
|
||||||
|
The path where to save memory profiling output.
|
||||||
|
|
||||||
|
**Note:** Memory profiling is not enabled by default in rust-analyzer builds, you need to build
|
||||||
|
from source for it.
|
||||||
|
|
||||||
|
|
||||||
## rust-analyzer.references.excludeImports {#references.excludeImports}
|
## rust-analyzer.references.excludeImports {#references.excludeImports}
|
||||||
|
|
||||||
Default: `false`
|
Default: `false`
|
||||||
|
|
|
||||||
|
|
@ -1627,6 +1627,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Goto Implementations",
|
||||||
|
"properties": {
|
||||||
|
"rust-analyzer.gotoImplementations.filterAdjacentDerives": {
|
||||||
|
"markdownDescription": "If this is `true`, when \"Goto Implementations\" and in \"Implementations\" lens, are triggered on a `struct` or `enum` or `union`, we filter out trait implementations that originate from `derive`s above the type.",
|
||||||
|
"default": false,
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Highlight Related",
|
"title": "Highlight Related",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2749,6 +2759,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Profiling",
|
||||||
|
"properties": {
|
||||||
|
"rust-analyzer.profiling.memoryProfile": {
|
||||||
|
"markdownDescription": "The path where to save memory profiling output.\n\n**Note:** Memory profiling is not enabled by default in rust-analyzer builds, you need to build\nfrom source for it.",
|
||||||
|
"default": null,
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "References",
|
"title": "References",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
|
|
@ -71,32 +71,9 @@ export function analyzerStatus(ctx: CtxInit): Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function memoryUsage(ctx: CtxInit): Cmd {
|
export function memoryUsage(ctx: CtxInit): Cmd {
|
||||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
|
||||||
readonly uri = vscode.Uri.parse("rust-analyzer-memory://memory");
|
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
|
||||||
|
|
||||||
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
|
|
||||||
if (!vscode.window.activeTextEditor) return "";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
|
|
||||||
return "Per-query memory usage:\n" + mem + "\n(note: database has been cleared)";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
|
||||||
return this.eventEmitter.event;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
ctx.pushExtCleanup(
|
|
||||||
vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-memory", tdcp),
|
|
||||||
);
|
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
tdcp.eventEmitter.fire(tdcp.uri);
|
const response = await ctx.client.sendRequest(ra.memoryUsage);
|
||||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
vscode.window.showInformationMessage(response);
|
||||||
return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ export async function applySnippetWorkspaceEdit(
|
||||||
for (const indel of edits) {
|
for (const indel of edits) {
|
||||||
assert(
|
assert(
|
||||||
!(indel instanceof vscode.SnippetTextEdit),
|
!(indel instanceof vscode.SnippetTextEdit),
|
||||||
`bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`,
|
`bad ws edit: snippet received with multiple edits: ${JSON.stringify(
|
||||||
|
edit,
|
||||||
|
)}`,
|
||||||
);
|
);
|
||||||
builder.replace(indel.range, indel.newText);
|
builder.replace(indel.range, indel.newText);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
fb24b04b096a980bffd80154f6aba22fd07cb3d9
|
c5dabe8cf798123087d094f06417f5a767ca73e8
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,22 @@ impl flags::Dist {
|
||||||
allocator,
|
allocator,
|
||||||
self.zig,
|
self.zig,
|
||||||
self.pgo,
|
self.pgo,
|
||||||
|
// Profiling requires debug information.
|
||||||
|
self.enable_profiling,
|
||||||
)?;
|
)?;
|
||||||
let release_tag = if stable { date_iso(sh)? } else { "nightly".to_owned() };
|
let release_tag = if stable { date_iso(sh)? } else { "nightly".to_owned() };
|
||||||
dist_client(sh, &version, &release_tag, &target)?;
|
dist_client(sh, &version, &release_tag, &target)?;
|
||||||
} else {
|
} else {
|
||||||
dist_server(sh, "0.0.0-standalone", &target, allocator, self.zig, self.pgo)?;
|
dist_server(
|
||||||
|
sh,
|
||||||
|
"0.0.0-standalone",
|
||||||
|
&target,
|
||||||
|
allocator,
|
||||||
|
self.zig,
|
||||||
|
self.pgo,
|
||||||
|
// Profiling requires debug information.
|
||||||
|
self.enable_profiling,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -92,9 +103,11 @@ fn dist_server(
|
||||||
allocator: Malloc,
|
allocator: Malloc,
|
||||||
zig: bool,
|
zig: bool,
|
||||||
pgo: Option<PgoTrainingCrate>,
|
pgo: Option<PgoTrainingCrate>,
|
||||||
|
dev_rel: bool,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let _e = sh.push_env("CFG_RELEASE", release);
|
let _e = sh.push_env("CFG_RELEASE", release);
|
||||||
let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin");
|
let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin");
|
||||||
|
let _e = sh.push_env("CARGO_PROFILE_DEV_REL_LTO", "thin");
|
||||||
|
|
||||||
// Uncomment to enable debug info for releases. Note that:
|
// Uncomment to enable debug info for releases. Note that:
|
||||||
// * debug info is split on windows and macs, so it does nothing for those platforms,
|
// * debug info is split on windows and macs, so it does nothing for those platforms,
|
||||||
|
|
@ -120,7 +133,7 @@ fn dist_server(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cmd = build_command(sh, command, &target_name, features);
|
let mut cmd = build_command(sh, command, &target_name, features, dev_rel);
|
||||||
if let Some(profile) = pgo_profile {
|
if let Some(profile) = pgo_profile {
|
||||||
cmd = cmd.env("RUSTFLAGS", format!("-Cprofile-use={}", profile.to_str().unwrap()));
|
cmd = cmd.env("RUSTFLAGS", format!("-Cprofile-use={}", profile.to_str().unwrap()));
|
||||||
}
|
}
|
||||||
|
|
@ -141,10 +154,12 @@ fn build_command<'a>(
|
||||||
command: &str,
|
command: &str,
|
||||||
target_name: &str,
|
target_name: &str,
|
||||||
features: &[&str],
|
features: &[&str],
|
||||||
|
dev_rel: bool,
|
||||||
) -> Cmd<'a> {
|
) -> Cmd<'a> {
|
||||||
|
let profile = if dev_rel { "dev-rel" } else { "release" };
|
||||||
cmd!(
|
cmd!(
|
||||||
sh,
|
sh,
|
||||||
"cargo {command} --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --release"
|
"cargo {command} --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --profile {profile}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ xflags::xflags! {
|
||||||
optional --mimalloc
|
optional --mimalloc
|
||||||
/// Use jemalloc allocator for server.
|
/// Use jemalloc allocator for server.
|
||||||
optional --jemalloc
|
optional --jemalloc
|
||||||
|
// Enable memory profiling support.
|
||||||
|
//
|
||||||
|
// **Warning:** This will produce a slower build of rust-analyzer, use only for profiling.
|
||||||
|
optional --enable-profiling
|
||||||
|
|
||||||
/// Install the proc-macro server.
|
/// Install the proc-macro server.
|
||||||
optional --proc-macro-server
|
optional --proc-macro-server
|
||||||
|
|
@ -67,6 +71,10 @@ xflags::xflags! {
|
||||||
optional --mimalloc
|
optional --mimalloc
|
||||||
/// Use jemalloc allocator for server
|
/// Use jemalloc allocator for server
|
||||||
optional --jemalloc
|
optional --jemalloc
|
||||||
|
// Enable memory profiling support.
|
||||||
|
//
|
||||||
|
// **Warning:** This will produce a slower build of rust-analyzer, use only for profiling.
|
||||||
|
optional --enable-profiling
|
||||||
optional --client-patch-version version: String
|
optional --client-patch-version version: String
|
||||||
/// Use cargo-zigbuild
|
/// Use cargo-zigbuild
|
||||||
optional --zig
|
optional --zig
|
||||||
|
|
@ -125,6 +133,7 @@ pub struct Install {
|
||||||
pub server: bool,
|
pub server: bool,
|
||||||
pub mimalloc: bool,
|
pub mimalloc: bool,
|
||||||
pub jemalloc: bool,
|
pub jemalloc: bool,
|
||||||
|
pub enable_profiling: bool,
|
||||||
pub proc_macro_server: bool,
|
pub proc_macro_server: bool,
|
||||||
pub dev_rel: bool,
|
pub dev_rel: bool,
|
||||||
pub force_always_assert: bool,
|
pub force_always_assert: bool,
|
||||||
|
|
@ -143,6 +152,7 @@ pub struct Release {
|
||||||
pub struct Dist {
|
pub struct Dist {
|
||||||
pub mimalloc: bool,
|
pub mimalloc: bool,
|
||||||
pub jemalloc: bool,
|
pub jemalloc: bool,
|
||||||
|
pub enable_profiling: bool,
|
||||||
pub client_patch_version: Option<String>,
|
pub client_patch_version: Option<String>,
|
||||||
pub zig: bool,
|
pub zig: bool,
|
||||||
pub pgo: Option<PgoTrainingCrate>,
|
pub pgo: Option<PgoTrainingCrate>,
|
||||||
|
|
@ -280,6 +290,7 @@ pub(crate) enum Malloc {
|
||||||
System,
|
System,
|
||||||
Mimalloc,
|
Mimalloc,
|
||||||
Jemalloc,
|
Jemalloc,
|
||||||
|
Dhat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Malloc {
|
impl Malloc {
|
||||||
|
|
@ -288,6 +299,7 @@ impl Malloc {
|
||||||
Malloc::System => &[][..],
|
Malloc::System => &[][..],
|
||||||
Malloc::Mimalloc => &["--features", "mimalloc"],
|
Malloc::Mimalloc => &["--features", "mimalloc"],
|
||||||
Malloc::Jemalloc => &["--features", "jemalloc"],
|
Malloc::Jemalloc => &["--features", "jemalloc"],
|
||||||
|
Malloc::Dhat => &["--features", "dhat"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -301,12 +313,15 @@ impl Install {
|
||||||
Malloc::Mimalloc
|
Malloc::Mimalloc
|
||||||
} else if self.jemalloc {
|
} else if self.jemalloc {
|
||||||
Malloc::Jemalloc
|
Malloc::Jemalloc
|
||||||
|
} else if self.enable_profiling {
|
||||||
|
Malloc::Dhat
|
||||||
} else {
|
} else {
|
||||||
Malloc::System
|
Malloc::System
|
||||||
};
|
};
|
||||||
Some(ServerOpt {
|
Some(ServerOpt {
|
||||||
malloc,
|
malloc,
|
||||||
dev_rel: self.dev_rel,
|
// Profiling requires debug information.
|
||||||
|
dev_rel: self.dev_rel || self.enable_profiling,
|
||||||
pgo: self.pgo.clone(),
|
pgo: self.pgo.clone(),
|
||||||
force_always_assert: self.force_always_assert,
|
force_always_assert: self.force_always_assert,
|
||||||
})
|
})
|
||||||
|
|
@ -331,6 +346,8 @@ impl Dist {
|
||||||
Malloc::Mimalloc
|
Malloc::Mimalloc
|
||||||
} else if self.jemalloc {
|
} else if self.jemalloc {
|
||||||
Malloc::Jemalloc
|
Malloc::Jemalloc
|
||||||
|
} else if self.enable_profiling {
|
||||||
|
Malloc::Dhat
|
||||||
} else {
|
} else {
|
||||||
Malloc::System
|
Malloc::System
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue