mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-28 10:39:45 +00:00
Auto merge of #136117 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
This commit is contained in:
commit
702da50daa
126 changed files with 2163 additions and 1325 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -1381,6 +1381,9 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
|
||||
if self.check_cfg(¯o_).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(¯o_).is_none() {
|
||||
return;
|
||||
}
|
||||
let Some(name) = macro_.name() else {
|
||||
statements.push(Statement::Item(Item::Other));
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -475,7 +475,7 @@ fn outer() {
|
|||
|
||||
block scope::tests
|
||||
name: _
|
||||
outer: v
|
||||
outer: vg
|
||||
|
||||
crate
|
||||
outer: v
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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((
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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#");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()))?;
|
||||
|
|
|
|||
|
|
@ -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 => {}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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(¯o_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(..)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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, &[]) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()?;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: "",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: "",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(")");
|
||||
|
|
|
|||
|
|
@ -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, ¶m_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(¶m_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();
|
||||
|
|
|
|||
|
|
@ -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))?
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue