mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 20:09:19 +00:00
Don't expose hir::Path out of hir
Conjecture: it's impossible to use hir::Path *correctly* from an IDE. I am not entirely sure about this, and we might need to add it back at some point, but I have to arguments that convince me that we probably won't: * `hir::Path` has to know about hygiene, which an IDE can't set up properly. * `hir::Path` lacks identity, but you actually have to know identity to resolve it correctly
This commit is contained in:
parent
2052d33b9b
commit
0ca1ba29e8
9 changed files with 51 additions and 92 deletions
|
@ -53,7 +53,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||||
|builder| {
|
|builder| {
|
||||||
insert_use_statement(
|
insert_use_statement(
|
||||||
&auto_import_assets.syntax_under_caret,
|
&auto_import_assets.syntax_under_caret,
|
||||||
&import,
|
&import.to_string(),
|
||||||
ctx,
|
ctx,
|
||||||
builder.text_edit_builder(),
|
builder.text_edit_builder(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use either::Either;
|
||||||
use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
|
use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
defs::{classify_name_ref, Definition, NameRefClass},
|
defs::{classify_name_ref, Definition, NameRefClass},
|
||||||
|
@ -10,8 +11,6 @@ use crate::{
|
||||||
AssistId, AssistKind,
|
AssistId, AssistKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use either::Either;
|
|
||||||
|
|
||||||
// Assist: expand_glob_import
|
// Assist: expand_glob_import
|
||||||
//
|
//
|
||||||
// Expands glob imports.
|
// Expands glob imports.
|
||||||
|
@ -40,11 +39,15 @@ use either::Either;
|
||||||
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let star = ctx.find_token_at_offset(T![*])?;
|
let star = ctx.find_token_at_offset(T![*])?;
|
||||||
let mod_path = find_mod_path(&star)?;
|
let mod_path = find_mod_path(&star)?;
|
||||||
|
let module = match ctx.sema.resolve_path(&mod_path)? {
|
||||||
|
PathResolution::Def(ModuleDef::Module(it)) => it,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
let source_file = ctx.source_file();
|
let source_file = ctx.source_file();
|
||||||
let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());
|
let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());
|
||||||
|
|
||||||
let defs_in_mod = find_defs_in_mod(ctx, scope, &mod_path)?;
|
let defs_in_mod = find_defs_in_mod(ctx, scope, module)?;
|
||||||
let name_refs_in_source_file =
|
let name_refs_in_source_file =
|
||||||
source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect();
|
source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect();
|
||||||
let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);
|
let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);
|
||||||
|
@ -82,17 +85,8 @@ impl Def {
|
||||||
fn find_defs_in_mod(
|
fn find_defs_in_mod(
|
||||||
ctx: &AssistContext,
|
ctx: &AssistContext,
|
||||||
from: SemanticsScope<'_>,
|
from: SemanticsScope<'_>,
|
||||||
path: &ast::Path,
|
module: hir::Module,
|
||||||
) -> Option<Vec<Def>> {
|
) -> Option<Vec<Def>> {
|
||||||
let hir_path = ctx.sema.lower_path(&path)?;
|
|
||||||
let module = if let Some(PathResolution::Def(ModuleDef::Module(module))) =
|
|
||||||
from.resolve_hir_path_qualifier(&hir_path)
|
|
||||||
{
|
|
||||||
module
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let module_scope = module.scope(ctx.db(), from.module());
|
let module_scope = module.scope(ctx.db(), from.module());
|
||||||
|
|
||||||
let mut defs = vec![];
|
let mut defs = vec![];
|
||||||
|
|
|
@ -106,7 +106,12 @@ fn insert_import(
|
||||||
if let Some(mut mod_path) = mod_path {
|
if let Some(mut mod_path) = mod_path {
|
||||||
mod_path.segments.pop();
|
mod_path.segments.pop();
|
||||||
mod_path.segments.push(variant_hir_name.clone());
|
mod_path.segments.push(variant_hir_name.clone());
|
||||||
insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
|
insert_use_statement(
|
||||||
|
path.syntax(),
|
||||||
|
&mod_path.to_string(),
|
||||||
|
ctx,
|
||||||
|
builder.text_edit_builder(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use hir;
|
use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode, TextRange};
|
||||||
use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
|
use test_utils::mark;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
utils::{find_insert_use_container, insert_use_statement},
|
utils::{find_insert_use_container, insert_use_statement},
|
||||||
|
@ -28,12 +28,19 @@ pub(crate) fn replace_qualified_name_with_use(
|
||||||
if path.syntax().ancestors().find_map(ast::Use::cast).is_some() {
|
if path.syntax().ancestors().find_map(ast::Use::cast).is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
if path.qualifier().is_none() {
|
||||||
let hir_path = ctx.sema.lower_path(&path)?;
|
mark::hit!(dont_import_trivial_paths);
|
||||||
let segments = collect_hir_path_segments(&hir_path)?;
|
|
||||||
if segments.len() < 2 {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let path_to_import = path.to_string().clone();
|
||||||
|
let path_to_import = match path.segment()?.generic_arg_list() {
|
||||||
|
Some(generic_args) => {
|
||||||
|
let generic_args_start =
|
||||||
|
generic_args.syntax().text_range().start() - path.syntax().text_range().start();
|
||||||
|
&path_to_import[TextRange::up_to(generic_args_start)]
|
||||||
|
}
|
||||||
|
None => path_to_import.as_str(),
|
||||||
|
};
|
||||||
|
|
||||||
let target = path.syntax().text_range();
|
let target = path.syntax().text_range();
|
||||||
acc.add(
|
acc.add(
|
||||||
|
@ -41,12 +48,16 @@ pub(crate) fn replace_qualified_name_with_use(
|
||||||
"Replace qualified path with use",
|
"Replace qualified path with use",
|
||||||
target,
|
target,
|
||||||
|builder| {
|
|builder| {
|
||||||
let path_to_import = hir_path.mod_path().clone();
|
|
||||||
let container = match find_insert_use_container(path.syntax(), ctx) {
|
let container = match find_insert_use_container(path.syntax(), ctx) {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
|
insert_use_statement(
|
||||||
|
path.syntax(),
|
||||||
|
&path_to_import.to_string(),
|
||||||
|
ctx,
|
||||||
|
builder.text_edit_builder(),
|
||||||
|
);
|
||||||
|
|
||||||
// Now that we've brought the name into scope, re-qualify all paths that could be
|
// Now that we've brought the name into scope, re-qualify all paths that could be
|
||||||
// affected (that is, all paths inside the node we added the `use` to).
|
// affected (that is, all paths inside the node we added the `use` to).
|
||||||
|
@ -58,26 +69,6 @@ pub(crate) fn replace_qualified_name_with_use(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
|
|
||||||
let mut ps = Vec::<SmolStr>::with_capacity(10);
|
|
||||||
match path.kind() {
|
|
||||||
hir::PathKind::Abs => ps.push("".into()),
|
|
||||||
hir::PathKind::Crate => ps.push("crate".into()),
|
|
||||||
hir::PathKind::Plain => {}
|
|
||||||
hir::PathKind::Super(0) => ps.push("self".into()),
|
|
||||||
hir::PathKind::Super(lvl) => {
|
|
||||||
let mut chain = "super".to_string();
|
|
||||||
for _ in 0..*lvl {
|
|
||||||
chain += "::super";
|
|
||||||
}
|
|
||||||
ps.push(chain.into());
|
|
||||||
}
|
|
||||||
hir::PathKind::DollarCrate(_) => return None,
|
|
||||||
}
|
|
||||||
ps.extend(path.segments().iter().map(|it| it.name.to_string().into()));
|
|
||||||
Some(ps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
|
/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
|
||||||
fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
|
fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
|
||||||
for child in node.children() {
|
for child in node.children() {
|
||||||
|
@ -467,7 +458,8 @@ impl Debug for Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_replace_not_applicable_one_segment() {
|
fn dont_import_trivial_paths() {
|
||||||
|
mark::check!(dont_import_trivial_paths);
|
||||||
check_assist_not_applicable(
|
check_assist_not_applicable(
|
||||||
replace_qualified_name_with_use,
|
replace_qualified_name_with_use,
|
||||||
r"
|
r"
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
use std::iter::successors;
|
use std::iter::successors;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{self, ModPath};
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, NameOwner, VisibilityOwner},
|
ast::{self, NameOwner, VisibilityOwner},
|
||||||
AstNode, AstToken, Direction, SmolStr,
|
AstNode, AstToken, Direction, SmolStr,
|
||||||
|
@ -35,11 +34,11 @@ pub(crate) fn find_insert_use_container(
|
||||||
pub(crate) fn insert_use_statement(
|
pub(crate) fn insert_use_statement(
|
||||||
// Ideally the position of the cursor, used to
|
// Ideally the position of the cursor, used to
|
||||||
position: &SyntaxNode,
|
position: &SyntaxNode,
|
||||||
path_to_import: &ModPath,
|
path_to_import: &str,
|
||||||
ctx: &AssistContext,
|
ctx: &AssistContext,
|
||||||
builder: &mut TextEditBuilder,
|
builder: &mut TextEditBuilder,
|
||||||
) {
|
) {
|
||||||
let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
|
let target = path_to_import.split("::").map(SmolStr::new).collect::<Vec<_>>();
|
||||||
let container = find_insert_use_container(position, ctx);
|
let container = find_insert_use_container(position, ctx);
|
||||||
|
|
||||||
if let Some(container) = container {
|
if let Some(container) = container {
|
||||||
|
|
|
@ -12,6 +12,7 @@ use hir_def::{
|
||||||
docs::Documentation,
|
docs::Documentation,
|
||||||
expr::{BindingAnnotation, Pat, PatId},
|
expr::{BindingAnnotation, Pat, PatId},
|
||||||
import_map,
|
import_map,
|
||||||
|
path::ModPath,
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
resolver::{HasResolver, Resolver},
|
resolver::{HasResolver, Resolver},
|
||||||
src::HasSource as _,
|
src::HasSource as _,
|
||||||
|
@ -344,11 +345,7 @@ impl Module {
|
||||||
|
|
||||||
/// Finds a path that can be used to refer to the given item from within
|
/// Finds a path that can be used to refer to the given item from within
|
||||||
/// this module, if possible.
|
/// this module, if possible.
|
||||||
pub fn find_use_path(
|
pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
|
||||||
self,
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
item: impl Into<ItemInNs>,
|
|
||||||
) -> Option<hir_def::path::ModPath> {
|
|
||||||
hir_def::find_path::find_path(db, item.into(), self.into())
|
hir_def::find_path::find_path(db, item.into(), self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1126,7 +1123,7 @@ impl ImplDef {
|
||||||
.value
|
.value
|
||||||
.attrs()
|
.attrs()
|
||||||
.filter_map(|it| {
|
.filter_map(|it| {
|
||||||
let path = hir_def::path::ModPath::from_src(it.path()?, &hygenic)?;
|
let path = ModPath::from_src(it.path()?, &hygenic)?;
|
||||||
if path.as_ident()?.to_string() == "derive" {
|
if path.as_ident()?.to_string() == "derive" {
|
||||||
Some(it)
|
Some(it)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub use hir_def::{
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
docs::Documentation,
|
docs::Documentation,
|
||||||
nameres::ModuleSource,
|
nameres::ModuleSource,
|
||||||
path::{ModPath, Path, PathKind},
|
path::ModPath,
|
||||||
type_ref::{Mutability, TypeRef},
|
type_ref::{Mutability, TypeRef},
|
||||||
};
|
};
|
||||||
pub use hir_expand::{
|
pub use hir_expand::{
|
||||||
|
@ -60,4 +60,7 @@ pub use hir_ty::display::HirDisplay;
|
||||||
// These are negative re-exports: pub using these names is forbidden, they
|
// These are negative re-exports: pub using these names is forbidden, they
|
||||||
// should remain private to hir internals.
|
// should remain private to hir internals.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use hir_expand::hygiene::Hygiene;
|
use {
|
||||||
|
hir_def::path::{Path, PathKind},
|
||||||
|
hir_expand::hygiene::Hygiene,
|
||||||
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors};
|
||||||
|
|
||||||
use base_db::{FileId, FileRange};
|
use base_db::{FileId, FileRange};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
resolver::{self, HasResolver, Resolver},
|
resolver::{self, HasResolver, Resolver, TypeNs},
|
||||||
AsMacroCall, FunctionId, TraitId, VariantId,
|
AsMacroCall, FunctionId, TraitId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
|
use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
|
||||||
|
@ -22,12 +22,11 @@ use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
diagnostics::Diagnostic,
|
diagnostics::Diagnostic,
|
||||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||||
source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
|
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||||
AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
|
AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
|
||||||
Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef,
|
Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef,
|
||||||
VariantDef,
|
VariantDef,
|
||||||
};
|
};
|
||||||
use resolver::TypeNs;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum PathResolution {
|
pub enum PathResolution {
|
||||||
|
@ -228,10 +227,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||||
self.imp.resolve_variant(record_lit).map(VariantDef::from)
|
self.imp.resolve_variant(record_lit).map(VariantDef::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lower_path(&self, path: &ast::Path) -> Option<Path> {
|
|
||||||
self.imp.lower_path(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
|
pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
|
||||||
self.imp.resolve_bind_pat_to_const(pat)
|
self.imp.resolve_bind_pat_to_const(pat)
|
||||||
}
|
}
|
||||||
|
@ -467,11 +462,6 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit)
|
self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_path(&self, path: &ast::Path) -> Option<Path> {
|
|
||||||
let src = self.find_file(path.syntax().clone());
|
|
||||||
Path::from_src(path.clone(), &Hygiene::new(self.db.upcast(), src.file_id.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
|
fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
|
||||||
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
|
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
|
||||||
}
|
}
|
||||||
|
@ -758,28 +748,7 @@ impl<'a> SemanticsScope<'a> {
|
||||||
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
|
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
|
||||||
let hygiene = Hygiene::new(self.db.upcast(), self.file_id);
|
let hygiene = Hygiene::new(self.db.upcast(), self.file_id);
|
||||||
let path = Path::from_src(path.clone(), &hygiene)?;
|
let path = Path::from_src(path.clone(), &hygiene)?;
|
||||||
self.resolve_hir_path(&path)
|
resolve_hir_path(self.db, &self.resolver, &path)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_hir_path(&self, path: &Path) -> Option<PathResolution> {
|
|
||||||
resolve_hir_path(self.db, &self.resolver, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves a path where we know it is a qualifier of another path.
|
|
||||||
///
|
|
||||||
/// For example, if we have:
|
|
||||||
/// ```
|
|
||||||
/// mod my {
|
|
||||||
/// pub mod foo {
|
|
||||||
/// struct Bar;
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// pub fn foo() {}
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
/// then we know that `foo` in `my::foo::Bar` refers to the module, not the function.
|
|
||||||
pub fn resolve_hir_path_qualifier(&self, path: &Path) -> Option<PathResolution> {
|
|
||||||
resolve_hir_path_qualifier(self.db, &self.resolver, path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ use hir_def::{
|
||||||
Body, BodySourceMap,
|
Body, BodySourceMap,
|
||||||
},
|
},
|
||||||
expr::{ExprId, Pat, PatId},
|
expr::{ExprId, Pat, PatId},
|
||||||
|
path::{ModPath, Path, PathKind},
|
||||||
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
|
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
|
||||||
AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, VariantId,
|
AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, VariantId,
|
||||||
};
|
};
|
||||||
|
@ -28,8 +29,7 @@ use syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, semantics::PathResolution, Adt, Const, EnumVariant, Field, Function, Local,
|
db::HirDatabase, semantics::PathResolution, Adt, Const, EnumVariant, Field, Function, Local,
|
||||||
MacroDef, ModPath, ModuleDef, Path, PathKind, Static, Struct, Trait, Type, TypeAlias,
|
MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam,
|
||||||
TypeParam,
|
|
||||||
};
|
};
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
|
|
||||||
|
@ -508,7 +508,7 @@ pub(crate) fn resolve_hir_path(
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// then we know that `foo` in `my::foo::Bar` refers to the module, not the function.
|
/// then we know that `foo` in `my::foo::Bar` refers to the module, not the function.
|
||||||
pub(crate) fn resolve_hir_path_qualifier(
|
fn resolve_hir_path_qualifier(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue