Auto merge of #136117 - lnicola:sync-from-ra, r=lnicola

Subtree update of `rust-analyzer`

r? `@ghost`
This commit is contained in:
bors 2025-01-29 08:43:30 +00:00
commit 702da50daa
126 changed files with 2163 additions and 1325 deletions

7
Cargo.lock generated
View file

@ -846,7 +846,6 @@ dependencies = [
"dashmap",
"hashbrown",
"rustc-hash 2.0.0",
"sptr",
"triomphe",
]
@ -1927,12 +1926,6 @@ dependencies = [
"vfs",
]
[[package]]
name = "sptr"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
[[package]]
name = "stdx"
version = "0.0.0"

View file

@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
resolver = "2"
[workspace.package]
rust-version = "1.83"
rust-version = "1.84"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer team"]

View file

@ -1381,6 +1381,9 @@ impl ExprCollector<'_> {
}
}
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
if self.check_cfg(&macro_).is_none() {
return;
}
let Some(name) = macro_.name() else {
statements.push(Statement::Item(Item::Other));
return;
@ -1390,6 +1393,9 @@ impl ExprCollector<'_> {
self.collect_macro_def(statements, macro_id);
}
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
if self.check_cfg(&macro_).is_none() {
return;
}
let Some(name) = macro_.name() else {
statements.push(Statement::Item(Item::Other));
return;

View file

@ -475,7 +475,7 @@ fn outer() {
block scope::tests
name: _
outer: v
outer: vg
crate
outer: v

View file

@ -445,6 +445,10 @@ fn find_in_dep(
};
cov_mark::hit!(partially_imported);
if info.is_unstable {
if !ctx.cfg.allow_unstable {
// the item is unstable and we are not allowed to use unstable items
continue;
}
choice.stability = Unstable;
}
@ -670,6 +674,7 @@ mod tests {
prefer_prelude: bool,
prefer_absolute: bool,
prefer_no_std: bool,
allow_unstable: bool,
expect: Expect,
) {
let (db, pos) = TestDB::with_position(ra_fixture);
@ -711,7 +716,7 @@ mod tests {
module,
prefix,
ignore_local_imports,
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute },
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable },
);
format_to!(
res,
@ -732,7 +737,7 @@ mod tests {
path: &str,
expect: Expect,
) {
check_found_path_(ra_fixture, path, false, false, false, expect);
check_found_path_(ra_fixture, path, false, false, false, false, expect);
}
fn check_found_path_prelude(
@ -740,7 +745,7 @@ mod tests {
path: &str,
expect: Expect,
) {
check_found_path_(ra_fixture, path, true, false, false, expect);
check_found_path_(ra_fixture, path, true, false, false, false, expect);
}
fn check_found_path_absolute(
@ -748,7 +753,7 @@ mod tests {
path: &str,
expect: Expect,
) {
check_found_path_(ra_fixture, path, false, true, false, expect);
check_found_path_(ra_fixture, path, false, true, false, false, expect);
}
fn check_found_path_prefer_no_std(
@ -756,7 +761,15 @@ mod tests {
path: &str,
expect: Expect,
) {
check_found_path_(ra_fixture, path, false, false, true, expect);
check_found_path_(ra_fixture, path, false, false, true, false, expect);
}
fn check_found_path_prefer_no_std_allow_unstable(
#[rust_analyzer::rust_fixture] ra_fixture: &str,
path: &str,
expect: Expect,
) {
check_found_path_(ra_fixture, path, false, false, true, true, expect);
}
#[test]
@ -1951,7 +1964,7 @@ pub mod ops {
#[test]
fn respect_unstable_modules() {
check_found_path_prefer_no_std(
check_found_path_prefer_no_std_allow_unstable(
r#"
//- /main.rs crate:main deps:std,core
extern crate std;

View file

@ -10,7 +10,6 @@ use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use span::Edition;
use stdx::{format_to, TupleExt};
use syntax::ToSmolStr;
use triomphe::Arc;
use crate::{
@ -88,9 +87,9 @@ impl ImportMap {
.iter()
// We've only collected items, whose name cannot be tuple field so unwrapping is fine.
.flat_map(|(&item, (info, _))| {
info.iter().enumerate().map(move |(idx, info)| {
(item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32)
})
info.iter()
.enumerate()
.map(move |(idx, info)| (item, info.name.as_str(), idx as u32))
})
.collect();
importables.sort_by(|(_, l_info, _), (_, r_info, _)| {
@ -168,7 +167,8 @@ impl ImportMap {
let attr_id = if let Some(import) = import {
match import {
ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
ImportOrExternCrate::Import(id) => Some(id.import.into()),
ImportOrExternCrate::Import(id) => Some(id.use_.into()),
ImportOrExternCrate::Glob(id) => Some(id.use_.into()),
}
} else {
match item {
@ -441,7 +441,7 @@ pub fn search_dependencies(
}
fn search_maps(
db: &dyn DefDatabase,
_db: &dyn DefDatabase,
import_maps: &[Arc<ImportMap>],
mut stream: fst::map::Union<'_>,
query: &Query,
@ -464,11 +464,7 @@ fn search_maps(
.then(|| (item, &import_infos[info_idx as usize]))
})
.filter(|&(_, info)| {
query.search_mode.check(
&query.query,
query.case_sensitive,
&info.name.unescaped().display(db.upcast()).to_smolstr(),
)
query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str())
});
res.extend(iter.map(TupleExt::head));
}

View file

@ -31,35 +31,103 @@ pub struct PerNsGlobImports {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ImportOrExternCrate {
Glob(GlobId),
Import(ImportId),
ExternCrate(ExternCrateId),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum ImportType {
Import(ImportId),
Glob(UseId),
ExternCrate(ExternCrateId),
impl From<ImportOrGlob> for ImportOrExternCrate {
fn from(value: ImportOrGlob) -> Self {
match value {
ImportOrGlob::Glob(it) => ImportOrExternCrate::Glob(it),
ImportOrGlob::Import(it) => ImportOrExternCrate::Import(it),
}
}
}
impl ImportOrExternCrate {
pub fn into_import(self) -> Option<ImportId> {
pub fn import_or_glob(self) -> Option<ImportOrGlob> {
match self {
ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)),
ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)),
_ => None,
}
}
pub fn import(self) -> Option<ImportId> {
match self {
ImportOrExternCrate::Import(it) => Some(it),
_ => None,
}
}
pub fn glob(self) -> Option<GlobId> {
match self {
ImportOrExternCrate::Glob(id) => Some(id),
_ => None,
}
}
pub fn use_(self) -> Option<UseId> {
match self {
ImportOrExternCrate::Glob(id) => Some(id.use_),
ImportOrExternCrate::Import(id) => Some(id.use_),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ImportOrGlob {
Glob(GlobId),
Import(ImportId),
}
impl ImportOrGlob {
pub fn into_import(self) -> Option<ImportId> {
match self {
ImportOrGlob::Import(it) => Some(it),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ImportOrDef {
Import(ImportId),
Glob(GlobId),
ExternCrate(ExternCrateId),
Def(ModuleDefId),
}
impl From<ImportOrExternCrate> for ImportOrDef {
fn from(value: ImportOrExternCrate) -> Self {
match value {
ImportOrExternCrate::Import(it) => ImportOrDef::Import(it),
ImportOrExternCrate::Glob(it) => ImportOrDef::Glob(it),
ImportOrExternCrate::ExternCrate(it) => ImportOrDef::ExternCrate(it),
}
}
}
impl From<ImportOrGlob> for ImportOrDef {
fn from(value: ImportOrGlob) -> Self {
match value {
ImportOrGlob::Import(it) => ImportOrDef::Import(it),
ImportOrGlob::Glob(it) => ImportOrDef::Glob(it),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ImportId {
pub import: UseId,
pub use_: UseId,
pub idx: Idx<ast::UseTree>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct GlobId {
pub use_: UseId,
pub idx: Idx<ast::UseTree>,
}
@ -96,8 +164,8 @@ pub struct ItemScope {
// the resolutions of the imports of this scope
use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
use_imports_values: FxHashMap<ImportId, ImportOrDef>,
use_imports_macros: FxHashMap<ImportId, ImportOrDef>,
use_imports_values: FxHashMap<ImportOrGlob, ImportOrDef>,
use_imports_macros: FxHashMap<ImportOrGlob, ImportOrDef>,
use_decls: Vec<UseId>,
extern_crate_decls: Vec<ExternCrateId>,
@ -162,7 +230,7 @@ impl ItemScope {
.map(move |name| (name, self.get(name)))
}
pub fn values(&self) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportId>)> + '_ {
pub fn values(&self) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportOrGlob>)> + '_ {
self.values.iter().map(|(n, &i)| (n, i))
}
@ -172,7 +240,7 @@ impl ItemScope {
self.types.iter().map(|(n, &i)| (n, i))
}
pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportId>)> + '_ {
pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportOrGlob>)> + '_ {
self.macros.iter().map(|(n, &i)| (n, i))
}
@ -180,9 +248,10 @@ impl ItemScope {
self.use_imports_types
.keys()
.copied()
.filter_map(ImportOrExternCrate::into_import)
.filter_map(ImportOrExternCrate::import_or_glob)
.chain(self.use_imports_values.keys().copied())
.chain(self.use_imports_macros.keys().copied())
.filter_map(ImportOrGlob::into_import)
.sorted()
.dedup()
}
@ -192,10 +261,10 @@ impl ItemScope {
let mut def_map;
let mut scope = self;
while let Some(&m) = scope.use_imports_macros.get(&import) {
while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) {
match m {
ImportOrDef::Import(i) => {
let module_id = i.import.lookup(db).container;
let module_id = i.use_.lookup(db).container;
def_map = module_id.def_map(db);
scope = &def_map[module_id.local_id].scope;
import = i;
@ -211,7 +280,7 @@ impl ItemScope {
while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) {
match m {
ImportOrDef::Import(i) => {
let module_id = i.import.lookup(db).container;
let module_id = i.use_.lookup(db).container;
def_map = module_id.def_map(db);
scope = &def_map[module_id.local_id].scope;
import = i;
@ -224,10 +293,10 @@ impl ItemScope {
}
}
let mut scope = self;
while let Some(&m) = scope.use_imports_values.get(&import) {
while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) {
match m {
ImportOrDef::Import(i) => {
let module_id = i.import.lookup(db).container;
let module_id = i.use_.lookup(db).container;
def_map = module_id.def_map(db);
scope = &def_map[module_id.local_id].scope;
import = i;
@ -488,9 +557,13 @@ impl ItemScope {
self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis)
}
pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
// FIXME: import
self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import: None });
pub(crate) fn push_unnamed_trait(
&mut self,
tr: TraitId,
vis: Visibility,
import: Option<ImportId>,
) {
self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import });
}
pub(crate) fn push_res_with_import(
@ -498,7 +571,7 @@ impl ItemScope {
glob_imports: &mut PerNsGlobImports,
lookup: (LocalModuleId, Name),
def: PerNs,
import: Option<ImportType>,
import: Option<ImportOrExternCrate>,
) -> bool {
let mut changed = false;
@ -509,41 +582,22 @@ impl ItemScope {
match existing {
Entry::Vacant(entry) => {
match import {
Some(ImportType::Glob(_)) => {
Some(ImportOrExternCrate::Glob(_)) => {
glob_imports.types.insert(lookup.clone());
}
_ => _ = glob_imports.types.remove(&lookup),
}
let import = match import {
Some(ImportType::ExternCrate(extern_crate)) => {
Some(ImportOrExternCrate::ExternCrate(extern_crate))
}
Some(ImportType::Import(import)) => {
Some(ImportOrExternCrate::Import(import))
}
None | Some(ImportType::Glob(_)) => None,
};
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_types.insert(
import,
match prev {
Some(ImportOrExternCrate::Import(import)) => {
ImportOrDef::Import(import)
}
Some(ImportOrExternCrate::ExternCrate(import)) => {
ImportOrDef::ExternCrate(import)
}
None => ImportOrDef::Def(fld.def),
},
);
self.use_imports_types
.insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
}
entry.insert(fld);
changed = true;
}
Entry::Occupied(mut entry) => {
match import {
Some(ImportType::Glob(..)) => {
Some(ImportOrExternCrate::Glob(..)) => {
// Multiple globs may import the same item and they may
// override visibility from previously resolved globs. This is
// currently handled by `DefCollector`, because we need to
@ -552,28 +606,11 @@ impl ItemScope {
}
_ => {
if glob_imports.types.remove(&lookup) {
let import = match import {
Some(ImportType::ExternCrate(extern_crate)) => {
Some(ImportOrExternCrate::ExternCrate(extern_crate))
}
Some(ImportType::Import(import)) => {
Some(ImportOrExternCrate::Import(import))
}
None | Some(ImportType::Glob(_)) => None,
};
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_types.insert(
import,
match prev {
Some(ImportOrExternCrate::Import(import)) => {
ImportOrDef::Import(import)
}
Some(ImportOrExternCrate::ExternCrate(import)) => {
ImportOrDef::ExternCrate(import)
}
None => ImportOrDef::Def(fld.def),
},
prev.map_or(ImportOrDef::Def(fld.def), Into::into),
);
}
cov_mark::hit!(import_shadowed);
@ -591,44 +628,31 @@ impl ItemScope {
match existing {
Entry::Vacant(entry) => {
match import {
Some(ImportType::Glob(_)) => {
Some(ImportOrExternCrate::Glob(_)) => {
glob_imports.values.insert(lookup.clone());
}
_ => _ = glob_imports.values.remove(&lookup),
}
let import = match import {
Some(ImportType::Import(import)) => Some(import),
_ => None,
};
let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_values.insert(
import,
match prev {
Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.def),
},
);
self.use_imports_values
.insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
}
entry.insert(fld);
changed = true;
}
Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
Entry::Occupied(mut entry)
if !matches!(import, Some(ImportOrExternCrate::Glob(..))) =>
{
if glob_imports.values.remove(&lookup) {
cov_mark::hit!(import_shadowed);
let import = match import {
Some(ImportType::Import(import)) => Some(import),
_ => None,
};
let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_values.insert(
import,
match prev {
Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.def),
},
);
self.use_imports_values
.insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
}
entry.insert(fld);
changed = true;
@ -643,43 +667,33 @@ impl ItemScope {
match existing {
Entry::Vacant(entry) => {
match import {
Some(ImportType::Glob(_)) => {
Some(ImportOrExternCrate::Glob(_)) => {
glob_imports.macros.insert(lookup.clone());
}
_ => _ = glob_imports.macros.remove(&lookup),
}
let import = match import {
Some(ImportType::Import(import)) => Some(import),
_ => None,
};
let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_macros.insert(
import,
match prev {
Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.def.into()),
},
prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into),
);
}
entry.insert(fld);
changed = true;
}
Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
Entry::Occupied(mut entry)
if !matches!(import, Some(ImportOrExternCrate::Glob(..))) =>
{
if glob_imports.macros.remove(&lookup) {
cov_mark::hit!(import_shadowed);
let import = match import {
Some(ImportType::Import(import)) => Some(import),
_ => None,
};
let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_macros.insert(
import,
match prev {
Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.def.into()),
},
prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into),
);
}
entry.insert(fld);
@ -704,16 +718,27 @@ impl ItemScope {
.map(|def| &mut def.vis)
.chain(self.values.values_mut().map(|def| &mut def.vis))
.chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis))
.for_each(|vis| {
*vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
.for_each(|vis| match vis {
&mut Visibility::Module(_, visibility_explicitness) => {
*vis = Visibility::Module(this_module, visibility_explicitness)
}
Visibility::Public => {
*vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
}
});
for mac in self.macros.values_mut() {
if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) {
continue;
}
mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit);
match mac.vis {
Visibility::Module(_, visibility_explicitness) => {
mac.vis = Visibility::Module(this_module, visibility_explicitness)
}
Visibility::Public => {
mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
}
}
}
}
@ -732,20 +757,25 @@ impl ItemScope {
buf.push_str(" t");
match import {
Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
Some(ImportOrExternCrate::Glob(_)) => buf.push('g'),
Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
None => (),
}
}
if let Some(Item { import, .. }) = def.values {
buf.push_str(" v");
if import.is_some() {
buf.push('i');
match import {
Some(ImportOrGlob::Import(_)) => buf.push('i'),
Some(ImportOrGlob::Glob(_)) => buf.push('g'),
None => (),
}
}
if let Some(Item { import, .. }) = def.macros {
buf.push_str(" m");
if import.is_some() {
buf.push('i');
match import {
Some(ImportOrGlob::Import(_)) => buf.push('i'),
Some(ImportOrGlob::Glob(_)) => buf.push('g'),
None => (),
}
}
if def.is_none() {
@ -828,7 +858,7 @@ impl PerNs {
match def {
ModuleDefId::ModuleId(_) => PerNs::types(def, v, import),
ModuleDefId::FunctionId(_) => {
PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob))
}
ModuleDefId::AdtId(adt) => match adt {
AdtId::UnionId(_) => PerNs::types(def, v, import),
@ -843,14 +873,14 @@ impl PerNs {
},
ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import),
ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => {
PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob))
}
ModuleDefId::TraitId(_) => PerNs::types(def, v, import),
ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import),
ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import),
ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import),
ModuleDefId::MacroId(mac) => {
PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import))
PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob))
}
}
}

View file

@ -372,6 +372,7 @@ language_item_table! {
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None;
Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1);
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);

View file

@ -114,6 +114,9 @@ pub struct ImportPathConfig {
pub prefer_prelude: bool,
/// If true, prefer abs path (starting with `::`) where it is available.
pub prefer_absolute: bool,
/// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no
/// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`.
pub allow_unstable: bool,
}
#[derive(Debug)]
@ -910,6 +913,7 @@ pub enum AssocItemId {
ConstId(ConstId),
TypeAliasId(TypeAliasId),
}
// FIXME: not every function, ... is actually an assoc item. maybe we should make
// sure that you can only turn actual assoc items into AssocItemIds. This would
// require not implementing From, and instead having some checked way of

View file

@ -28,7 +28,7 @@ use triomphe::Arc;
use crate::{
attr::Attrs,
db::DefDatabase,
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
item_tree::{
self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind,
@ -208,7 +208,7 @@ struct DefCollector<'a> {
def_map: DefMap,
// The dependencies of the current crate, including optional deps like `test`.
deps: FxHashMap<Name, Dependency>,
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<(ImportDirective, PerNs)>,
unresolved_macros: Vec<MacroDirective>,
@ -524,11 +524,7 @@ impl DefCollector<'_> {
match per_ns.types {
Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => {
// FIXME: This should specifically look for a glob import somehow and record that here
self.def_map.prelude = Some((
m,
import.and_then(ImportOrExternCrate::into_import).map(|it| it.import),
));
self.def_map.prelude = Some((m, import.and_then(ImportOrExternCrate::use_)));
}
types => {
tracing::debug!(
@ -845,13 +841,14 @@ impl DefCollector<'_> {
def.values = None;
def.macros = None;
}
let imp = ImportType::Import(ImportId { import: id, idx: use_tree });
let imp = ImportOrExternCrate::Import(ImportId { use_: id, idx: use_tree });
tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
self.update(module_id, &[(name.cloned(), def)], vis, Some(imp));
}
ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => {
ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree } => {
tracing::debug!("glob import: {:?}", import);
let glob = GlobId { use_: id, idx: use_tree };
match def.take_types() {
Some(ModuleDefId::ModuleId(m)) => {
if is_prelude {
@ -875,7 +872,12 @@ impl DefCollector<'_> {
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
self.update(
module_id,
&items,
vis,
Some(ImportOrExternCrate::Glob(glob)),
);
} else {
// glob import from same crate => we do an initial
// import, and then need to propagate any further
@ -907,11 +909,16 @@ impl DefCollector<'_> {
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
self.update(
module_id,
&items,
vis,
Some(ImportOrExternCrate::Glob(glob)),
);
// record the glob import in case we add further items
let glob = self.glob_imports.entry(m.local_id).or_default();
match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) {
None => glob.push((module_id, vis, id)),
let glob_imports = self.glob_imports.entry(m.local_id).or_default();
match glob_imports.iter_mut().find(|(mid, _, _)| *mid == module_id) {
None => glob_imports.push((module_id, vis, glob)),
Some((_, old_vis, _)) => {
if let Some(new_vis) = old_vis.max(vis, &self.def_map) {
*old_vis = new_vis;
@ -944,7 +951,12 @@ impl DefCollector<'_> {
(Some(name), res)
})
.collect::<Vec<_>>();
self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id)));
self.update(
module_id,
&resolutions,
vis,
Some(ImportOrExternCrate::Glob(glob)),
);
}
Some(d) => {
tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d);
@ -964,7 +976,7 @@ impl DefCollector<'_> {
resolutions: &[(Option<Name>, PerNs)],
// Visibility this import will have
vis: Visibility,
import: Option<ImportType>,
import: Option<ImportOrExternCrate>,
) {
self.db.unwind_if_cancelled();
self.update_recursive(module_id, resolutions, vis, import, 0)
@ -978,7 +990,7 @@ impl DefCollector<'_> {
// All resolutions are imported with this visibility; the visibilities in
// the `PerNs` values are ignored and overwritten
vis: Visibility,
import: Option<ImportType>,
import: Option<ImportOrExternCrate>,
depth: usize,
) {
if GLOB_RECURSION_LIMIT.check(depth).is_err() {
@ -994,8 +1006,10 @@ impl DefCollector<'_> {
self.push_res_and_update_glob_vis(module_id, name, *res, vis, import);
}
None => {
let tr = match res.take_types() {
Some(ModuleDefId::TraitId(tr)) => tr,
let (tr, import) = match res.take_types_full() {
Some(Item { def: ModuleDefId::TraitId(tr), vis: _, import }) => {
(tr, import)
}
Some(other) => {
tracing::debug!("non-trait `_` import of {:?}", other);
continue;
@ -1021,7 +1035,11 @@ impl DefCollector<'_> {
if should_update {
changed = true;
self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis);
self.def_map.modules[module_id].scope.push_unnamed_trait(
tr,
vis,
import.and_then(ImportOrExternCrate::import),
);
}
}
}
@ -1043,13 +1061,13 @@ impl DefCollector<'_> {
.cloned()
.collect::<Vec<_>>();
for (glob_importing_module, glob_import_vis, use_) in glob_imports {
for (glob_importing_module, glob_import_vis, glob) in glob_imports {
let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis);
self.update_recursive(
glob_importing_module,
resolutions,
vis,
Some(ImportType::Glob(use_)),
Some(ImportOrExternCrate::Glob(glob)),
depth + 1,
);
}
@ -1061,7 +1079,7 @@ impl DefCollector<'_> {
name: &Name,
mut defs: PerNs,
vis: Visibility,
def_import_type: Option<ImportType>,
def_import_type: Option<ImportOrExternCrate>,
) -> bool {
// `extern crate crate_name` things can be re-exported as `pub use crate_name`.
// But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name`
@ -1074,10 +1092,10 @@ impl DefCollector<'_> {
let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else {
return false;
};
let Some(ImportType::Import(id)) = def_import_type else {
let Some(ImportOrExternCrate::Import(id)) = def_import_type else {
return false;
};
let use_id = id.import.lookup(self.db).id;
let use_id = id.use_.lookup(self.db).id;
let item_tree = use_id.item_tree(self.db);
let use_kind = item_tree[use_id.value].use_tree.kind();
let UseTreeKind::Single { path, .. } = use_kind else {
@ -1100,7 +1118,7 @@ impl DefCollector<'_> {
let mut changed = false;
if let Some(ImportType::Glob(_)) = def_import_type {
if let Some(ImportOrExternCrate::Glob(_)) = def_import_type {
let prev_defs = self.def_map[module_id].scope.get(name);
// Multiple globs may import the same item and they may override visibility from
@ -1727,7 +1745,7 @@ impl ModCollector<'_, '_> {
),
)],
vis,
Some(ImportType::ExternCrate(id)),
Some(ImportOrExternCrate::ExternCrate(id)),
);
} else {
if let Some(name) = name {

View file

@ -4,7 +4,6 @@ use base_db::AnchoredPath;
use hir_expand::{name::Name, HirFileIdExt};
use limit::Limit;
use span::EditionedFileId;
use syntax::ToSmolStr as _;
use crate::{db::DefDatabase, HirFileId};
@ -35,7 +34,7 @@ impl ModDir {
let path = match attr_path {
None => {
let mut path = self.dir_path.clone();
path.push(&name.unescaped().display_no_db().to_smolstr());
path.push(name.as_str());
path
}
Some(attr_path) => {
@ -66,7 +65,7 @@ impl ModDir {
name: &Name,
attr_path: Option<&str>,
) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> {
let name = name.unescaped();
let name = name.as_str();
let mut candidate_files = ArrayVec::<_, 2>::new();
match attr_path {
@ -74,16 +73,8 @@ impl ModDir {
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
}
None => {
candidate_files.push(format!(
"{}{}.rs",
self.dir_path.0,
name.display(db.upcast())
));
candidate_files.push(format!(
"{}{}/mod.rs",
self.dir_path.0,
name.display(db.upcast())
));
candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
}
};
@ -97,7 +88,7 @@ impl ModDir {
let dir_path = if root_dir_owner {
DirPath::empty()
} else {
DirPath::new(format!("{}/", name.display(db.upcast())))
DirPath::new(format!("{}/", name))
};
if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) {
return Ok((

View file

@ -103,8 +103,8 @@ mod a {
c: t
crate::a::b::c
A: v
b: t
A: vg
b: tg
"#]],
);
}
@ -256,8 +256,8 @@ pub enum Foo { Bar, Baz }
"#,
expect![[r#"
crate
Bar: t v
Baz: t v
Bar: tg vg
Baz: tg vg
"#]],
);
}
@ -421,10 +421,10 @@ pub struct NotExported;
"#,
expect![[r#"
crate
Exported: t v
PublicItem: t v
allowed_reexport: t
exported: t
Exported: tg vg
PublicItem: tg vg
allowed_reexport: tg
exported: tg
not_allowed_reexport1: _
not_allowed_reexport2: _
"#]],
@ -692,7 +692,7 @@ mod b {
b: t
crate::a
T: t v
T: t vg
crate::b
T: v

View file

@ -18,9 +18,9 @@ pub struct Baz;
"#,
expect![[r#"
crate
Baz: t v
Foo: t v
bar: t
Baz: tg vg
Foo: tg vg
bar: tg
foo: t
crate::foo
@ -53,20 +53,20 @@ pub use super::*;
"#,
expect![[r#"
crate
Baz: t v
Foo: t v
bar: t
Baz: tg vg
Foo: tg vg
bar: tg
foo: t
crate::foo
Baz: t v
Baz: tg vg
Foo: t v
bar: t
crate::foo::bar
Baz: t v
Foo: t v
bar: t
Foo: tg vg
bar: tg
"#]],
);
}
@ -91,20 +91,20 @@ pub use super::*;
",
expect![[r#"
crate
Baz: t v
bar: t
Baz: tg vg
bar: tg
foo: t
crate::foo
Baz: t v
Baz: tg vg
PrivateStructFoo: t v
bar: t
crate::foo::bar
Baz: t v
PrivateStructBar: t v
PrivateStructFoo: t v
bar: t
PrivateStructFoo: tg vg
bar: tg
"#]],
);
}
@ -130,9 +130,9 @@ pub(crate) struct PubCrateStruct;
",
expect![[r#"
crate
Foo: t
PubCrateStruct: t v
bar: t
Foo: tg
PubCrateStruct: tg vg
bar: tg
foo: t
crate::foo
@ -160,7 +160,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
Baz: t v
Baz: tg vg
"#]],
);
}
@ -178,7 +178,7 @@ struct Foo;
"#,
expect![[r#"
crate
Baz: t v
Baz: tg vg
"#]],
);
}
@ -193,8 +193,8 @@ use self::Foo::*;
"#,
expect![[r#"
crate
Bar: t v
Baz: t v
Bar: tg vg
Baz: tg vg
Foo: t
"#]],
);
@ -210,8 +210,8 @@ use self::Foo::{*};
"#,
expect![[r#"
crate
Bar: t v
Baz: t v
Bar: tg vg
Baz: tg vg
Foo: t
"#]],
);
@ -359,7 +359,7 @@ use event::Event;
event: t
crate::event
Event: t v
Event: t vg
serenity: t
crate::event::serenity
@ -388,10 +388,10 @@ use reexport::*;
"#,
expect![[r#"
crate
Trait: t
Trait: tg
defs: t
function: v
makro: m
function: vg
makro: mg
reexport: t
crate::defs
@ -400,10 +400,10 @@ use reexport::*;
makro: m
crate::reexport
Trait: t
function: v
Trait: tg
function: vg
inner: t
makro: m
makro: mg
crate::reexport::inner
Trait: ti
@ -442,12 +442,12 @@ mod glob_target {
ShouldBePrivate: t v
crate::outer
ShouldBePrivate: t v
ShouldBePrivate: tg vg
inner_superglob: t
crate::outer::inner_superglob
ShouldBePrivate: t v
inner_superglob: t
ShouldBePrivate: tg vg
inner_superglob: tg
"#]],
);
}
@ -473,20 +473,20 @@ use reexport_2::*;
"#,
expect![[r#"
crate
Placeholder: t v
Placeholder: tg vg
libs: t
reexport_1: t
reexport_1: tg
reexport_2: t
crate::libs
Placeholder: t v
crate::reexport_2
Placeholder: t v
Placeholder: tg vg
reexport_1: t
crate::reexport_2::reexport_1
Placeholder: t v
Placeholder: tg vg
"#]],
);
}

View file

@ -97,9 +97,9 @@ macro_rules! structs {
bar: t
crate::bar
Bar: t
Foo: t
bar: t
Bar: tg
Foo: tg
bar: tg
"#]],
);
}
@ -130,9 +130,9 @@ macro_rules! structs {
bar: t
crate::bar
Bar: t
Foo: t
bar: t
Bar: tg
Foo: tg
bar: tg
"#]],
);
}
@ -169,9 +169,9 @@ macro_rules! inner {
bar: t
crate::bar
Bar: t
Foo: t
bar: t
Bar: tg
Foo: tg
bar: tg
"#]],
);
}
@ -794,7 +794,7 @@ pub trait Clone {}
"#,
expect![[r#"
crate
Clone: t m
Clone: tg mg
"#]],
);
}
@ -1075,9 +1075,9 @@ macro_rules! mbe {
"#,
expect![[r#"
crate
DummyTrait: m
attribute_macro: m
function_like_macro: m
DummyTrait: mg
attribute_macro: mg
function_like_macro: mg
"#]],
);
}

View file

@ -6,7 +6,7 @@
use bitflags::bitflags;
use crate::{
item_scope::{ImportId, ImportOrExternCrate, ItemInNs},
item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ItemInNs},
visibility::Visibility,
MacroId, ModuleDefId,
};
@ -36,8 +36,8 @@ pub struct Item<Def, Import = ImportId> {
}
pub type TypesItem = Item<ModuleDefId, ImportOrExternCrate>;
pub type ValuesItem = Item<ModuleDefId>;
pub type MacrosItem = Item<MacroId>;
pub type ValuesItem = Item<ModuleDefId, ImportOrGlob>;
pub type MacrosItem = Item<MacroId, ImportOrGlob>;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct PerNs {
@ -59,7 +59,7 @@ impl PerNs {
PerNs { types: None, values: None, macros: None }
}
pub fn values(def: ModuleDefId, vis: Visibility, import: Option<ImportId>) -> PerNs {
pub fn values(def: ModuleDefId, vis: Visibility, import: Option<ImportOrGlob>) -> PerNs {
PerNs { types: None, values: Some(Item { def, vis, import }), macros: None }
}
@ -78,13 +78,13 @@ impl PerNs {
values: Some(Item {
def: values,
vis,
import: import.and_then(ImportOrExternCrate::into_import),
import: import.and_then(ImportOrExternCrate::import_or_glob),
}),
macros: None,
}
}
pub fn macros(def: MacroId, vis: Visibility, import: Option<ImportId>) -> PerNs {
pub fn macros(def: MacroId, vis: Visibility, import: Option<ImportOrGlob>) -> PerNs {
PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) }
}
@ -108,7 +108,7 @@ impl PerNs {
self.values.map(|it| it.def)
}
pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> {
pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportOrGlob>)> {
self.values.map(|it| (it.def, it.import))
}
@ -116,7 +116,7 @@ impl PerNs {
self.macros.map(|it| it.def)
}
pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> {
pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportOrGlob>)> {
self.macros.map(|it| (it.def, it.import))
}
@ -159,14 +159,12 @@ impl PerNs {
.map(|it| (ItemInNs::Types(it.def), it.import))
.into_iter()
.chain(
self.values.map(|it| {
(ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::Import))
}),
self.values
.map(|it| (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::from))),
)
.chain(
self.macros.map(|it| {
(ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::Import))
}),
self.macros
.map(|it| (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::from))),
)
}
}

View file

@ -19,7 +19,7 @@ use crate::{
db::DefDatabase,
generics::{GenericParams, TypeOrConstParamData},
hir::{BindingId, ExprId, LabelId},
item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE},
item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
lang_item::LangItemTarget,
nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo},
path::{ModPath, Path, PathKind},
@ -107,7 +107,7 @@ pub enum TypeNs {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ResolveValueResult {
ValueNs(ValueNs, Option<ImportId>),
ValueNs(ValueNs, Option<ImportOrGlob>),
Partial(TypeNs, usize, Option<ImportOrExternCrate>),
}
@ -485,7 +485,7 @@ impl Resolver {
db: &dyn DefDatabase,
path: &ModPath,
expected_macro_kind: Option<MacroSubNs>,
) -> Option<(MacroId, Option<ImportId>)> {
) -> Option<(MacroId, Option<ImportOrGlob>)> {
let (item_map, module) = self.item_scope();
item_map
.resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
@ -1014,7 +1014,7 @@ impl ModuleItemMap {
}
}
fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportId>)> {
fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportOrGlob>)> {
let (def, import) = per_ns.take_values_import()?;
let res = match def {
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),

View file

@ -23,15 +23,6 @@ pub struct ModPath {
segments: SmallVec<[Name; 1]>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnescapedModPath<'a>(&'a ModPath);
impl<'a> UnescapedModPath<'a> {
pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
UnescapedDisplay { db, path: self }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathKind {
Plain,
@ -135,9 +126,11 @@ impl ModPath {
_ => None,
}
}
pub fn unescaped(&self) -> UnescapedModPath<'_> {
UnescapedModPath(self)
pub fn display_verbatim<'a>(
&'a self,
db: &'a dyn crate::db::ExpandDatabase,
) -> impl fmt::Display + 'a {
Display { db, path: self, edition: None }
}
pub fn display<'a>(
@ -145,7 +138,7 @@ impl ModPath {
db: &'a dyn crate::db::ExpandDatabase,
edition: Edition,
) -> impl fmt::Display + 'a {
Display { db, path: self, edition }
Display { db, path: self, edition: Some(edition) }
}
}
@ -158,23 +151,12 @@ impl Extend<Name> for ModPath {
struct Display<'a> {
db: &'a dyn ExpandDatabase,
path: &'a ModPath,
edition: Edition,
edition: Option<Edition>,
}
impl fmt::Display for Display<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition))
}
}
struct UnescapedDisplay<'a> {
db: &'a dyn ExpandDatabase,
path: &'a UnescapedModPath<'a>,
}
impl fmt::Display for UnescapedDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
display_fmt_path(self.db, self.path.0, f, Escape::No)
display_fmt_path(self.db, self.path, f, self.edition)
}
}
@ -184,16 +166,11 @@ impl From<Name> for ModPath {
}
}
enum Escape {
No,
IfNeeded(Edition),
}
fn display_fmt_path(
db: &dyn ExpandDatabase,
path: &ModPath,
f: &mut fmt::Formatter<'_>,
escaped: Escape,
edition: Option<Edition>,
) -> fmt::Result {
let mut first_segment = true;
let mut add_segment = |s| -> fmt::Result {
@ -221,10 +198,10 @@ fn display_fmt_path(
f.write_str("::")?;
}
first_segment = false;
match escaped {
Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?,
Escape::No => segment.unescaped().display(db).fmt(f)?,
}
match edition {
Some(edition) => segment.display(db, edition).fmt(f)?,
None => fmt::Display::fmt(segment.as_str(), f)?,
};
}
Ok(())
}

View file

@ -4,8 +4,8 @@ use std::fmt;
use intern::{sym, Symbol};
use span::{Edition, SyntaxContextId};
use syntax::ast;
use syntax::utils::is_raw_identifier;
use syntax::{ast, format_smolstr};
/// `Name` is a wrapper around string, which is used in hir for both references
/// and declarations. In theory, names should also carry hygiene info, but we are
@ -51,33 +51,26 @@ impl PartialEq<Symbol> for Name {
}
}
impl PartialEq<&Symbol> for Name {
fn eq(&self, &sym: &&Symbol) -> bool {
self.symbol == *sym
}
}
impl PartialEq<Name> for Symbol {
fn eq(&self, name: &Name) -> bool {
*self == name.symbol
}
}
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnescapedName<'a>(&'a Name);
impl<'a> UnescapedName<'a> {
pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
_ = db;
UnescapedDisplay { name: self }
}
#[doc(hidden)]
pub fn display_no_db(self) -> impl fmt::Display + 'a {
UnescapedDisplay { name: self }
impl PartialEq<Name> for &Symbol {
fn eq(&self, name: &Name) -> bool {
**self == name.symbol
}
}
impl Name {
/// Note: this is private to make creating name from random string hard.
/// Hopefully, this should allow us to integrate hygiene cleaner in the
/// future, and to switch to interned representation of names.
fn new_text(text: &str) -> Name {
debug_assert!(!text.starts_with("r#"));
Name { symbol: Symbol::intern(text), ctx: () }
}
@ -87,12 +80,15 @@ impl Name {
// Can't do that for all `SyntaxContextId`s because it breaks Salsa.
ctx.remove_root_edition();
_ = ctx;
Self::new_text(text)
match text.strip_prefix("r#") {
Some(text) => Self::new_text(text),
None => Self::new_text(text),
}
}
pub fn new_root(text: &str) -> Name {
// The edition doesn't matter for hygiene.
Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015))
Self::new(text, SyntaxContextId::root(Edition::Edition2015))
}
pub fn new_tuple_field(idx: usize) -> Name {
@ -119,12 +115,22 @@ impl Name {
}
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
Self::new_text(lt.text().as_str().trim_start_matches("r#"))
let text = lt.text();
match text.strip_prefix("'r#") {
Some(text) => Self::new_text(&format_smolstr!("'{text}")),
None => Self::new_text(text.as_str()),
}
}
/// Resolve a name from the text of token.
fn resolve(raw_text: &str) -> Name {
Name::new_text(raw_text.trim_start_matches("r#"))
pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
debug_assert!(!symbol.as_str().starts_with("r#"));
_ = ctx;
Self { symbol, ctx: () }
}
// FIXME: This needs to go once we have hygiene
pub fn new_symbol_root(sym: Symbol) -> Self {
Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015))
}
/// A fake name for things missing in the source code.
@ -161,22 +167,19 @@ impl Name {
self.symbol.as_str().parse().ok()
}
/// Whether this name needs to be escaped in the given edition via `r#`.
pub fn needs_escape(&self, edition: Edition) -> bool {
is_raw_identifier(self.symbol.as_str(), edition)
}
/// Returns the text this name represents if it isn't a tuple field.
///
/// Do not use this for user-facing text, use `display` instead to handle editions properly.
// FIXME: This should take a database argument to hide the interning
pub fn as_str(&self) -> &str {
self.symbol.as_str()
}
// FIXME: Remove this
pub fn unescaped(&self) -> UnescapedName<'_> {
UnescapedName(self)
}
pub fn needs_escape(&self, edition: Edition) -> bool {
is_raw_identifier(self.symbol.as_str(), edition)
}
pub fn display<'a>(
&'a self,
db: &dyn crate::db::ExpandDatabase,
@ -186,7 +189,7 @@ impl Name {
self.display_no_db(edition)
}
// FIXME: Remove this
// FIXME: Remove this in favor of `display`, see fixme on `as_str`
#[doc(hidden)]
pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ {
Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) }
@ -195,24 +198,6 @@ impl Name {
pub fn symbol(&self) -> &Symbol {
&self.symbol
}
pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
debug_assert!(!symbol.as_str().starts_with("r#"));
_ = ctx;
Self { symbol, ctx: () }
}
// FIXME: This needs to go once we have hygiene
pub fn new_symbol_root(sym: Symbol) -> Self {
debug_assert!(!sym.as_str().starts_with("r#"));
Self { symbol: sym, ctx: () }
}
// FIXME: Remove this
#[inline]
pub fn eq_ident(&self, ident: &str) -> bool {
self.as_str() == ident.trim_start_matches("r#")
}
}
struct Display<'a> {
@ -229,17 +214,6 @@ impl fmt::Display for Display<'_> {
}
}
struct UnescapedDisplay<'a> {
name: UnescapedName<'a>,
}
impl fmt::Display for UnescapedDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let symbol = self.name.0.symbol.as_str();
fmt::Display::fmt(symbol, f)
}
}
pub trait AsName {
fn as_name(&self) -> Name;
}
@ -248,14 +222,14 @@ impl AsName for ast::NameRef {
fn as_name(&self) -> Name {
match self.as_tuple_field() {
Some(idx) => Name::new_tuple_field(idx),
None => Name::resolve(&self.text()),
None => Name::new_root(&self.text()),
}
}
}
impl AsName for ast::Name {
fn as_name(&self) -> Name {
Name::resolve(&self.text())
Name::new_root(&self.text())
}
}
@ -270,7 +244,7 @@ impl AsName for ast::NameOrNameRef {
impl<Span> AsName for tt::Ident<Span> {
fn as_name(&self) -> Name {
Name::resolve(self.sym.as_str())
Name::new_root(self.sym.as_str())
}
}
@ -288,6 +262,6 @@ impl AsName for ast::FieldKind {
impl AsName for base_db::Dependency {
fn as_name(&self) -> Name {
Name::new_text(&self.name)
Name::new_root(&self.name)
}
}

View file

@ -17,7 +17,7 @@ use crate::{
TraitEnvironment, Ty, TyBuilder, TyKind,
};
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(20);
#[derive(Debug)]
pub(crate) enum AutoderefKind {
@ -39,7 +39,7 @@ pub fn autoderef(
) -> impl Iterator<Item = Ty> {
let mut table = InferenceTable::new(db, env);
let ty = table.instantiate_canonical(ty);
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false);
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false);
let mut v = Vec::new();
while let Some((ty, _steps)) = autoderef.next() {
// `ty` may contain unresolved inference variables. Since there's no chance they would be
@ -49,7 +49,7 @@ pub fn autoderef(
// If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
// would revisit some already visited types. Stop here to avoid duplication.
//
// XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't
// XXX: The recursion limit for `Autoderef` is currently 20, so `Vec::contains()` shouldn't
// be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more
// performant.
if v.contains(&resolved) {
@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> {
at_start: bool,
steps: T,
explicit: bool,
use_receiver_trait: bool,
}
impl<'table, 'db> Autoderef<'table, 'db> {
pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
pub(crate) fn new(
table: &'table mut InferenceTable<'db>,
ty: Ty,
explicit: bool,
use_receiver_trait: bool,
) -> Self {
let ty = table.resolve_ty_shallow(&ty);
Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit }
Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait }
}
pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> {
table: &'table mut InferenceTable<'db>,
ty: Ty,
explicit: bool,
use_receiver_trait: bool,
) -> Self {
let ty = table.resolve_ty_shallow(&ty);
Autoderef { table, ty, at_start: true, steps: 0, explicit }
Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait }
}
}
@ -137,7 +144,8 @@ impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
return None;
}
let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?;
let (kind, new_ty) =
autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?;
self.steps.push(kind, &self.ty);
self.ty = new_ty;
@ -150,11 +158,12 @@ pub(crate) fn autoderef_step(
table: &mut InferenceTable<'_>,
ty: Ty,
explicit: bool,
use_receiver_trait: bool,
) -> Option<(AutoderefKind, Ty)> {
if let Some(derefed) = builtin_deref(table.db, &ty, explicit) {
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
} else {
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?))
}
}
@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>(
pub(crate) fn deref_by_trait(
table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
ty: Ty,
use_receiver_trait: bool,
) -> Option<Ty> {
let _p = tracing::info_span!("deref_by_trait").entered();
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait(
return None;
}
let deref_trait =
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
let trait_id = || {
if use_receiver_trait {
if let Some(receiver) =
db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
{
return Some(receiver);
}
}
// Old rustc versions might not have `Receiver` trait.
// Fallback to `Deref` if they don't
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())
};
let trait_id = trait_id()?;
let target = db
.trait_data(deref_trait)
.trait_data(trait_id)
.associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?;
let projection = {
let b = TyBuilder::subst_for_def(db, deref_trait, None);
let b = TyBuilder::subst_for_def(db, trait_id, None);
if b.remaining() != 1 {
// the Target type + Deref trait should only have one generic parameter,
// namely Deref's Self type

View file

@ -231,8 +231,7 @@ impl<'a> DeclValidator<'a> {
.filter_map(|(pat_id, pat)| match pat {
Pat::Bind { id, .. } => {
let bind_name = &body.bindings[*id].name;
let mut suggested_text =
to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?;
let mut suggested_text = to_lower_snake_case(bind_name.as_str())?;
if is_raw_identifier(&suggested_text, edition) {
suggested_text.insert_str(0, "r#");
}

View file

@ -34,6 +34,7 @@ use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use span::Edition;
use stdx::never;
@ -87,6 +88,35 @@ pub struct HirFormatter<'a> {
omit_verbose_types: bool,
closure_style: ClosureStyle,
display_target: DisplayTarget,
bounds_formatting_ctx: BoundsFormattingCtx,
}
#[derive(Default)]
enum BoundsFormattingCtx {
Entered {
/// We can have recursive bounds like the following case:
/// ```rust
/// where
/// T: Foo,
/// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar
/// ```
/// So, record the projection types met while formatting bounds and
//. prevent recursing into their bounds to avoid infinite loops.
projection_tys_met: FxHashSet<ProjectionTy>,
},
#[default]
Exited,
}
impl BoundsFormattingCtx {
fn contains(&mut self, proj: &ProjectionTy) -> bool {
match self {
BoundsFormattingCtx::Entered { projection_tys_met } => {
projection_tys_met.contains(proj)
}
BoundsFormattingCtx::Exited => false,
}
}
}
impl HirFormatter<'_> {
@ -97,6 +127,30 @@ impl HirFormatter<'_> {
fn end_location_link(&mut self) {
self.fmt.end_location_link();
}
fn format_bounds_with<T, F: FnOnce(&mut Self) -> T>(
&mut self,
target: ProjectionTy,
format_bounds: F,
) -> T {
match self.bounds_formatting_ctx {
BoundsFormattingCtx::Entered { ref mut projection_tys_met } => {
projection_tys_met.insert(target);
format_bounds(self)
}
BoundsFormattingCtx::Exited => {
let mut projection_tys_met = FxHashSet::default();
projection_tys_met.insert(target);
self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met };
let res = format_bounds(self);
// Since we want to prevent only the infinite recursions in bounds formatting
// and do not want to skip formatting of other separate bounds, clear context
// when exiting the formatting of outermost bounds
self.bounds_formatting_ctx = BoundsFormattingCtx::Exited;
res
}
}
}
}
pub trait HirDisplay {
@ -220,6 +274,7 @@ pub trait HirDisplay {
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
show_container_bounds: false,
bounds_formatting_ctx: Default::default(),
}) {
Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@ -427,6 +482,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
display_target: self.display_target,
closure_style: self.closure_style,
show_container_bounds: self.show_container_bounds,
bounds_formatting_ctx: Default::default(),
})
}
@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy {
// `<Param as Trait>::Assoc`
if !f.display_target.is_source_code() {
if let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
let db = f.db;
let id = from_placeholder_idx(db, *idx);
let generics = generics(db.upcast(), id.parent);
if !f.bounds_formatting_ctx.contains(self) {
let db = f.db;
let id = from_placeholder_idx(db, *idx);
let generics = generics(db.upcast(), id.parent);
let substs = generics.placeholder_subst(db);
let bounds = db
.generic_predicates(id.parent)
.iter()
.map(|pred| pred.clone().substitute(Interner, &substs))
.filter(|wc| match wc.skip_binders() {
WhereClause::Implemented(tr) => {
match tr.self_type_parameter(Interner).kind(Interner) {
TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
_ => false,
let substs = generics.placeholder_subst(db);
let bounds = db
.generic_predicates(id.parent)
.iter()
.map(|pred| pred.clone().substitute(Interner, &substs))
.filter(|wc| match wc.skip_binders() {
WhereClause::Implemented(tr) => {
matches!(
tr.self_type_parameter(Interner).kind(Interner),
TyKind::Alias(_)
)
}
}
WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) {
TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
_ => false,
},
// We shouldn't be here if these exist
WhereClause::AliasEq(_) => false,
WhereClause::LifetimeOutlives(_) => false,
})
.collect::<Vec<_>>();
if !bounds.is_empty() {
return write_bounds_like_dyn_trait_with_prefix(
f,
"impl",
Either::Left(
&TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner),
),
&bounds,
SizedByDefault::NotSized,
);
};
WhereClause::TypeOutlives(t) => {
matches!(t.ty.kind(Interner), TyKind::Alias(_))
}
// We shouldn't be here if these exist
WhereClause::AliasEq(_) => false,
WhereClause::LifetimeOutlives(_) => false,
})
.collect::<Vec<_>>();
if !bounds.is_empty() {
return f.format_bounds_with(self.clone(), |f| {
write_bounds_like_dyn_trait_with_prefix(
f,
"impl",
Either::Left(
&TyKind::Alias(AliasTy::Projection(self.clone()))
.intern(Interner),
),
&bounds,
SizedByDefault::NotSized,
)
});
}
}
}
}
@ -1159,6 +1219,7 @@ impl HirDisplay for Ty {
prefer_no_std: false,
prefer_prelude: true,
prefer_absolute: false,
allow_unstable: true,
},
) {
write!(f, "{}", path.display(f.db.upcast(), f.edition()))?;

View file

@ -277,7 +277,7 @@ impl CapturedItem {
/// Converts the place to a name that can be inserted into source code.
pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
let body = db.body(owner);
let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string();
let mut result = body[self.place.local].name.as_str().to_owned();
for proj in &self.place.projections {
match proj {
ProjectionElem::Deref => {}

View file

@ -420,7 +420,7 @@ impl InferenceTable<'_> {
let snapshot = self.snapshot();
let mut autoderef = Autoderef::new(self, from_ty.clone(), false);
let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false);
let mut first_error = None;
let mut found = None;

View file

@ -487,7 +487,7 @@ impl InferenceContext<'_> {
}
Expr::Call { callee, args, .. } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
let (res, derefed_callee) = loop {
let Some((callee_deref_ty, _)) = derefs.next() else {
break (None, callee_ty.clone());
@ -854,7 +854,7 @@ impl InferenceContext<'_> {
if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) {
self.resolve_ty_shallow(derefed)
} else {
deref_by_trait(&mut self.table, inner_ty)
deref_by_trait(&mut self.table, inner_ty, false)
.unwrap_or_else(|| self.err_ty())
}
}
@ -1718,7 +1718,7 @@ impl InferenceContext<'_> {
receiver_ty: &Ty,
name: &Name,
) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false);
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false);
let mut private_field = None;
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
let (field_id, parameters) = match derefed_ty.kind(Interner) {

View file

@ -528,7 +528,7 @@ impl ReceiverAdjustments {
let mut ty = table.resolve_ty_shallow(&ty);
let mut adjust = Vec::new();
for _ in 0..self.autoderefs {
match autoderef::autoderef_step(table, ty.clone(), true) {
match autoderef::autoderef_step(table, ty.clone(), true, false) {
None => {
never!("autoderef not possible for {:?}", ty);
ty = TyKind::Error.intern(Interner);
@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver(
// be found in any of the derefs of receiver_ty, so we have to go through
// that, including raw derefs.
table.run_in_snapshot(|table| {
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
let mut autoderef =
autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
while let Some((self_ty, _)) = autoderef.next() {
iterate_inherent_methods(
&self_ty,
@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver(
ControlFlow::Continue(())
})?;
table.run_in_snapshot(|table| {
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
let mut autoderef =
autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
while let Some((self_ty, _)) = autoderef.next() {
if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) {
// don't try to resolve methods on unknown types
@ -1709,7 +1711,7 @@ fn autoderef_method_receiver(
ty: Ty,
) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
let mut deref_chain: Vec<_> = Vec::new();
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false);
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true);
while let Some((ty, derefs)) = autoderef.next() {
deref_chain.push((
autoderef.table.canonicalize(ty),

View file

@ -1343,7 +1343,7 @@ fn foo<T: Trait>(a: &T) {
fn autoderef_visibility_field() {
check(
r#"
//- minicore: deref
//- minicore: receiver
mod a {
pub struct Foo(pub char);
pub struct Bar(i32);
@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() {
cov_mark::check!(autoderef_candidate_not_visible);
check(
r#"
//- minicore: deref
//- minicore: receiver
mod a {
pub struct Foo(pub char);
impl Foo {
@ -1741,7 +1741,7 @@ fn main() {
fn deref_fun_1() {
check_types(
r#"
//- minicore: deref
//- minicore: receiver
struct A<T, U>(T, U);
struct B<T>(T);
@ -1782,7 +1782,7 @@ fn test() {
fn deref_fun_2() {
check_types(
r#"
//- minicore: deref
//- minicore: receiver
struct A<T, U>(T, U);
struct B<T>(T);
@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into<Foo>) {
fn bad_inferred_reference_2() {
check_no_mismatches(
r#"
//- minicore: deref
//- minicore: receiver
trait ExactSizeIterator {
fn len(&self) -> usize;
}
@ -2054,7 +2054,7 @@ fn foo() {
fn box_deref_is_builtin() {
check(
r#"
//- minicore: deref
//- minicore: receiver
use core::ops::Deref;
#[lang = "owned_box"]
@ -2087,7 +2087,7 @@ fn test() {
fn manually_drop_deref_is_not_builtin() {
check(
r#"
//- minicore: manually_drop, deref
//- minicore: manually_drop, receiver
struct Foo;
impl Foo {
fn foo(&self) {}
@ -2105,7 +2105,7 @@ fn test() {
fn mismatched_args_due_to_supertraits_with_deref() {
check_no_mismatches(
r#"
//- minicore: deref
//- minicore: receiver
use core::ops::Deref;
trait Trait1 {
@ -2139,3 +2139,34 @@ fn problem_method<T: Trait3>() {
"#,
);
}
#[test]
fn receiver_without_deref_impl() {
check(
r#"
//- minicore: receiver
use core::ops::Receiver;
struct Foo;
impl Foo {
fn foo1(self: &Bar) -> i32 { 42 }
fn foo2(self: Bar) -> bool { true }
}
struct Bar;
impl Receiver for Bar {
type Target = Foo;
}
fn main() {
let bar = Bar;
let _v1 = bar.foo1();
//^^^ type: i32
let _v2 = bar.foo2();
//^^^ type: bool
}
"#,
);
}

View file

@ -45,7 +45,7 @@ use hir_def::{
body::BodyDiagnostic,
data::{adt::VariantData, TraitFlags},
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat},
hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
lang_item::LangItemTarget,
layout::{self, ReprOptions, TargetDataLayout},
@ -2470,20 +2470,31 @@ impl Param {
}
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
let parent = match self.func {
Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it),
Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0,
_ => return None,
};
let body = db.body(parent);
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
Some(Local { parent, binding_id: self_param })
} else if let Pat::Bind { id, .. } =
&body[body.params[self.idx - body.self_param.is_some() as usize]]
{
Some(Local { parent, binding_id: *id })
} else {
None
match self.func {
Callee::Def(CallableDefId::FunctionId(it)) => {
let parent = DefWithBodyId::FunctionId(it);
let body = db.body(parent);
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
Some(Local { parent, binding_id: self_param })
} else if let Pat::Bind { id, .. } =
&body[body.params[self.idx - body.self_param.is_some() as usize]]
{
Some(Local { parent, binding_id: *id })
} else {
None
}
}
Callee::Closure(closure, _) => {
let c = db.lookup_intern_closure(closure.into());
let body = db.body(c.0);
if let Expr::Closure { args, .. } = &body[c.1] {
if let Pat::Bind { id, .. } = &body[args[self.idx]] {
return Some(Local { parent: c.0, binding_id: *id });
}
}
None
}
_ => None,
}
}
@ -2756,6 +2767,15 @@ impl Trait {
traits.iter().map(|tr| Trait::from(*tr)).collect()
}
pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq<Name>) -> Option<Function> {
db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then(
|&(_, it)| match it {
AssocItemId::FunctionId(id) => Some(Function { id }),
_ => None,
},
)
}
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
}
@ -4673,6 +4693,10 @@ impl Type {
matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool))
}
pub fn is_str(&self) -> bool {
matches!(self.ty.kind(Interner), TyKind::Str)
}
pub fn is_never(&self) -> bool {
matches!(self.ty.kind(Interner), TyKind::Never)
}

View file

@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
}
pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
/// Env is used to derive the trait environment
// FIXME: better api for the trait environment
pub fn resolve_trait_impl_method(
&self,
env: Type,
trait_: Trait,
func: Function,
subst: impl IntoIterator<Item = Type>,
) -> Option<Function> {
let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None);
for s in subst {
substs = substs.push(s.ty);
}
Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into())
}
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
@ -1471,6 +1483,8 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
}
// This does not resolve the method call to the correct trait impl!
// We should probably fix that.
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
}

View file

@ -322,68 +322,6 @@ impl SourceAnalyzer {
}
}
// If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str.
pub(crate) fn resolve_known_blanket_dual_impls(
&self,
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
) -> Option<Function> {
// e.g. if the method call is let b = a.into(),
// - receiver_type is A (type of a)
// - return_type is B (type of b)
// We will find the definition of B::from(a: A).
let callable = self.resolve_method_call_as_callable(db, call)?;
let (_, receiver_type) = callable.receiver_param(db)?;
let return_type = callable.return_type();
let (search_method, substs) = match call.name_ref()?.text().as_str() {
"into" => {
let trait_ =
self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?;
(
self.trait_fn(db, trait_, "from")?,
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
.push(return_type.ty)
.push(receiver_type.ty)
.build(),
)
}
"try_into" => {
let trait_ = self
.resolver
.resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?;
(
self.trait_fn(db, trait_, "try_from")?,
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
// If the method is try_into() or parse(), return_type is Result<T, Error>.
// Get T from type arguments of Result<T, Error>.
.push(return_type.type_arguments().next()?.ty)
.push(receiver_type.ty)
.build(),
)
}
"parse" => {
let trait_ =
self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?;
(
self.trait_fn(db, trait_, "from_str")?,
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
.push(return_type.type_arguments().next()?.ty)
.build(),
)
}
_ => return None,
};
let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs);
// If found_method == search_method, the method in trait itself is resolved.
// It means the blanket dual impl is not found.
if found_method == search_method {
None
} else {
Some(found_method.into())
}
}
pub(crate) fn resolve_expr_as_callable(
&self,
db: &dyn HirDatabase,
@ -1309,18 +1247,6 @@ impl SourceAnalyzer {
Some((trait_id, fn_id))
}
fn trait_fn(
&self,
db: &dyn HirDatabase,
trait_id: TraitId,
method_name: &str,
) -> Option<FunctionId> {
db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item {
AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t),
_ => None,
})
}
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
}

View file

@ -3,7 +3,7 @@
use either::Either;
use hir_def::{
db::DefDatabase,
item_scope::{ImportId, ImportOrExternCrate},
item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob},
per_ns::Item,
src::{HasChildSource, HasSource},
visibility::{Visibility, VisibilityExplicitness},
@ -55,9 +55,10 @@ impl DeclarationLocation {
}
/// Represents an outstanding module that the symbol collector must collect symbols from.
#[derive(Debug)]
struct SymbolCollectorWork {
module_id: ModuleId,
parent: Option<DefWithBodyId>,
parent: Option<Name>,
}
pub struct SymbolCollector<'a> {
@ -81,7 +82,15 @@ impl<'a> SymbolCollector<'a> {
}
}
pub fn new_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> {
let mut symbol_collector = SymbolCollector::new(db);
symbol_collector.collect(module);
symbol_collector.finish()
}
pub fn collect(&mut self, module: Module) {
let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered();
tracing::info!(?module, "SymbolCollector::collect",);
self.edition = module.krate().edition(self.db);
// The initial work is the root module we're collecting, additional work will
@ -97,16 +106,12 @@ impl<'a> SymbolCollector<'a> {
self.symbols.into_iter().collect()
}
pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> {
let mut symbol_collector = SymbolCollector::new(db);
symbol_collector.collect(module);
symbol_collector.finish()
}
fn do_work(&mut self, work: SymbolCollectorWork) {
let _p = tracing::info_span!("SymbolCollector::do_work", ?work).entered();
tracing::info!(?work, "SymbolCollector::do_work");
self.db.unwind_if_cancelled();
let parent_name = work.parent.and_then(|id| self.def_with_body_id_name(id));
let parent_name = work.parent.map(|name| name.as_str().to_smolstr());
self.with_container_name(parent_name, |s| s.collect_from_module(work.module_id));
}
@ -116,18 +121,18 @@ impl<'a> SymbolCollector<'a> {
ModuleDefId::ModuleId(id) => this.push_module(id, name),
ModuleDefId::FunctionId(id) => {
this.push_decl(id, name, false);
this.collect_from_body(id);
this.collect_from_body(id, Some(name.clone()));
}
ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false),
ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false),
ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false),
ModuleDefId::ConstId(id) => {
this.push_decl(id, name, false);
this.collect_from_body(id);
this.collect_from_body(id, Some(name.clone()));
}
ModuleDefId::StaticId(id) => {
this.push_decl(id, name, false);
this.collect_from_body(id);
this.collect_from_body(id, Some(name.clone()));
}
ModuleDefId::TraitId(id) => {
this.push_decl(id, name, false);
@ -153,24 +158,32 @@ impl<'a> SymbolCollector<'a> {
// Nested trees are very common, so a cache here will hit a lot.
let import_child_source_cache = &mut FxHashMap::default();
let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| {
let is_explicit_import = |vis| match vis {
Visibility::Public => true,
Visibility::Module(_, VisibilityExplicitness::Explicit) => true,
Visibility::Module(_, VisibilityExplicitness::Implicit) => false,
};
let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId, vis| {
let source = import_child_source_cache
.entry(i.import)
.or_insert_with(|| i.import.child_source(this.db.upcast()));
.entry(i.use_)
.or_insert_with(|| i.use_.child_source(this.db.upcast()));
let Some(use_tree_src) = source.value.get(i.idx) else { return };
let Some(name_ptr) = use_tree_src
.rename()
.and_then(|rename| rename.name())
.map(Either::Left)
.or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))
.map(|it| AstPtr::new(&it))
else {
let rename = use_tree_src.rename().and_then(|rename| rename.name());
let name_syntax = match rename {
Some(name) => Some(Either::Left(name)),
None if is_explicit_import(vis) => {
(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))()
}
None => None,
};
let Some(name_syntax) = name_syntax else {
return;
};
let dec_loc = DeclarationLocation {
hir_file_id: source.file_id,
ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
name_ptr,
name_ptr: AstPtr::new(&name_syntax),
};
this.symbols.insert(FileSymbol {
name: name.symbol().clone(),
@ -183,23 +196,23 @@ impl<'a> SymbolCollector<'a> {
};
let push_extern_crate =
|this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| {
|this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId, vis| {
let loc = i.lookup(this.db.upcast());
let source = loc.source(this.db.upcast());
let Some(name_ptr) = source
.value
.rename()
.and_then(|rename| rename.name())
.map(Either::Left)
.or_else(|| source.value.name_ref().map(Either::Right))
.map(|it| AstPtr::new(&it))
else {
let rename = source.value.rename().and_then(|rename| rename.name());
let name_syntax = match rename {
Some(name) => Some(Either::Left(name)),
None if is_explicit_import(vis) => None,
None => source.value.name_ref().map(Either::Right),
};
let Some(name_syntax) = name_syntax else {
return;
};
let dec_loc = DeclarationLocation {
hir_file_id: source.file_id,
ptr: SyntaxNodePtr::new(source.value.syntax()),
name_ptr,
name_ptr: AstPtr::new(&name_syntax),
};
this.symbols.insert(FileSymbol {
name: name.symbol().clone(),
@ -211,18 +224,6 @@ impl<'a> SymbolCollector<'a> {
});
};
let is_explicit_import = |vis| {
match vis {
Visibility::Module(_, VisibilityExplicitness::Explicit) => true,
Visibility::Module(_, VisibilityExplicitness::Implicit) => {
// consider imports in the crate root explicit, as these are visibly
// crate-wide anyways
module_id.is_crate_root()
}
Visibility::Public => true,
}
};
let def_map = module_id.def_map(self.db.upcast());
let scope = &def_map[module_id.local_id].scope;
@ -232,14 +233,14 @@ impl<'a> SymbolCollector<'a> {
for (name, Item { def, vis, import }) in scope.types() {
if let Some(i) = import {
if is_explicit_import(vis) {
match i {
ImportOrExternCrate::Import(i) => push_import(self, i, name, def),
ImportOrExternCrate::ExternCrate(i) => {
push_extern_crate(self, i, name, def)
}
match i {
ImportOrExternCrate::Import(i) => push_import(self, i, name, def, vis),
ImportOrExternCrate::Glob(_) => (),
ImportOrExternCrate::ExternCrate(i) => {
push_extern_crate(self, i, name, def, vis)
}
}
continue;
}
// self is a declaration
@ -248,8 +249,9 @@ impl<'a> SymbolCollector<'a> {
for (name, Item { def, vis, import }) in scope.macros() {
if let Some(i) = import {
if is_explicit_import(vis) {
push_import(self, i, name, def.into());
match i {
ImportOrGlob::Import(i) => push_import(self, i, name, def.into(), vis),
ImportOrGlob::Glob(_) => (),
}
continue;
}
@ -259,8 +261,9 @@ impl<'a> SymbolCollector<'a> {
for (name, Item { def, vis, import }) in scope.values() {
if let Some(i) = import {
if is_explicit_import(vis) {
push_import(self, i, name, def);
match i {
ImportOrGlob::Import(i) => push_import(self, i, name, def, vis),
ImportOrGlob::Glob(_) => (),
}
continue;
}
@ -269,7 +272,7 @@ impl<'a> SymbolCollector<'a> {
}
for const_id in scope.unnamed_consts() {
self.collect_from_body(const_id);
self.collect_from_body(const_id, None);
}
for (name, id) in scope.legacy_macros() {
@ -285,7 +288,7 @@ impl<'a> SymbolCollector<'a> {
}
}
fn collect_from_body(&mut self, body_id: impl Into<DefWithBodyId>) {
fn collect_from_body(&mut self, body_id: impl Into<DefWithBodyId>, name: Option<Name>) {
let body_id = body_id.into();
let body = self.db.body(body_id);
@ -294,7 +297,7 @@ impl<'a> SymbolCollector<'a> {
for (id, _) in def_map.modules() {
self.work.push(SymbolCollectorWork {
module_id: def_map.module_id(id),
parent: Some(body_id),
parent: name.clone(),
});
}
}
@ -333,24 +336,6 @@ impl<'a> SymbolCollector<'a> {
}
}
fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
match body_id {
DefWithBodyId::FunctionId(id) => {
Some(self.db.function_data(id).name.display_no_db(self.edition).to_smolstr())
}
DefWithBodyId::StaticId(id) => {
Some(self.db.static_data(id).name.display_no_db(self.edition).to_smolstr())
}
DefWithBodyId::ConstId(id) => {
Some(self.db.const_data(id).name.as_ref()?.display_no_db(self.edition).to_smolstr())
}
DefWithBodyId::VariantId(id) => {
Some(self.db.enum_variant_data(id).name.display_no_db(self.edition).to_smolstr())
}
DefWithBodyId::InTypeConstId(_) => Some("in type const".into()),
}
}
fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) {
match assoc_item_id {
AssocItemId::FunctionId(id) => self.push_decl(id, name, true),

View file

@ -28,6 +28,7 @@ impl AssistConfig {
prefer_no_std: self.prefer_no_std,
prefer_prelude: self.prefer_prelude,
prefer_absolute: self.prefer_absolute,
allow_unstable: true,
}
}
}

View file

@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
};
// Verify this is `bool::then` that is being called.
let func = ctx.sema.resolve_method_call(&mcall)?;
if !func.name(ctx.sema.db).eq_ident("then") {
if func.name(ctx.sema.db) != sym::then {
return None;
}
let assoc = func.as_assoc_item(ctx.sema.db)?;

View file

@ -343,11 +343,9 @@ fn compute_closure_type_params(
let mut mentioned_names = mentioned_generic_params
.iter()
.filter_map(|param| match param {
hir::GenericParam::TypeParam(param) => {
Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr())
}
hir::GenericParam::TypeParam(param) => Some(param.name(ctx.db()).as_str().to_smolstr()),
hir::GenericParam::ConstParam(param) => {
Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr())
Some(param.name(ctx.db()).as_str().to_smolstr())
}
hir::GenericParam::LifetimeParam(_) => None,
})
@ -390,7 +388,7 @@ fn compute_closure_type_params(
let has_name = syntax
.descendants()
.filter_map(ast::NameOrNameRef::cast)
.any(|name| mentioned_names.contains(&*name.text()));
.any(|name| mentioned_names.contains(name.text().trim_start_matches("r#")));
let mut has_new_params = false;
if has_name {
syntax

View file

@ -170,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
),
_ => false,
})
.any(|(name, _)| name.eq_ident(variant_name.text().as_str()))
.any(|(name, _)| name.as_str() == variant_name.text().trim_start_matches("r#"))
}
fn extract_generic_params(

View file

@ -1672,8 +1672,8 @@ macro_rules! vec {
() => {Vec}
}
fn main() {
let $0vec = vec![];
let _ = vec;
let $0items = vec![];
let _ = items;
}
"#,
"Extract into variable",
@ -1696,8 +1696,8 @@ macro_rules! vec {
() => {Vec}
}
fn main() {
const $0VEC: Vec = vec![];
let _ = VEC;
const $0ITEMS: Vec = vec![];
let _ = ITEMS;
}
"#,
"Extract into constant",
@ -1720,8 +1720,8 @@ macro_rules! vec {
() => {Vec}
}
fn main() {
static $0VEC: Vec = vec![];
let _ = VEC;
static $0ITEMS: Vec = vec![];
let _ = ITEMS;
}
"#,
"Extract into static",
@ -2019,8 +2019,8 @@ impl<T> Vec<T> {
}
fn foo(s: &mut S) {
let $0vec = &mut s.vec;
vec.push(0);
let $0items = &mut s.vec;
items.push(0);
}"#,
"Extract into variable",
);
@ -2106,8 +2106,8 @@ impl<T> Vec<T> {
}
fn foo(f: &mut Y) {
let $0vec = &mut f.field.field.vec;
vec.push(0);
let $0items = &mut f.field.field.vec;
items.push(0);
}"#,
"Extract into variable",
);

View file

@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
let (_, def) = module
.scope(ctx.db(), None)
.into_iter()
.find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?;
.find(|(name, _)| name.as_str() == name_ref.text().trim_start_matches("r#"))?;
let ScopeDef::ModuleDef(def) = def else {
return None;
};

View file

@ -2,7 +2,7 @@ use ide_db::{
assists::{AssistId, AssistKind},
base_db::AnchoredPathBuf,
};
use syntax::{ast, AstNode};
use syntax::{ast, AstNode, ToSmolStr};
use crate::{
assist_context::{AssistContext, Assists},
@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}
let target = source_file.syntax().text_range();
let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string();
let module_name = module.name(ctx.db())?.as_str().to_smolstr();
let path = format!("../{module_name}.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path };
acc.add(

View file

@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
.string_value_unescape()
.is_none() =>
{
format_to!(buf, "{}/", name.unescaped().display(db))
format_to!(buf, "{}/", name.as_str())
}
_ => (),
}

View file

@ -2,7 +2,7 @@ use ide_db::{
assists::{AssistId, AssistKind},
base_db::AnchoredPathBuf,
};
use syntax::{ast, AstNode};
use syntax::{ast, AstNode, ToSmolStr};
use crate::{
assist_context::{AssistContext, Assists},
@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
}
let target = source_file.syntax().text_range();
let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string();
let module_name = module.name(ctx.db())?.as_str().to_smolstr();
let path = format!("./{module_name}/mod.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path };
acc.add(

View file

@ -208,7 +208,7 @@ fn find_trait_method(
if let Some(hir::AssocItem::Function(method)) =
trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
item.name(db)
.map(|name| name.eq_ident(trait_method_name.text().as_str()))
.map(|name| name.as_str() == trait_method_name.text().trim_start_matches("r#"))
.unwrap_or(false)
})
{

View file

@ -110,7 +110,7 @@ fn compute_fields_ranks(
.fields(ctx.db())
.into_iter()
.enumerate()
.map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx))
.map(|(idx, field)| (field.name(ctx.db()).as_str().to_owned(), idx))
.collect();
Some(res)

View file

@ -122,7 +122,7 @@ fn compute_item_ranks(
.iter()
.flat_map(|i| i.name(ctx.db()))
.enumerate()
.map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx))
.map(|(idx, name)| (name.as_str().to_owned(), idx))
.collect(),
)
}

View file

@ -188,9 +188,6 @@ impl Completions {
resolution: hir::ScopeDef,
doc_aliases: Vec<syntax::SmolStr>,
) {
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
return;
}
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@ -216,9 +213,6 @@ impl Completions {
local_name: hir::Name,
resolution: hir::ScopeDef,
) {
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
return;
}
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@ -241,7 +235,7 @@ impl Completions {
path_ctx: &PathCompletionCtx,
e: hir::Enum,
) {
if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
if !ctx.check_stability_and_hidden(e) {
return;
}
e.variants(ctx.db)
@ -257,9 +251,6 @@ impl Completions {
local_name: hir::Name,
doc_aliases: Vec<syntax::SmolStr>,
) {
if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
return;
}
self.add_path_resolution(
ctx,
path_ctx,
@ -276,9 +267,6 @@ impl Completions {
mac: hir::Macro,
local_name: hir::Name,
) {
if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&mac) {
Visible::Yes => false,
Visible::Editable => true,
@ -302,9 +290,6 @@ impl Completions {
func: hir::Function,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
@ -332,9 +317,6 @@ impl Completions {
receiver: Option<SmolStr>,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
@ -362,9 +344,6 @@ impl Completions {
func: hir::Function,
import: LocatedImport,
) {
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
@ -387,9 +366,6 @@ impl Completions {
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&konst) {
Visible::Yes => false,
Visible::Editable => true,
@ -406,9 +382,6 @@ impl Completions {
ctx: &CompletionContext<'_>,
type_alias: hir::TypeAlias,
) {
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&type_alias) {
Visible::Yes => false,
Visible::Editable => true,
@ -438,7 +411,7 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
if !ctx.check_stability_and_hidden(variant) {
return;
}
if let Some(builder) =
@ -455,7 +428,7 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
if !ctx.check_stability_and_hidden(variant) {
return;
}
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
@ -479,9 +452,6 @@ impl Completions {
field: hir::Field,
ty: &hir::Type,
) {
if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&field) {
Visible::Yes => false,
Visible::Editable => true,
@ -506,12 +476,18 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
return;
}
if let Some(builder) =
render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
{
let is_private_editable = match ctx.is_visible(&strukt) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
if let Some(builder) = render_struct_literal(
RenderContext::new(ctx).private_editable(is_private_editable),
path_ctx,
strukt,
path,
local_name,
) {
self.add(builder.build(ctx.db));
}
}
@ -523,10 +499,17 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
return;
}
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
let is_private_editable = match ctx.is_visible(&un) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
let item = render_union_literal(
RenderContext::new(ctx).private_editable(is_private_editable),
un,
path,
local_name,
);
self.add_opt(item);
}
@ -571,7 +554,7 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
if !ctx.check_stability_and_hidden(variant) {
return;
}
self.add_opt(render_variant_pat(
@ -591,7 +574,7 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
if !ctx.check_stability_and_hidden(variant) {
return;
}
let path = Some(&path);
@ -612,10 +595,17 @@ impl Completions {
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
return;
}
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
let is_private_editable = match ctx.is_visible(&strukt) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
self.add_opt(render_struct_pat(
RenderContext::new(ctx).private_editable(is_private_editable),
pattern_ctx,
strukt,
local_name,
));
}
pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) {
@ -660,7 +650,7 @@ fn enum_variants_with_paths(
if let Some(path) = ctx.module.find_path(
ctx.db,
hir::ModuleDef::from(variant),
ctx.config.import_path_config(),
ctx.config.import_path_config(ctx.is_nightly),
) {
// Variants with trivial paths are already added by the existing completion logic,
// so we should avoid adding these twice

View file

@ -42,31 +42,38 @@ pub(crate) fn complete_dot(
item.detail("expr.await");
item.add_to(acc, ctx.db);
// Completions that skip `.await`, e.g. `.await.foo()`.
let dot_access_kind = match &dot_access.kind {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
}
it @ DotAccessKind::Method { .. } => *it,
};
let dot_access = DotAccess {
receiver: dot_access.receiver.clone(),
receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }),
kind: dot_access_kind,
ctx: dot_access.ctx,
};
complete_fields(
acc,
ctx,
&future_output,
|acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty),
|acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
is_field_access,
is_method_access_with_parens,
);
complete_methods(ctx, &future_output, &traits_in_scope, |func| {
acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
});
if ctx.config.enable_auto_await {
// Completions that skip `.await`, e.g. `.await.foo()`.
let dot_access_kind = match &dot_access.kind {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
}
it @ DotAccessKind::Method { .. } => *it,
};
let dot_access = DotAccess {
receiver: dot_access.receiver.clone(),
receiver_ty: Some(hir::TypeInfo {
original: future_output.clone(),
adjusted: None,
}),
kind: dot_access_kind,
ctx: dot_access.ctx,
};
complete_fields(
acc,
ctx,
&future_output,
|acc, field, ty| {
acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty)
},
|acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
is_field_access,
is_method_access_with_parens,
);
complete_methods(ctx, &future_output, &traits_in_scope, |func| {
acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
});
}
}
complete_fields(
@ -82,39 +89,41 @@ pub(crate) fn complete_dot(
acc.add_method(ctx, dot_access, func, None, None)
});
// FIXME:
// Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
// Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid
let iter = receiver_ty
.strip_references()
.add_reference(hir::Mutability::Shared)
.into_iterator_iter(ctx.db)
.map(|ty| (ty, SmolStr::new_static("iter()")));
// Does <receiver_ty as IntoIterator>::IntoIter` exist?
let into_iter = || {
receiver_ty
.clone()
if ctx.config.enable_auto_iter {
// FIXME:
// Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
// Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid
let iter = receiver_ty
.strip_references()
.add_reference(hir::Mutability::Shared)
.into_iterator_iter(ctx.db)
.map(|ty| (ty, SmolStr::new_static("into_iter()")))
};
if let Some((iter, iter_sym)) = iter.or_else(into_iter) {
// Skip iterators, e.g. complete `.iter().filter_map()`.
let dot_access_kind = match &dot_access.kind {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
}
it @ DotAccessKind::Method { .. } => *it,
.map(|ty| (ty, SmolStr::new_static("iter()")));
// Does <receiver_ty as IntoIterator>::IntoIter` exist?
let into_iter = || {
receiver_ty
.clone()
.into_iterator_iter(ctx.db)
.map(|ty| (ty, SmolStr::new_static("into_iter()")))
};
let dot_access = DotAccess {
receiver: dot_access.receiver.clone(),
receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
kind: dot_access_kind,
ctx: dot_access.ctx,
};
complete_methods(ctx, &iter, &traits_in_scope, |func| {
acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
});
if let Some((iter, iter_sym)) = iter.or_else(into_iter) {
// Skip iterators, e.g. complete `.iter().filter_map()`.
let dot_access_kind = match &dot_access.kind {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
}
it @ DotAccessKind::Method { .. } => *it,
};
let dot_access = DotAccess {
receiver: dot_access.receiver.clone(),
receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
kind: dot_access_kind,
ctx: dot_access.ctx,
};
complete_methods(ctx, &iter, &traits_in_scope, |func| {
acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
});
}
}
}
@ -1466,4 +1475,34 @@ async fn bar() {
"#,
);
}
#[test]
fn receiver_without_deref_impl_completion() {
check_no_kw(
r#"
//- minicore: receiver
use core::ops::Receiver;
struct Foo;
impl Foo {
fn foo(self: Bar) {}
}
struct Bar;
impl Receiver for Bar {
type Target = Foo;
}
fn main() {
let bar = Bar;
bar.$0
}
"#,
expect![[r#"
me foo() fn(self: Bar)
"#]],
);
}
}

View file

@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path(
.find_path(
ctx.db,
hir::ModuleDef::from(strukt),
ctx.config.import_path_config(),
ctx.config.import_path_config(ctx.is_nightly),
)
.filter(|it| it.len() > 1);
@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path(
.find_path(
ctx.db,
hir::ModuleDef::from(un),
ctx.config.import_path_config(),
ctx.config.import_path_config(ctx.is_nightly),
)
.filter(|it| it.len() > 1);

View file

@ -5,7 +5,7 @@ use ide_db::imports::{
insert_use::ImportScope,
};
use itertools::Itertools;
use syntax::{ast, AstNode, SyntaxNode, ToSmolStr};
use syntax::{ast, AstNode, SyntaxNode};
use crate::{
config::AutoImportExclusionType,
@ -257,7 +257,7 @@ fn import_on_the_fly(
};
let user_input_lowercased = potential_import_name.to_lowercase();
let import_cfg = ctx.config.import_path_config();
let import_cfg = ctx.config.import_path_config(ctx.is_nightly);
import_assets
.search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind)
@ -316,7 +316,7 @@ fn import_on_the_fly_pat_(
ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)),
};
let user_input_lowercased = potential_import_name.to_lowercase();
let cfg = ctx.config.import_path_config();
let cfg = ctx.config.import_path_config(ctx.is_nightly);
import_assets
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
@ -358,7 +358,7 @@ fn import_on_the_fly_method(
let user_input_lowercased = potential_import_name.to_lowercase();
let cfg = ctx.config.import_path_config();
let cfg = ctx.config.import_path_config(ctx.is_nightly);
import_assets
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
@ -444,7 +444,7 @@ fn compute_fuzzy_completion_order_key(
cov_mark::hit!(certain_fuzzy_order_test);
let import_name = match proposed_mod_path.segments().last() {
// FIXME: nasty alloc, this is a hot path!
Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(),
Some(name) => name.as_str().to_ascii_lowercase(),
None => return usize::MAX,
};
match import_name.match_indices(user_input_lowercased).next() {

View file

@ -31,7 +31,7 @@
//! }
//! ```
use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name};
use hir::{db::ExpandDatabase, MacroFileId, Name};
use ide_db::text_edit::TextEdit;
use ide_db::{
documentation::HasDocs, path_transform::PathTransform,
@ -85,7 +85,7 @@ fn complete_trait_impl_name(
name: &Option<ast::Name>,
kind: ImplCompletionKind,
) -> Option<()> {
let item = match name {
let macro_file_item = match name {
Some(name) => name.syntax().parent(),
None => {
let token = &ctx.token;
@ -96,12 +96,12 @@ fn complete_trait_impl_name(
.parent()
}
}?;
let item = ctx.sema.original_syntax_node_rooted(&item)?;
let real_file_item = ctx.sema.original_syntax_node_rooted(&macro_file_item)?;
// item -> ASSOC_ITEM_LIST -> IMPL
let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
let impl_def = ast::Impl::cast(macro_file_item.parent()?.parent()?)?;
let replacement_range = {
// ctx.sema.original_ast_node(item)?;
let first_child = item
let first_child = real_file_item
.children_with_tokens()
.find(|child| {
!matches!(
@ -109,7 +109,7 @@ fn complete_trait_impl_name(
SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR
)
})
.unwrap_or_else(|| SyntaxElement::Node(item.clone()));
.unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone()));
TextRange::new(first_child.text_range().start(), ctx.source_range().end())
};
@ -133,8 +133,11 @@ pub(crate) fn complete_trait_impl_item_by_name(
acc,
ctx,
ImplCompletionKind::All,
match name_ref {
Some(name) => name.syntax().text_range(),
match name_ref
.as_ref()
.and_then(|name| ctx.sema.original_syntax_node_rooted(name.syntax()))
{
Some(name) => name.text_range(),
None => ctx.source_range(),
},
impl_,
@ -152,7 +155,7 @@ fn complete_trait_impl(
if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
get_missing_assoc_items(&ctx.sema, impl_def)
.into_iter()
.filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
.filter(|item| ctx.check_stability_and_hidden(*item))
.for_each(|item| {
use self::ImplCompletionKind::*;
match (item, kind) {
@ -359,7 +362,7 @@ fn add_type_alias_impl(
type_alias: hir::TypeAlias,
impl_def: hir::Impl,
) {
let alias_name = type_alias.name(ctx.db).unescaped().display(ctx.db).to_smolstr();
let alias_name = type_alias.name(ctx.db).as_str().to_smolstr();
let label = format_smolstr!("type {alias_name} =");
@ -516,7 +519,7 @@ fn function_declaration(
mod tests {
use expect_test::expect;
use crate::tests::{check_edit, check_no_kw};
use crate::tests::{check, check_edit, check_no_kw};
#[test]
fn no_completion_inside_fn() {
@ -1639,4 +1642,51 @@ impl DesugaredAsyncTrait for () {
"#,
);
}
#[test]
fn within_attr_macro() {
check(
r#"
//- proc_macros: identity
trait Trait {
fn foo(&self) {}
fn bar(&self) {}
fn baz(&self) {}
}
#[proc_macros::identity]
impl Trait for () {
f$0
}
"#,
expect![[r#"
me fn bar(..)
me fn baz(..)
me fn foo(..)
md proc_macros
kw crate::
kw self::
"#]],
);
check(
r#"
//- proc_macros: identity
trait Trait {
fn foo(&self) {}
fn bar(&self) {}
fn baz(&self) {}
}
#[proc_macros::identity]
impl Trait for () {
fn $0
}
"#,
expect![[r#"
me fn bar(..)
me fn baz(..)
me fn foo(..)
"#]],
);
}
}

View file

@ -7,7 +7,7 @@ use ide_db::{
base_db::{SourceRootDatabase, VfsPath},
FxHashSet, RootDatabase, SymbolKind,
};
use syntax::{ast, AstNode, SyntaxKind, ToSmolStr};
use syntax::{ast, AstNode, SyntaxKind};
use crate::{context::CompletionContext, CompletionItem, Completions};
@ -140,9 +140,7 @@ fn directory_to_look_for_submodules(
module_chain_to_containing_module_file(module, db)
.into_iter()
.filter_map(|module| module.name(db))
.try_fold(base_directory, |path, name| {
path.join(&name.unescaped().display_no_db().to_smolstr())
})
.try_fold(base_directory, |path, name| path.join(name.as_str()))
}
fn module_chain_to_containing_module_file(

View file

@ -60,7 +60,7 @@ pub(crate) fn complete_postfix(
None => return,
};
let cfg = ctx.config.import_path_config();
let cfg = ctx.config.import_path_config(ctx.is_nightly);
if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) {

View file

@ -52,8 +52,14 @@ pub(crate) fn complete_use_path(
)
};
for (name, def) in module_scope {
if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
continue;
if let (Some(attrs), Some(defining_crate)) =
(def.attrs(ctx.db), def.krate(ctx.db))
{
if !ctx.check_stability(Some(&attrs))
|| ctx.is_doc_hidden(&attrs, defining_crate)
{
continue;
}
}
let is_name_already_imported =
already_imported_names.contains(name.as_str());

View file

@ -14,6 +14,8 @@ pub struct CompletionConfig<'a> {
pub enable_postfix_completions: bool,
pub enable_imports_on_the_fly: bool,
pub enable_self_on_the_fly: bool,
pub enable_auto_iter: bool,
pub enable_auto_await: bool,
pub enable_private_editable: bool,
pub enable_term_search: bool,
pub term_search_fuel: u64,
@ -57,11 +59,12 @@ impl CompletionConfig<'_> {
.flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip)))
}
pub fn import_path_config(&self) -> ImportPathConfig {
pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig {
ImportPathConfig {
prefer_no_std: self.prefer_no_std,
prefer_prelude: self.prefer_prelude,
prefer_absolute: self.prefer_absolute,
allow_unstable,
}
}
}

View file

@ -443,7 +443,9 @@ pub(crate) struct CompletionContext<'a> {
/// The module of the `scope`.
pub(crate) module: hir::Module,
/// Whether nightly toolchain is used. Cached since this is looked up a lot.
is_nightly: bool,
pub(crate) is_nightly: bool,
/// The edition of the current crate
// FIXME: This should probably be the crate of the current token?
pub(crate) edition: Edition,
/// The expected name of what we are completing.
@ -532,7 +534,7 @@ impl CompletionContext<'_> {
}
}
/// Checks if an item is visible and not `doc(hidden)` at the completion site.
/// Checks if an item is visible, not `doc(hidden)` and stable at the completion site.
pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
where
I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
@ -568,6 +570,15 @@ impl CompletionContext<'_> {
!attrs.is_unstable() || self.is_nightly
}
pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
where
I: hir::HasAttrs + hir::HasCrate,
{
let defining_crate = item.krate(self.db);
let attrs = item.attrs(self.db);
self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate)
}
/// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
match trait_.attrs(self.db).lang() {
@ -645,6 +656,10 @@ impl CompletionContext<'_> {
attrs: &hir::Attrs,
defining_crate: hir::Crate,
) -> Visible {
if !self.check_stability(Some(attrs)) {
return Visible::No;
}
if !vis.is_visible_from(self.db, self.module.into()) {
if !self.config.enable_private_editable {
return Visible::No;
@ -664,7 +679,7 @@ impl CompletionContext<'_> {
}
}
fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
// `doc(hidden)` items are only completed within the defining crate.
self.krate != defining_crate && attrs.has_doc_hidden()
}

View file

@ -5,7 +5,7 @@ use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant};
use ide_db::{active_parameter::ActiveParameter, RootDatabase};
use itertools::Either;
use syntax::{
algo::{ancestors_at_offset, find_node_at_offset, non_trivia_sibling},
algo::{self, ancestors_at_offset, find_node_at_offset, non_trivia_sibling},
ast::{
self, AttrKind, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName,
NameOrNameRef,
@ -85,6 +85,11 @@ pub(super) fn expand_and_analyze(
})
}
fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option<SyntaxToken> {
let token = file.token_at_offset(offset).left_biased()?;
algo::skip_whitespace_token(token, Direction::Prev)
}
/// Expand attributes and macro calls at the current cursor position for both the original file
/// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
/// and speculative states stay in sync.
@ -125,9 +130,7 @@ fn expand(
// Left biased since there may already be an identifier token there, and we appended to it.
if !sema.might_be_inside_macro_call(&fake_ident_token)
&& original_file
.token_at_offset(original_offset + relative_offset)
.left_biased()
&& token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset)
.is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
{
// Recursion base case.
@ -143,9 +146,11 @@ fn expand(
let parent_item =
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset)
.and_then(|token| token.parent_ancestors().find_map(ast::Item::cast));
let ancestor_items = iter::successors(
Option::zip(
find_node_at_offset::<ast::Item>(&original_file, original_offset),
original_node,
find_node_at_offset::<ast::Item>(
&speculative_file,
fake_ident_token.text_range().start(),
@ -1590,11 +1595,11 @@ fn pattern_context_for(
}).map(|enum_| enum_.variants(sema.db))
})
}).map(|variants| variants.iter().filter_map(|variant| {
let variant_name = variant.name(sema.db).unescaped().display(sema.db).to_string();
let variant_name = variant.name(sema.db);
let variant_already_present = match_arm_list.arms().any(|arm| {
arm.pat().and_then(|pat| {
let pat_already_present = pat.syntax().to_string().contains(&variant_name);
let pat_already_present = pat.syntax().to_string().contains(variant_name.as_str());
pat_already_present.then_some(pat_already_present)
}).is_some()
});

View file

@ -82,8 +82,7 @@ pub struct CompletionItem {
pub ref_match: Option<(CompletionItemRefMode, TextSize)>,
/// The import data to add to completion's edits.
/// (ImportPath, LastSegment)
pub import_to_add: SmallVec<[(String, String); 1]>,
pub import_to_add: SmallVec<[String; 1]>,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
@ -181,6 +180,8 @@ pub struct CompletionRelevance {
pub postfix_match: Option<CompletionRelevancePostfixMatch>,
/// This is set for items that are function (associated or method)
pub function: Option<CompletionRelevanceFn>,
/// true when there is an `await.method()` or `iter().method()` completion.
pub is_skipping_completion: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct CompletionRelevanceTraitInfo {
@ -269,6 +270,7 @@ impl CompletionRelevance {
postfix_match,
trait_,
function,
is_skipping_completion,
} = self;
// only applicable for completions within use items
@ -296,6 +298,12 @@ impl CompletionRelevance {
score -= 5;
}
}
// Lower rank for completions that skip `await` and `iter()`.
if is_skipping_completion {
score -= 7;
}
// lower rank for items that need an import
if requires_import {
score -= 1;
@ -561,12 +569,7 @@ impl Builder {
let import_to_add = self
.imports_to_add
.into_iter()
.filter_map(|import| {
Some((
import.import_path.display(db, self.edition).to_string(),
import.import_path.segments().last()?.display(db, self.edition).to_string(),
))
})
.map(|import| import.import_path.display(db, self.edition).to_string())
.collect();
CompletionItem {

View file

@ -10,17 +10,13 @@ mod snippet;
#[cfg(test)]
mod tests;
use ide_db::text_edit::TextEdit;
use ide_db::{
helpers::mod_path_to_ast,
imports::{
import_assets::NameToImport,
insert_use::{self, ImportScope},
},
items_locator,
imports::insert_use::{self, ImportScope},
syntax_helpers::tree_diff::diff,
text_edit::TextEdit,
FilePosition, FxHashSet, RootDatabase,
};
use syntax::ast::make;
use crate::{
completions::Completions,
@ -272,7 +268,7 @@ pub fn resolve_completion_edits(
db: &RootDatabase,
config: &CompletionConfig<'_>,
FilePosition { file_id, offset }: FilePosition,
imports: impl IntoIterator<Item = (String, String)>,
imports: impl IntoIterator<Item = String>,
) -> Option<Vec<TextEdit>> {
let _p = tracing::info_span!("resolve_completion_edits").entered();
let sema = hir::Semantics::new(db);
@ -289,27 +285,12 @@ pub fn resolve_completion_edits(
let new_ast = scope.clone_for_update();
let mut import_insert = TextEdit::builder();
let cfg = config.import_path_config();
imports.into_iter().for_each(|(full_import_path, imported_name)| {
let items_with_name = items_locator::items_with_name(
&sema,
current_crate,
NameToImport::exact_case_sensitive(imported_name),
items_locator::AssocSearchMode::Include,
imports.into_iter().for_each(|full_import_path| {
insert_use::insert_use(
&new_ast,
make::path_from_text_with_edition(&full_import_path, current_edition),
&config.insert_use,
);
let import = items_with_name
.filter_map(|candidate| {
current_module.find_use_path(db, candidate, config.insert_use.prefix_kind, cfg)
})
.find(|mod_path| mod_path.display(db, current_edition).to_string() == full_import_path);
if let Some(import_path) = import {
insert_use::insert_use(
&new_ast,
mod_path_to_ast(&import_path, current_edition),
&config.insert_use,
);
}
});
diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert);

View file

@ -130,10 +130,8 @@ pub(crate) fn render_field(
let db = ctx.db();
let is_deprecated = ctx.is_deprecated(field);
let name = field.name(db);
let (name, escaped_name) = (
name.unescaped().display(db).to_smolstr(),
name.display_no_db(ctx.completion.edition).to_smolstr(),
);
let (name, escaped_name) =
(name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr());
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
@ -142,7 +140,8 @@ pub(crate) fn render_field(
);
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(ctx.completion, ty),
exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()),
exact_name_match: compute_exact_name_match(ctx.completion, &name),
is_skipping_completion: receiver.is_some(),
..CompletionRelevance::default()
});
item.detail(ty.display(db, ctx.completion.edition).to_string())
@ -215,6 +214,10 @@ pub(crate) fn render_tuple_field(
);
item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
.lookup_by(field.to_string());
item.set_relevance(CompletionRelevance {
is_skipping_completion: receiver.is_some(),
..ctx.completion_relevance()
});
item.build(ctx.db())
}
@ -298,7 +301,7 @@ pub(crate) fn render_expr(
.unwrap_or_else(|| String::from("..."))
};
let cfg = ctx.config.import_path_config();
let cfg = ctx.config.import_path_config(ctx.is_nightly);
let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?;
@ -512,7 +515,7 @@ fn render_resolution_simple_(
let mut item = CompletionItem::new(
kind,
ctx.source_range(),
local_name.unescaped().display(db).to_smolstr(),
local_name.as_str().to_smolstr(),
ctx.completion.edition,
);
item.set_relevance(ctx.completion_relevance())
@ -1335,6 +1338,7 @@ fn main() { let _: m::Spam = S$0 }
is_private_editable: false,
postfix_match: None,
function: None,
is_skipping_completion: false,
},
trigger_call_info: true,
},
@ -1364,6 +1368,7 @@ fn main() { let _: m::Spam = S$0 }
is_private_editable: false,
postfix_match: None,
function: None,
is_skipping_completion: false,
},
trigger_call_info: true,
},
@ -1453,6 +1458,7 @@ fn foo() { A { the$0 } }
is_private_editable: false,
postfix_match: None,
function: None,
is_skipping_completion: false,
},
},
]
@ -1511,6 +1517,7 @@ impl S {
return_type: Other,
},
),
is_skipping_completion: false,
},
},
CompletionItem {
@ -1653,6 +1660,7 @@ fn foo(s: S) { s.$0 }
return_type: Other,
},
),
is_skipping_completion: false,
},
},
]
@ -1864,6 +1872,7 @@ fn f() -> i32 {
is_private_editable: false,
postfix_match: None,
function: None,
is_skipping_completion: false,
},
},
]
@ -2624,6 +2633,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
return_type: Other,
},
),
is_skipping_completion: false,
},
ref_match: "&@107",
},
@ -2709,6 +2719,7 @@ fn foo() {
is_private_editable: false,
postfix_match: None,
function: None,
is_skipping_completion: false,
},
},
]
@ -2766,6 +2777,7 @@ fn main() {
return_type: Other,
},
),
is_skipping_completion: false,
},
ref_match: "&@92",
},
@ -3140,6 +3152,7 @@ fn main() {
is_private_editable: false,
postfix_match: None,
function: None,
is_skipping_completion: false,
},
},
CompletionItem {
@ -3173,6 +3186,7 @@ fn main() {
is_private_editable: false,
postfix_match: None,
function: None,
is_skipping_completion: false,
},
},
]

View file

@ -14,10 +14,8 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option
fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
let db = ctx.db();
let name = const_.name(db)?;
let (name, escaped_name) = (
name.unescaped().display(db).to_smolstr(),
name.display(db, ctx.completion.edition).to_smolstr(),
);
let (name, escaped_name) =
(name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr());
let detail = const_.display(db, ctx.completion.edition).to_string();
let mut item =

View file

@ -59,13 +59,10 @@ fn render(
let (call, escaped_call) = match &func_kind {
FuncKind::Method(_, Some(receiver)) => (
format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())),
format_smolstr!("{}.{}", receiver, name.as_str()),
format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)),
),
_ => (
name.unescaped().display(db).to_smolstr(),
name.display(db, completion.edition).to_smolstr(),
),
_ => (name.as_str().to_smolstr(), name.display(db, completion.edition).to_smolstr()),
};
let has_self_param = func.self_param(db).is_some();
let mut item = CompletionItem::new(
@ -126,6 +123,7 @@ fn render(
exact_name_match: compute_exact_name_match(completion, &call),
function,
trait_: trait_info,
is_skipping_completion: matches!(func_kind, FuncKind::Method(_, Some(_))),
..ctx.completion_relevance()
});
@ -151,7 +149,7 @@ fn render(
item.set_documentation(ctx.docs(func))
.set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
.detail(detail)
.lookup_by(name.unescaped().display(db).to_smolstr());
.lookup_by(name.as_str().to_smolstr());
if let Some((cap, (self_param, params))) = complete_call_parens {
add_call_parens(

View file

@ -75,7 +75,7 @@ fn render(
None => (name.clone().into(), name.into(), false),
};
let (qualified_name, escaped_qualified_name) = (
qualified_name.unescaped().display(ctx.db()).to_string(),
qualified_name.display_verbatim(ctx.db()).to_string(),
qualified_name.display(ctx.db(), completion.edition).to_string(),
);
let snippet_cap = ctx.snippet_cap();

View file

@ -46,21 +46,19 @@ fn render(
ctx.source_range()
};
let (name, escaped_name) = (
name.unescaped().display(ctx.db()).to_smolstr(),
name.display(ctx.db(), completion.edition).to_smolstr(),
);
let (name, escaped_name) =
(name.as_str(), name.display(ctx.db(), completion.edition).to_smolstr());
let docs = ctx.docs(macro_);
let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default();
let is_fn_like = macro_.is_fn_like(completion.db);
let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
let (bra, ket) = if is_fn_like { guess_macro_braces(name, docs_str) } else { ("", "") };
let needs_bang = is_fn_like && !is_use_path && !has_macro_bang;
let mut item = CompletionItem::new(
SymbolKind::from(macro_.kind(completion.db)),
source_range,
label(&ctx, needs_bang, bra, ket, &name),
label(&ctx, needs_bang, bra, ket, &name.to_smolstr()),
completion.edition,
);
item.set_deprecated(ctx.is_deprecated(macro_))
@ -71,11 +69,11 @@ fn render(
match ctx.snippet_cap() {
Some(cap) if needs_bang && !has_call_parens => {
let snippet = format!("{escaped_name}!{bra}$0{ket}");
let lookup = banged_name(&name);
let lookup = banged_name(name);
item.insert_snippet(cap, snippet).lookup_by(lookup);
}
_ if needs_bang => {
item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name));
item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(name));
}
_ => {
cov_mark::hit!(dont_insert_macro_call_parens_unnecessary);

View file

@ -31,13 +31,11 @@ pub(crate) fn render_struct_pat(
}
let name = local_name.unwrap_or_else(|| strukt.name(ctx.db()));
let (name, escaped_name) = (
name.unescaped().display(ctx.db()).to_smolstr(),
name.display(ctx.db(), ctx.completion.edition).to_smolstr(),
);
let (name, escaped_name) =
(name.as_str(), name.display(ctx.db(), ctx.completion.edition).to_smolstr());
let kind = strukt.kind(ctx.db());
let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap());
let lookup = format_literal_lookup(name.as_str(), kind);
let label = format_literal_label(name, kind, ctx.snippet_cap());
let lookup = format_literal_lookup(name, kind);
let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
let db = ctx.db();
@ -61,13 +59,13 @@ pub(crate) fn render_variant_pat(
let (name, escaped_name) = match path {
Some(path) => (
path.unescaped().display(ctx.db()).to_string().into(),
path.display(ctx.db(), ctx.completion.edition).to_string().into(),
path.display_verbatim(ctx.db()).to_smolstr(),
path.display(ctx.db(), ctx.completion.edition).to_smolstr(),
),
None => {
let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
let it = (
name.unescaped().display(ctx.db()).to_smolstr(),
name.as_str().to_smolstr(),
name.display(ctx.db(), ctx.completion.edition).to_smolstr(),
);
it

View file

@ -32,14 +32,11 @@ fn render(
let name = type_alias.name(db);
let (name, escaped_name) = if with_eq {
(
SmolStr::from_iter([&name.unescaped().display(db).to_smolstr(), " = "]),
SmolStr::from_iter([&name.as_str().to_smolstr(), " = "]),
SmolStr::from_iter([&name.display_no_db(ctx.completion.edition).to_smolstr(), " = "]),
)
} else {
(
name.unescaped().display(db).to_smolstr(),
name.display_no_db(ctx.completion.edition).to_smolstr(),
)
(name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr())
};
let detail = type_alias.display(db, ctx.completion.edition).to_string();

View file

@ -23,12 +23,12 @@ pub(crate) fn render_union_literal(
let (qualified_name, escaped_qualified_name) = match path {
Some(p) => (
p.unescaped().display(ctx.db()).to_string(),
p.display(ctx.db(), ctx.completion.edition).to_string(),
p.display_verbatim(ctx.db()).to_smolstr(),
p.display(ctx.db(), ctx.completion.edition).to_smolstr(),
),
None => (
name.unescaped().display(ctx.db()).to_string(),
name.display(ctx.db(), ctx.completion.edition).to_string(),
name.as_str().to_smolstr(),
name.display(ctx.db(), ctx.completion.edition).to_smolstr(),
),
};
let label = format_literal_label(

View file

@ -164,7 +164,7 @@ impl Snippet {
}
fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> {
let import_cfg = ctx.config.import_path_config();
let import_cfg = ctx.config.import_path_config(ctx.is_nightly);
let resolve = |import| {
let item = ctx.scope.resolve_mod_path(import).next()?;

View file

@ -87,6 +87,8 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
fields_to_resolve: CompletionFieldsToResolve::empty(),
exclude_flyimport: vec![],
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
};
pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {

View file

@ -1965,3 +1965,24 @@ fn bar() {
"#]],
);
}
#[test]
fn doc_hidden_enum_variant() {
check(
r#"
//- /foo.rs crate:foo
pub enum Enum {
#[doc(hidden)] Hidden,
Visible,
}
//- /lib.rs crate:lib deps:foo
fn foo() {
let _ = foo::Enum::$0;
}
"#,
expect![[r#"
ev Visible Visible
"#]],
);
}

View file

@ -1390,6 +1390,41 @@ pub struct FooStruct {}
);
}
#[test]
fn flyimport_pattern_unstable_path() {
check(
r#"
//- /main.rs crate:main deps:std
fn function() {
let foo$0
}
//- /std.rs crate:std
#[unstable]
pub mod unstable {
pub struct FooStruct {}
}
"#,
expect![""],
);
check(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
fn function() {
let foo$0
}
//- /std.rs crate:std
#[unstable]
pub mod unstable {
pub struct FooStruct {}
}
"#,
expect![[r#"
st FooStruct (use std::unstable::FooStruct)
"#]],
);
}
#[test]
fn flyimport_pattern_unstable_item_on_nightly() {
check(

View file

@ -451,3 +451,20 @@ marco_rules! m { () => {} }
"#]],
);
}
#[test]
fn use_tree_doc_hidden() {
check(
r#"
//- /foo.rs crate:foo
#[doc(hidden)] pub struct Hidden;
pub struct Visible;
//- /lib.rs crate:lib deps:foo
use foo::$0;
"#,
expect![[r#"
st Visible Visible
"#]],
);
}

View file

@ -794,7 +794,7 @@ impl NameRefClass {
hir::AssocItem::TypeAlias(it) => Some(it),
_ => None,
})
.find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str()))
.find(|alias| alias.name(sema.db).as_str() == name_ref.text().trim_start_matches("r#"))
{
// No substitution, this can only occur in type position.
return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None));

View file

@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> {
self.find_trait("core:cmp:Ord")
}
pub fn core_convert_FromStr(&self) -> Option<Trait> {
self.find_trait("core:str:FromStr")
}
pub fn core_convert_From(&self) -> Option<Trait> {
self.find_trait("core:convert:From")
}
@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> {
self.find_trait("core:convert:Into")
}
pub fn core_convert_TryFrom(&self) -> Option<Trait> {
self.find_trait("core:convert:TryFrom")
}
pub fn core_convert_TryInto(&self) -> Option<Trait> {
self.find_trait("core:convert:TryInto")
}
pub fn core_convert_Index(&self) -> Option<Trait> {
self.find_trait("core:ops:Index")
}
@ -130,6 +142,13 @@ impl FamousDefs<'_, '_> {
self.find_macro("core:unimplemented")
}
pub fn core_fmt_Display(&self) -> Option<Trait> {
self.find_trait("core:fmt:Display")
}
pub fn alloc_string_ToString(&self) -> Option<Trait> {
self.find_trait("alloc:string:ToString")
}
pub fn builtin_crates(&self) -> impl Iterator<Item = Crate> {
IntoIterator::into_iter([
self.std(),
@ -202,14 +221,15 @@ impl FamousDefs<'_, '_> {
for segment in path {
module = module.children(db).find_map(|child| {
let name = child.name(db)?;
if name.eq_ident(segment) {
if name.as_str() == segment {
Some(child)
} else {
None
}
})?;
}
let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1;
let def =
module.scope(db, None).into_iter().find(|(name, _def)| name.as_str() == trait_)?.1;
Some(def)
}
}

View file

@ -319,6 +319,7 @@ impl Ctx<'_> {
prefer_no_std: false,
prefer_prelude: true,
prefer_absolute: false,
allow_unstable: true,
};
let found_path = self.target_module.find_path(
self.source_scope.db.upcast(),
@ -378,6 +379,7 @@ impl Ctx<'_> {
prefer_no_std: false,
prefer_prelude: true,
prefer_absolute: false,
allow_unstable: true,
};
let found_path =
self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?;
@ -417,6 +419,7 @@ impl Ctx<'_> {
prefer_no_std: false,
prefer_prelude: true,
prefer_absolute: false,
allow_unstable: true,
};
let found_path = self.target_module.find_path(
self.source_scope.db.upcast(),

View file

@ -263,13 +263,12 @@ fn rename_mod(
// - Module has submodules defined in separate files
let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) {
// Go up one level since the anchor is inside the dir we're trying to rename
(true, _, Some(mod_name)) => Some((
format!("../{}", mod_name.unescaped().display(sema.db)),
format!("../{new_name}"),
)),
(true, _, Some(mod_name)) => {
Some((format!("../{}", mod_name.as_str()), format!("../{new_name}")))
}
// The anchor is on the same level as target dir
(false, true, Some(mod_name)) => {
Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned()))
Some((mod_name.as_str().to_owned(), new_name.to_owned()))
}
_ => None,
};

View file

@ -625,7 +625,7 @@ impl<'a> FindUsages<'a> {
let _p = tracing::info_span!("collect_possible_aliases").entered();
let db = sema.db;
let container_name = container.name(db).unescaped().display(db).to_smolstr();
let container_name = container.name(db).as_str().to_smolstr();
let search_scope = Definition::from(container).search_scope(db);
let mut seen = FxHashSet::default();
let mut completed = FxHashSet::default();
@ -925,12 +925,8 @@ impl<'a> FindUsages<'a> {
.or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
})
};
// We need to unescape the name in case it is written without "r#" in earlier
// editions of Rust where it isn't a keyword.
self.def
.name(sema.db)
.or_else(self_kw_refs)
.map(|it| it.unescaped().display(sema.db).to_smolstr())
// We need to search without the `r#`, hence `as_str` access.
self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.as_str().to_smolstr())
}
};
let name = match &name {

View file

@ -143,7 +143,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar
fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc<SymbolIndex> {
let _p = tracing::info_span!("module_symbols").entered();
Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module)))
Arc::new(SymbolIndex::new(SymbolCollector::new_module(db.upcast(), module)))
}
pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolIndex>]> {
@ -284,13 +284,15 @@ impl SymbolIndex {
builder.insert(key, value).unwrap();
}
// FIXME: fst::Map should ideally have a way to shrink the backing buffer without the unwrap dance
let map = fst::Map::new({
let mut buf = builder.into_inner().unwrap();
buf.shrink_to_fit();
buf
})
.unwrap();
let map = builder
.into_inner()
.and_then(|mut buf| {
fst::Map::new({
buf.shrink_to_fit();
buf
})
})
.unwrap();
SymbolIndex { symbols, map }
}
@ -491,7 +493,7 @@ pub(self) use crate::Trait as IsThisJustATrait;
.modules(&db)
.into_iter()
.map(|module_id| {
let mut symbols = SymbolCollector::collect_module(&db, module_id);
let mut symbols = SymbolCollector::new_module(&db, module_id);
symbols.sort_by_key(|it| it.name.as_str().to_owned());
(module_id, symbols)
})
@ -518,7 +520,7 @@ struct Duplicate;
.modules(&db)
.into_iter()
.map(|module_id| {
let mut symbols = SymbolCollector::collect_module(&db, module_id);
let mut symbols = SymbolCollector::new_module(&db, module_id);
symbols.sort_by_key(|it| it.name.as_str().to_owned());
(module_id, symbols)
})

View file

@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"];
/// `Result<User, Error>` -> `User`
const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"];
/// Generic types replaced by a plural of their first argument.
///
/// # Examples
/// `Vec<Name>` -> "names"
const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"];
/// Prefixes to strip from methods names
///
/// # Examples
@ -378,6 +384,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
return name_of_type(&inner_ty, db, edition);
}
if SEQUENCE_TYPES.contains(&name.as_str()) {
let inner_ty = ty.type_arguments().next();
return Some(sequence_name(inner_ty.as_ref(), db, edition));
}
name
} else if let Some(trait_) = ty.as_dyn_trait() {
trait_name(&trait_, db, edition)?
@ -390,12 +401,32 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
name
} else if let Some(inner_ty) = ty.remove_ref() {
return name_of_type(&inner_ty, db, edition);
} else if let Some(inner_ty) = ty.as_slice() {
return Some(sequence_name(Some(&inner_ty), db, edition));
} else {
return None;
};
normalize(&name)
}
fn sequence_name(inner_ty: Option<&hir::Type>, db: &RootDatabase, edition: Edition) -> SmolStr {
let items_str = SmolStr::new_static("items");
let Some(inner_ty) = inner_ty else {
return items_str;
};
let Some(name) = name_of_type(inner_ty, db, edition) else {
return items_str;
};
if name.ends_with(['s', 'x', 'y']) {
// Given a type called e.g. "Boss", "Fox" or "Story", don't try to
// create a plural.
items_str
} else {
SmolStr::new(format!("{name}s"))
}
}
fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option<String> {
let name = trait_.name(db).display(db, edition).to_string();
if USELESS_TRAITS.contains(&name.as_str()) {
@ -897,6 +928,58 @@ fn foo() { $0(bar())$0; }
);
}
#[test]
fn vec_value() {
check(
r#"
struct Vec<T> {};
struct Seed;
fn bar() -> Vec<Seed> {}
fn foo() { $0(bar())$0; }
"#,
"seeds",
);
}
#[test]
fn vec_value_ends_with_s() {
check(
r#"
struct Vec<T> {};
struct Boss;
fn bar() -> Vec<Boss> {}
fn foo() { $0(bar())$0; }
"#,
"items",
);
}
#[test]
fn vecdeque_value() {
check(
r#"
struct VecDeque<T> {};
struct Seed;
fn bar() -> VecDeque<Seed> {}
fn foo() { $0(bar())$0; }
"#,
"seeds",
);
}
#[test]
fn slice_value() {
check(
r#"
struct Vec<T> {};
struct Seed;
fn bar() -> &[Seed] {}
fn foo() { $0(bar())$0; }
"#,
"seeds",
);
}
#[test]
fn ref_call() {
check(

View file

@ -1007,6 +1007,39 @@
is_alias: false,
is_assoc: false,
},
FileSymbol {
name: "ThisStruct",
def: Adt(
Struct(
Struct {
id: StructId(
4,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: EditionedFileId(
FileId(
1,
),
Edition2021,
),
ptr: SyntaxNodePtr {
kind: USE_TREE,
range: 85..125,
},
name_ptr: AstPtr(
SyntaxNodePtr {
kind: NAME,
range: 115..125,
},
),
},
container_name: None,
is_alias: false,
is_assoc: false,
},
],
),
]

View file

@ -26,7 +26,7 @@ impl TryEnum {
_ => return None,
};
TryEnum::ALL.iter().find_map(|&var| {
if enum_.name(sema.db).eq_ident(var.type_name()) {
if enum_.name(sema.db).as_str() == var.type_name() {
return Some(var);
}
None

View file

@ -147,6 +147,7 @@ pub(crate) fn json_in_items(
prefer_no_std: config.prefer_no_std,
prefer_prelude: config.prefer_prelude,
prefer_absolute: config.prefer_absolute,
allow_unstable: true,
};
if !scope_has("Serialize") {

View file

@ -128,6 +128,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
allow_unstable: ctx.is_nightly,
},
)?;

View file

@ -70,6 +70,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
allow_unstable: ctx.is_nightly,
},
ctx.edition,
)

View file

@ -112,7 +112,7 @@ fn fixes(
// shouldn't occur
_ => continue 'crates,
};
match current.children.iter().find(|(name, _)| name.eq_ident(seg)) {
match current.children.iter().find(|(name, _)| name.as_str() == seg) {
Some((_, &child)) => current = &crate_def_map[child],
None => continue 'crates,
}
@ -161,7 +161,7 @@ fn fixes(
// try finding a parent that has an inline tree from here on
let mut current = module;
for s in stack.iter().rev() {
match module.children.iter().find(|(name, _)| name.eq_ident(s)) {
match module.children.iter().find(|(name, _)| name.as_str() == s) {
Some((_, child)) => {
current = &crate_def_map[*child];
}

View file

@ -83,7 +83,7 @@ use either::Either;
use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics};
use ide_db::{
assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
base_db::SourceDatabase,
base_db::{ReleaseChannel, SourceDatabase},
generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS},
imports::insert_use::InsertUseConfig,
label::Label,
@ -276,6 +276,7 @@ struct DiagnosticsContext<'a> {
sema: Semantics<'a, RootDatabase>,
resolve: &'a AssistResolveStrategy,
edition: Edition,
is_nightly: bool,
}
impl DiagnosticsContext<'_> {
@ -368,7 +369,11 @@ pub fn semantic_diagnostics(
let module = sema.file_to_module_def(file_id);
let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition() };
let is_nightly = matches!(
module.and_then(|m| db.toolchain_channel(m.krate().into())),
Some(ReleaseChannel::Nightly) | None
);
let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly };
let mut diags = Vec::new();
match module {

View file

@ -673,6 +673,7 @@ impl Match {
prefer_no_std: false,
prefer_prelude: true,
prefer_absolute: false,
allow_unstable: true,
};
let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| {
match_error!("Failed to render template path `{}` at match location")

View file

@ -413,8 +413,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> {
def.canonical_module_path(db).map(|it| {
let mut path = String::new();
it.flat_map(|it| it.name(db))
.for_each(|name| format_to!(path, "{}/", name.unescaped().display(db)));
it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.as_str()));
path
})
}
@ -590,10 +589,10 @@ fn filename_and_frag_for_def(
let res = match def {
Definition::Adt(adt) => match adt {
Adt::Struct(s) => {
format!("struct.{}.html", s.name(db).unescaped().display(db.upcast()))
format!("struct.{}.html", s.name(db).as_str())
}
Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())),
Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())),
Adt::Enum(e) => format!("enum.{}.html", e.name(db).as_str()),
Adt::Union(u) => format!("union.{}.html", u.name(db).as_str()),
},
Definition::Crate(_) => String::from("index.html"),
Definition::Module(m) => match m.name(db) {
@ -603,48 +602,48 @@ fn filename_and_frag_for_def(
Some(kw) => {
format!("keyword.{}.html", kw)
}
None => format!("{}/index.html", name.unescaped().display(db.upcast())),
None => format!("{}/index.html", name.as_str()),
}
}
None => String::from("index.html"),
},
Definition::Trait(t) => {
format!("trait.{}.html", t.name(db).unescaped().display(db.upcast()))
format!("trait.{}.html", t.name(db).as_str())
}
Definition::TraitAlias(t) => {
format!("traitalias.{}.html", t.name(db).unescaped().display(db.upcast()))
format!("traitalias.{}.html", t.name(db).as_str())
}
Definition::TypeAlias(t) => {
format!("type.{}.html", t.name(db).unescaped().display(db.upcast()))
format!("type.{}.html", t.name(db).as_str())
}
Definition::BuiltinType(t) => {
format!("primitive.{}.html", t.name().unescaped().display(db.upcast()))
format!("primitive.{}.html", t.name().as_str())
}
Definition::Function(f) => {
format!("fn.{}.html", f.name(db).unescaped().display(db.upcast()))
format!("fn.{}.html", f.name(db).as_str())
}
Definition::Variant(ev) => {
format!(
"enum.{}.html#variant.{}",
ev.parent_enum(db).name(db).unescaped().display(db.upcast()),
ev.name(db).unescaped().display(db.upcast())
ev.parent_enum(db).name(db).as_str(),
ev.name(db).as_str()
)
}
Definition::Const(c) => {
format!("const.{}.html", c.name(db)?.unescaped().display(db.upcast()))
format!("const.{}.html", c.name(db)?.as_str())
}
Definition::Static(s) => {
format!("static.{}.html", s.name(db).unescaped().display(db.upcast()))
format!("static.{}.html", s.name(db).as_str())
}
Definition::Macro(mac) => match mac.kind(db) {
hir::MacroKind::Declarative
| hir::MacroKind::BuiltIn
| hir::MacroKind::Attr
| hir::MacroKind::ProcMacro => {
format!("macro.{}.html", mac.name(db).unescaped().display(db.upcast()))
format!("macro.{}.html", mac.name(db).as_str())
}
hir::MacroKind::Derive => {
format!("derive.{}.html", mac.name(db).unescaped().display(db.upcast()))
format!("derive.{}.html", mac.name(db).as_str())
}
},
Definition::Field(field) => {
@ -654,11 +653,7 @@ fn filename_and_frag_for_def(
hir::VariantDef::Variant(it) => Definition::Variant(it),
};
let (_, file, _) = filename_and_frag_for_def(db, def)?;
return Some((
def,
file,
Some(format!("structfield.{}", field.name(db).unescaped().display(db.upcast()))),
));
return Some((def, file, Some(format!("structfield.{}", field.name(db).as_str()))));
}
Definition::SelfType(impl_) => {
let adt = impl_.self_ty(db).as_adt()?.into();
@ -667,7 +662,7 @@ fn filename_and_frag_for_def(
return Some((adt, file, Some(String::from("impl"))));
}
Definition::ExternCrateDecl(it) => {
format!("{}/index.html", it.name(db).unescaped().display(db.upcast()))
format!("{}/index.html", it.name(db).as_str())
}
Definition::Local(_)
| Definition::GenericParam(_)
@ -699,16 +694,16 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) ->
// Rustdoc makes this decision based on whether a method 'has defaultness'.
// Currently this is only the case for provided trait methods.
if is_trait_method && !function.has_body(db) {
format!("tymethod.{}", function.name(db).unescaped().display(db.upcast()))
format!("tymethod.{}", function.name(db).as_str())
} else {
format!("method.{}", function.name(db).unescaped().display(db.upcast()))
format!("method.{}", function.name(db).as_str())
}
}
AssocItem::Const(constant) => {
format!("associatedconstant.{}", constant.name(db)?.unescaped().display(db.upcast()))
format!("associatedconstant.{}", constant.name(db)?.as_str())
}
AssocItem::TypeAlias(ty) => {
format!("associatedtype.{}", ty.name(db).unescaped().display(db.upcast()))
format!("associatedtype.{}", ty.name(db).as_str())
}
})
}

View file

@ -5,10 +5,14 @@ use crate::{
navigation_target::{self, ToNav},
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
};
use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
use hir::{
sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt,
ModuleDef, Semantics,
};
use ide_db::{
base_db::{AnchoredPath, FileLoader, SourceDatabase},
defs::{Definition, IdentClass},
famous_defs::FamousDefs,
helpers::pick_best_token,
RootDatabase, SymbolKind,
};
@ -129,15 +133,74 @@ pub(crate) fn goto_definition(
Some(RangeInfo::new(original_token.text_range(), navs))
}
// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr.
// If the token is into(), try_into(), search the definition of From, TryFrom.
fn find_definition_for_known_blanket_dual_impls(
sema: &Semantics<'_, RootDatabase>,
original_token: &SyntaxToken,
) -> Option<Vec<NavigationTarget>> {
let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;
let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?;
let callable = sema.resolve_method_call_as_callable(&method_call)?;
let CallableKind::Function(f) = callable.kind() else { return None };
let assoc = f.as_assoc_item(sema.db)?;
let def = Definition::from(target_method);
let return_type = callable.return_type();
let fd = FamousDefs(sema, return_type.krate(sema.db));
let t = match assoc.container(sema.db) {
hir::AssocItemContainer::Trait(t) => t,
hir::AssocItemContainer::Impl(impl_)
if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse =>
{
let t = fd.core_convert_FromStr()?;
let t_f = t.function(sema.db, &sym::from_str)?;
return sema
.resolve_trait_impl_method(
return_type.clone(),
t,
t_f,
[return_type.type_arguments().next()?],
)
.map(|f| def_to_nav(sema.db, f.into()));
}
hir::AssocItemContainer::Impl(_) => return None,
};
let fn_name = f.name(sema.db);
let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) {
let dual = fd.core_convert_From()?;
let dual_f = dual.function(sema.db, &sym::from)?;
sema.resolve_trait_impl_method(
return_type.clone(),
dual,
dual_f,
[return_type, callable.receiver_param(sema.db)?.1],
)?
} else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) {
let dual = fd.core_convert_TryFrom()?;
let dual_f = dual.function(sema.db, &sym::try_from)?;
sema.resolve_trait_impl_method(
return_type.clone(),
dual,
dual_f,
// Extract the `T` from `Result<T, ..>`
[return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1],
)?
} else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) {
let dual = fd.core_fmt_Display()?;
let dual_f = dual.function(sema.db, &sym::fmt)?;
sema.resolve_trait_impl_method(
return_type.clone(),
dual,
dual_f,
[callable.receiver_param(sema.db)?.1.strip_reference()],
)?
} else {
return None;
};
// Assert that we got a trait impl function, if we are back in a trait definition we didn't
// succeed
let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?;
let def = Definition::from(f);
Some(def_to_nav(sema.db, def))
}
@ -3168,18 +3231,45 @@ fn f() {
r#"
//- minicore: from, str
struct A;
impl FromStr for A {
type Error = String;
fn from_str(value: &str) -> Result<Self, Self::Error> {
//^^^^^^^^
Ok(A)
}
}
fn f() {
let a: Result<A, _> = "aaaaaa".parse$0();
}
"#,
);
}
#[test]
fn to_string_call_to_display_definition() {
check(
r#"
//- minicore:fmt
//- /alloc.rs crate:alloc
pub mod string {
pub struct String;
pub trait ToString {
fn to_string(&self) -> String;
}
impl<T: core::fmt::Display> ToString for T {
fn to_string(&self) -> String { String }
}
}
//- /lib.rs crate:lib deps:alloc
use alloc::string::ToString;
struct A;
impl core::fmt::Display for A {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {}
// ^^^
}
fn f() {
A.to_string$0();
}
"#,
);

View file

@ -346,7 +346,7 @@ fn hover_offset(
.unique()
.reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
acc.actions.extend(actions);
acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup));
acc.markup = Markup::from(format!("{}\n\n---\n{markup}", acc.markup));
acc
})
.map(|mut res: HoverResult| {

View file

@ -1082,7 +1082,19 @@ fn render_memory_layout(
if config.niches {
if let Some(niches) = layout.niches() {
format_to!(label, "niches = {niches}, ");
if niches > 1024 {
if niches.is_power_of_two() {
format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches));
} else if is_pwr2plus1(niches) {
format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1));
} else if is_pwr2minus1(niches) {
format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1));
} else {
format_to!(label, "niches = a lot, ");
}
} else {
format_to!(label, "niches = {niches}, ");
}
}
}
label.pop(); // ' '
@ -1210,3 +1222,74 @@ fn render_dyn_compatibility(
}
}
}
fn is_pwr2minus1(val: u128) -> bool {
val == u128::MAX || (val + 1).is_power_of_two()
}
fn is_pwr2plus1(val: u128) -> bool {
val != 0 && (val - 1).is_power_of_two()
}
/// Formats a power of two as an exponent of two, i.e. 16 => ⁴. Note that `num` MUST be a power
/// of 2, or this function will panic.
fn pwr2_to_exponent(num: u128) -> String {
const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
assert_eq!(num.count_ones(), 1);
num.trailing_zeros()
.to_string()
.chars()
.map(|c| c.to_digit(10).unwrap() as usize)
.map(|idx| DIGITS[idx])
.collect::<String>()
}
#[cfg(test)]
mod tests {
use super::*;
const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX];
#[test]
fn test_is_pwr2minus1() {
const OUTCOMES: [bool; 10] =
[true, true, false, true, false, true, false, false, false, true];
for (test, expected) in TESTERS.iter().zip(OUTCOMES) {
let actual = is_pwr2minus1(*test);
assert_eq!(actual, expected, "is_pwr2minu1({test}) gave {actual}, expected {expected}");
}
}
#[test]
fn test_is_pwr2plus1() {
const OUTCOMES: [bool; 10] =
[false, false, true, true, false, false, false, true, false, false];
for (test, expected) in TESTERS.iter().zip(OUTCOMES) {
let actual = is_pwr2plus1(*test);
assert_eq!(actual, expected, "is_pwr2plus1({test}) gave {actual}, expected {expected}");
}
}
#[test]
fn test_pwr2_to_exponent() {
const TESTERS: [u128; 9] = [
1,
2,
4,
8,
16,
9223372036854775808,
18446744073709551616,
36893488147419103232,
170141183460469231731687303715884105728,
];
const OUTCOMES: [&str; 9] = ["", "¹", "²", "³", "", "⁶³", "⁶⁴", "⁶⁵", "¹²⁷"];
for (test, expected) in TESTERS.iter().zip(OUTCOMES) {
let actual = pwr2_to_exponent(*test);
assert_eq!(
actual, expected,
"pwr2_to_exponent({test}) returned {actual}, expected {expected}",
);
}
}
}

View file

@ -303,6 +303,7 @@ m!(ab$0c);
---
Outer
---
```rust
@ -1357,7 +1358,7 @@ fn hover_enum_limit() {
---
size = 12 (0xC), align = 4, niches = 4294967288
size = 12 (0xC), align = 4, niches = a lot
"#]],
);
}
@ -4401,6 +4402,7 @@ fn main() {
---
size = 8, align = 8, niches = 1
---
```rust
@ -10094,6 +10096,7 @@ fn bar() {
```rust
let field: i32
```
---
```rust
@ -10128,6 +10131,7 @@ fn bar() {
---
size = 4, align = 4
---
```rust

View file

@ -209,7 +209,7 @@ fn hints(
) {
closing_brace::hints(hints, sema, config, file_id, node.clone());
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
generic_param::hints(hints, sema, config, any_has_generic_args);
generic_param::hints(hints, famous_defs, config, any_has_generic_args);
}
match_ast! {
@ -300,22 +300,23 @@ pub struct InlayHintsConfig {
pub closing_brace_hints_min_lines: Option<usize>,
pub fields_to_resolve: InlayFieldsToResolve,
}
impl InlayHintsConfig {
fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy<TextEdit> {
fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty<TextEdit> {
if self.fields_to_resolve.resolve_text_edits {
Lazy::Lazy
LazyProperty::Lazy
} else {
let edit = finish();
never!(edit.is_empty(), "inlay hint produced an empty text edit");
Lazy::Computed(edit)
LazyProperty::Computed(edit)
}
}
fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy<InlayTooltip> {
fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> LazyProperty<InlayTooltip> {
if self.fields_to_resolve.resolve_hint_tooltip
&& self.fields_to_resolve.resolve_label_tooltip
{
Lazy::Lazy
LazyProperty::Lazy
} else {
let tooltip = finish();
never!(
@ -326,7 +327,20 @@ impl InlayHintsConfig {
.is_empty(),
"inlay hint produced an empty tooltip"
);
Lazy::Computed(tooltip)
LazyProperty::Computed(tooltip)
}
}
/// This always reports a resolvable location, so only use this when it is very likely for a
/// location link to actually resolve but where computing `finish` would be costly.
fn lazy_location_opt(
&self,
finish: impl FnOnce() -> Option<FileRange>,
) -> Option<LazyProperty<FileRange>> {
if self.fields_to_resolve.resolve_label_location {
Some(LazyProperty::Lazy)
} else {
finish().map(LazyProperty::Computed)
}
}
}
@ -441,7 +455,7 @@ pub struct InlayHint {
/// The actual label to show in the inlay hint.
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<Lazy<TextEdit>>,
pub text_edit: Option<LazyProperty<TextEdit>>,
/// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the
/// hint does not support resolving.
pub resolve_parent: Option<TextRange>,
@ -449,15 +463,15 @@ pub struct InlayHint {
/// A type signaling that a value is either computed, or is available for computation.
#[derive(Clone, Debug)]
pub enum Lazy<T> {
pub enum LazyProperty<T> {
Computed(T),
Lazy,
}
impl<T> Lazy<T> {
impl<T> LazyProperty<T> {
pub fn computed(self) -> Option<T> {
match self {
Lazy::Computed(it) => Some(it),
LazyProperty::Computed(it) => Some(it),
_ => None,
}
}
@ -508,8 +522,8 @@ pub struct InlayHintLabel {
impl InlayHintLabel {
pub fn simple(
s: impl Into<String>,
tooltip: Option<Lazy<InlayTooltip>>,
linked_location: Option<FileRange>,
tooltip: Option<LazyProperty<InlayTooltip>>,
linked_location: Option<LazyProperty<FileRange>>,
) -> InlayHintLabel {
InlayHintLabel {
parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }],
@ -593,16 +607,16 @@ pub struct InlayHintLabelPart {
/// refers to (not necessarily the location itself).
/// When setting this, no tooltip must be set on the containing hint, or VS Code will display
/// them both.
pub linked_location: Option<FileRange>,
pub linked_location: Option<LazyProperty<FileRange>>,
/// The tooltip to show when hovering over the inlay hint, this may invoke other actions like
/// hover requests to show.
pub tooltip: Option<Lazy<InlayTooltip>>,
pub tooltip: Option<LazyProperty<InlayTooltip>>,
}
impl std::hash::Hash for InlayHintLabelPart {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.text.hash(state);
self.linked_location.hash(state);
self.linked_location.is_some().hash(state);
self.tooltip.is_some().hash(state);
}
}
@ -610,7 +624,9 @@ impl std::hash::Hash for InlayHintLabelPart {
impl fmt::Debug for InlayHintLabelPart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f),
Self { text, linked_location: None, tooltip: None | Some(LazyProperty::Lazy) } => {
text.fmt(f)
}
Self { text, linked_location, tooltip } => f
.debug_struct("InlayHintLabelPart")
.field("text", text)
@ -618,8 +634,10 @@ impl fmt::Debug for InlayHintLabelPart {
.field(
"tooltip",
&tooltip.as_ref().map_or("", |it| match it {
Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it,
Lazy::Lazy => "",
LazyProperty::Computed(
InlayTooltip::String(it) | InlayTooltip::Markdown(it),
) => it,
LazyProperty::Lazy => "",
}),
)
.finish(),
@ -632,7 +650,8 @@ struct InlayHintLabelBuilder<'a> {
db: &'a RootDatabase,
result: InlayHintLabel,
last_part: String,
location: Option<FileRange>,
resolve: bool,
location: Option<LazyProperty<FileRange>>,
}
impl fmt::Write for InlayHintLabelBuilder<'_> {
@ -645,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> {
fn start_location_link(&mut self, def: ModuleDefId) {
never!(self.location.is_some(), "location link is already started");
self.make_new_part();
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
let location = location.call_site();
let location =
FileRange { file_id: location.file_id, range: location.focus_or_full_range() };
self.location = Some(location);
self.location = Some(if self.resolve {
LazyProperty::Lazy
} else {
LazyProperty::Computed({
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
let location = location.call_site();
FileRange { file_id: location.file_id, range: location.focus_or_full_range() }
})
});
}
fn end_location_link(&mut self) {
@ -735,6 +759,7 @@ fn label_of_ty(
last_part: String::new(),
location: None,
result: InlayHintLabel::default(),
resolve: config.fields_to_resolve.resolve_label_location,
};
let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition);
let r = label_builder.finish();
@ -783,7 +808,7 @@ fn ty_to_text_edit(
ty: &hir::Type,
offset_to_insert: TextSize,
prefix: impl Into<String>,
) -> Option<Lazy<TextEdit>> {
) -> Option<LazyProperty<TextEdit>> {
// FIXME: Limit the length and bail out on excess somehow?
let rendered = sema
.scope(node_for_hint)

View file

@ -1203,6 +1203,40 @@ fn f5<G: T<Assoc = ()>>(it: G) {
let l = it.f();
//^ ()
}
"#,
);
}
#[test]
fn regression_19007() {
check_types(
r#"
trait Foo {
type Assoc;
fn foo(&self) -> Self::Assoc;
}
trait Bar {
type Target;
}
trait Baz<T> {}
struct Struct<T: Foo> {
field: T,
}
impl<T> Struct<T>
where
T: Foo,
T::Assoc: Baz<<T::Assoc as Bar>::Target> + Bar,
{
fn f(&self) {
let x = self.field.foo();
//^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar
}
}
"#,
);
}

View file

@ -22,11 +22,7 @@ pub(super) fn hints(
return None;
}
let linked_location =
famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| {
let n = it.call_site();
FileRange { file_id: n.file_id, range: n.focus_or_full_range() }
});
let sized_trait = famous_defs.core_marker_Sized();
for param in params.type_or_const_params() {
match param {
@ -48,7 +44,17 @@ pub(super) fn hints(
}
hint.parts.push(InlayHintLabelPart {
text: "Sized".to_owned(),
linked_location,
linked_location: sized_trait.and_then(|it| {
config.lazy_location_opt(|| {
it.try_to_nav(sema.db).map(|it| {
let n = it.call_site();
FileRange {
file_id: n.file_id,
range: n.focus_or_full_range(),
}
})
})
}),
tooltip: None,
});
if has_bounds {
@ -134,12 +140,14 @@ fn foo<T>() {}
InlayHintLabelPart {
text: "Sized",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 135..140,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 135..140,
},
),
),
tooltip: "",
},

View file

@ -81,7 +81,10 @@ mod tests {
use crate::{
fixture,
inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
inlay_hints::{
tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
LazyProperty,
},
InlayHintsConfig,
};
@ -99,7 +102,7 @@ mod tests {
let (analysis, file_id) = fixture::file(ra_fixture);
let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| {
if let Some(loc) = &mut hint.linked_location {
if let Some(LazyProperty::Computed(loc)) = &mut hint.linked_location {
loc.range = TextRange::empty(TextSize::from(0));
}
});
@ -134,12 +137,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 63..64,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 63..64,
},
),
),
tooltip: "",
},
@ -151,12 +156,14 @@ fn main() {
InlayHintLabelPart {
text: "A",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..8,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..8,
},
),
),
tooltip: "",
},
@ -213,12 +220,14 @@ fn main() {
InlayHintLabelPart {
text: "C",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 51..52,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 51..52,
},
),
),
tooltip: "",
},
@ -230,12 +239,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 29..30,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 29..30,
},
),
),
tooltip: "",
},
@ -276,12 +287,14 @@ fn main() {
InlayHintLabelPart {
text: "C",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 51..52,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 51..52,
},
),
),
tooltip: "",
},
@ -293,12 +306,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 29..30,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 29..30,
},
),
),
tooltip: "",
},
@ -340,12 +355,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 23..24,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 23..24,
},
),
),
tooltip: "",
},
@ -353,12 +370,14 @@ fn main() {
InlayHintLabelPart {
text: "X",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 55..56,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 55..56,
},
),
),
tooltip: "",
},
@ -371,12 +390,14 @@ fn main() {
InlayHintLabelPart {
text: "A",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..8,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..8,
},
),
),
tooltip: "",
},
@ -384,12 +405,14 @@ fn main() {
InlayHintLabelPart {
text: "X",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 55..56,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 55..56,
},
),
),
tooltip: "",
},
@ -435,12 +458,14 @@ fn main() {
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
),
),
tooltip: "",
},
@ -448,12 +473,14 @@ fn main() {
InlayHintLabelPart {
text: "Item",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
),
),
tooltip: "",
},
@ -467,12 +494,14 @@ fn main() {
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
),
),
tooltip: "",
},
@ -480,12 +509,14 @@ fn main() {
InlayHintLabelPart {
text: "Item",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
),
),
tooltip: "",
},
@ -499,12 +530,14 @@ fn main() {
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
),
),
tooltip: "",
},
@ -512,12 +545,14 @@ fn main() {
InlayHintLabelPart {
text: "Item",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 0..0,
},
),
),
tooltip: "",
},
@ -531,12 +566,14 @@ fn main() {
InlayHintLabelPart {
text: "MyIter",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 0..0,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 0..0,
},
),
),
tooltip: "",
},
@ -577,12 +614,14 @@ fn main() {
InlayHintLabelPart {
text: "Struct",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..13,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..13,
},
),
),
tooltip: "",
},
@ -594,12 +633,14 @@ fn main() {
InlayHintLabelPart {
text: "Struct",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..13,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..13,
},
),
),
tooltip: "",
},
@ -611,12 +652,14 @@ fn main() {
InlayHintLabelPart {
text: "Struct",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..13,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..13,
},
),
),
tooltip: "",
},
@ -628,12 +671,14 @@ fn main() {
InlayHintLabelPart {
text: "self",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 42..46,
},
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 42..46,
},
),
),
tooltip: "",
},

View file

@ -11,7 +11,10 @@ use syntax::{
match_ast, SyntaxKind, SyntaxNode, T,
};
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
use crate::{
inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig,
InlayKind,
};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@ -141,7 +144,7 @@ pub(super) fn hints(
acc.push(InlayHint {
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location),
label: InlayHintLabel::simple(label, None, linked_location.map(LazyProperty::Computed)),
text_edit: None,
position: InlayHintPosition::After,
pad_left: true,

View file

@ -53,10 +53,6 @@ pub(super) fn hints(
let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() {
let local = capture.local();
let source = local.primary_source(sema.db);
// force cache the source file, otherwise sema lookup will potentially panic
_ = sema.parse_or_expand(source.file());
let label = format!(
"{}{}",
@ -73,8 +69,17 @@ pub(super) fn hints(
}
hint.label.append_part(InlayHintLabelPart {
text: label,
linked_location: source.name().and_then(|name| {
name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into)
linked_location: config.lazy_location_opt(|| {
let source = local.primary_source(sema.db);
// force cache the source file, otherwise sema lookup will potentially panic
_ = sema.parse_or_expand(source.file());
source.name().and_then(|name| {
name.syntax()
.original_file_range_opt(sema.db)
.map(TupleExt::head)
.map(Into::into)
})
}),
tooltip: None,
});

View file

@ -1,17 +1,19 @@
//! Implementation of inlay hints for generic parameters.
use ide_db::{active_parameter::generic_def_for_node, RootDatabase};
use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs};
use syntax::{
ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName},
AstNode,
};
use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind};
use crate::{
inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
};
use super::param_name::{is_argument_similar_to_param_name, render_label};
use super::param_name::is_argument_similar_to_param_name;
pub(crate) fn hints(
acc: &mut Vec<InlayHint>,
sema: &hir::Semantics<'_, RootDatabase>,
FamousDefs(sema, krate): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
node: AnyHasGenericArgs,
) -> Option<()> {
@ -45,12 +47,23 @@ pub(crate) fn hints(
return None;
}
let name = param.name(sema.db);
let param_name = name.as_str();
let allowed = match (param, &arg) {
(hir::GenericParam::TypeParam(_), ast::GenericArg::TypeArg(_)) => type_hints,
(hir::GenericParam::ConstParam(_), ast::GenericArg::ConstArg(_)) => const_hints,
(hir::GenericParam::LifetimeParam(_), ast::GenericArg::LifetimeArg(_)) => {
lifetime_hints
}
_ => false,
};
if !allowed {
return None;
}
let param_name = param.name(sema.db);
let should_hide = {
let argument = get_string_representation(&arg)?;
is_argument_similar_to_param_name(&argument, param_name)
is_argument_similar_to_param_name(&argument, param_name.as_str())
};
if should_hide {
@ -59,30 +72,28 @@ pub(crate) fn hints(
let range = sema.original_range_opt(arg.syntax())?.range;
let source_syntax = match param {
hir::GenericParam::TypeParam(it) => {
if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) {
return None;
}
sema.source(it.merge())?.value.syntax().clone()
}
hir::GenericParam::ConstParam(it) => {
if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) {
return None;
}
let syntax = sema.source(it.merge())?.value.syntax().clone();
let const_param = ast::ConstParam::cast(syntax)?;
const_param.name()?.syntax().clone()
}
hir::GenericParam::LifetimeParam(it) => {
if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) {
return None;
}
sema.source(it)?.value.syntax().clone()
}
};
let linked_location = sema.original_range_opt(&source_syntax);
let label = render_label(param_name, config, linked_location);
let colon = if config.render_colons { ":" } else { "" };
let label = InlayHintLabel::simple(
format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
None,
config.lazy_location_opt(|| {
let source_syntax = match param {
hir::GenericParam::TypeParam(it) => {
sema.source(it.merge()).map(|it| it.value.syntax().clone())
}
hir::GenericParam::ConstParam(it) => {
let syntax = sema.source(it.merge())?.value.syntax().clone();
let const_param = ast::ConstParam::cast(syntax)?;
const_param.name().map(|it| it.syntax().clone())
}
hir::GenericParam::LifetimeParam(it) => {
sema.source(it).map(|it| it.value.syntax().clone())
}
};
let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it));
linked_location.map(Into::into)
}),
);
Some(InlayHint {
range,

View file

@ -49,7 +49,7 @@ pub(super) fn hints(
if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() {
continue; // Arguably only ADTs have significant drop impls
}
let Some(binding) = local_to_binding.get(place.local) else {
let Some(&binding_idx) = local_to_binding.get(place.local) else {
continue; // Ignore temporary values
};
let range = match terminator.span {
@ -91,25 +91,26 @@ pub(super) fn hints(
},
MirSpan::Unknown => continue,
};
let binding_source = source_map
.patterns_for_binding(*binding)
.first()
.and_then(|d| source_map.pat_syntax(*d).ok())
.and_then(|d| {
Some(FileRange {
file_id: d.file_id.file_id()?.into(),
range: d.value.text_range(),
})
});
let binding = &hir.bindings[*binding];
let binding = &hir.bindings[binding_idx];
let name = binding.name.display_no_db(file_id.edition()).to_smolstr();
if name.starts_with("<ra@") {
continue; // Ignore desugared variables
}
let mut label = InlayHintLabel::simple(
name,
Some(config.lazy_tooltip(|| crate::InlayTooltip::String("moz".into()))),
binding_source,
None,
config.lazy_location_opt(|| {
source_map
.patterns_for_binding(binding_idx)
.first()
.and_then(|d| source_map.pat_syntax(*d).ok())
.and_then(|d| {
Some(FileRange {
file_id: d.file_id.file_id()?.into(),
range: d.value.text_range(),
})
})
}),
);
label.prepend_str("drop(");
label.append_str(")");

View file

@ -3,7 +3,6 @@
//! fn max(x: i32, y: i32) -> i32 { x + y }
//! _ = max(/*x*/4, /*y*/4);
//! ```
use std::fmt::Display;
use either::Either;
use hir::{Callable, Semantics};
@ -20,7 +19,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
FamousDefs(sema, krate): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
expr: ast::Expr,
@ -37,23 +36,29 @@ pub(super) fn hints(
.filter_map(|(p, arg)| {
// Only annotate hints for expressions that exist in the original file
let range = sema.original_range_opt(arg.syntax())?;
let source = sema.source(p)?;
let (param_name, name_syntax) = match source.value.as_ref() {
Either::Left(pat) => (pat.name()?, pat.name()),
Either::Right(param) => match param.pat()? {
ast::Pat::IdentPat(it) => (it.name()?, it.name()),
_ => return None,
},
};
Some((name_syntax, param_name, arg, range))
let param_name = p.name(sema.db)?;
Some((p, param_name, arg, range))
})
.filter(|(_, param_name, arg, _)| {
!should_hide_param_name_hint(sema, &callable, &param_name.text(), arg)
!should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg)
})
.map(|(param, param_name, _, hir::FileRange { range, .. })| {
let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax()));
let label = render_label(&param_name, config, linked_location);
let colon = if config.render_colons { ":" } else { "" };
let label = InlayHintLabel::simple(
format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
None,
config.lazy_location_opt(|| {
let source = sema.source(param)?;
let name_syntax = match source.value.as_ref() {
Either::Left(pat) => pat.name(),
Either::Right(param) => match param.pat()? {
ast::Pat::IdentPat(it) => it.name(),
_ => None,
},
}?;
sema.original_range_opt(name_syntax.syntax()).map(Into::into)
}),
);
InlayHint {
range,
kind: InlayKind::Parameter,
@ -70,16 +75,6 @@ pub(super) fn hints(
Some(())
}
pub(super) fn render_label(
param_name: impl Display,
config: &InlayHintsConfig,
linked_location: Option<hir::FileRange>,
) -> InlayHintLabel {
let colon = if config.render_colons { ":" } else { "" };
InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into))
}
fn get_callable(
sema: &Semantics<'_, RootDatabase>,
expr: &ast::Expr,
@ -124,9 +119,7 @@ fn should_hide_param_name_hint(
}
let fn_name = match callable.kind() {
hir::CallableKind::Function(it) => {
Some(it.name(sema.db).unescaped().display_no_db().to_smolstr())
}
hir::CallableKind::Function(it) => Some(it.name(sema.db).as_str().to_smolstr()),
_ => None,
};
let fn_name = fn_name.as_deref();

View file

@ -91,7 +91,8 @@ pub use crate::{
inlay_hints::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints,
InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty,
LifetimeElisionHints,
},
join_lines::JoinLinesConfig,
markup::Markup,
@ -671,7 +672,7 @@ impl Analysis {
&self,
config: &CompletionConfig<'_>,
position: FilePosition,
imports: impl IntoIterator<Item = (String, String)> + std::panic::UnwindSafe,
imports: impl IntoIterator<Item = String> + std::panic::UnwindSafe,
) -> Cancellable<Vec<TextEdit>> {
Ok(self
.with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))?

View file

@ -263,7 +263,7 @@ fn find_definitions(
.and_then(|def| {
// if the name differs from the definitions name it has to be an alias
if def
.name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str()))
.name(sema.db).is_some_and(|it| it.as_str() != name_ref.text().trim_start_matches("r#"))
{
Err(format_err!("Renaming aliases is currently unsupported"))
} else {

View file

@ -19,7 +19,6 @@ dashmap.workspace = true
hashbrown.workspace = true
rustc-hash.workspace = true
triomphe.workspace = true
sptr = "0.3.2"
[lints]
workspace = true

Some files were not shown because too many files have changed in this diff Show more