rust-analyzer/crates/hir-def/src/find_path.rs
Chayim Refael Friedman 9d3368f2c2 Properly account for editions in names
This PR touches a lot of parts. But the main changes are changing
`hir_expand::Name` to be raw edition-dependently and only when necessary
(unrelated to how the user originally wrote the identifier),
and changing `is_keyword()` and `is_raw_identifier()` to be edition-aware
(this was done in #17896, but the FIXMEs were fixed here).

It is possible that I missed some cases, but most IDE parts should properly
escape (or not escape) identifiers now.

The rules of thumb are:

 - If we show the identifier to the user, its rawness should be determined
   by the edition of the edited crate. This is nice for IDE features,
   but really important for changes we insert to the source code.
 - For tests, I chose `Edition::CURRENT` (so we only have to (maybe) update
   tests when an edition becomes stable, to avoid churn).
 - For debugging tools (helper methods and logs), I used `Edition::LATEST`.
2024-08-16 16:46:24 +03:00

2136 lines
60 KiB
Rust

//! An algorithm to find a path to refer to a certain item.
use std::{cell::Cell, cmp::Ordering, iter};
use base_db::{CrateId, CrateOrigin, LangCrateOrigin};
use hir_expand::{
name::{AsName, Name},
Lookup,
};
use intern::sym;
use rustc_hash::FxHashSet;
use crate::{
db::DefDatabase,
item_scope::ItemInNs,
nameres::DefMap,
path::{ModPath, PathKind},
visibility::{Visibility, VisibilityExplicitness},
ImportPathConfig, ModuleDefId, ModuleId,
};
/// Find a path that can be used to refer to a certain item. This can depend on
/// *from where* you're referring to the item, hence the `from` parameter.
pub fn find_path(
db: &dyn DefDatabase,
item: ItemInNs,
from: ModuleId,
mut prefix_kind: PrefixKind,
ignore_local_imports: bool,
mut cfg: ImportPathConfig,
) -> Option<ModPath> {
let _p = tracing::info_span!("find_path").entered();
// - if the item is a builtin, it's in scope
if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
return Some(ModPath::from_segments(PathKind::Plain, iter::once(builtin.as_name())));
}
// within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so
// default to plain paths.
let item_module = item.module(db)?;
if item_module.is_within_block() {
prefix_kind = PrefixKind::Plain;
}
cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate());
find_path_inner(
&FindPathCtx {
db,
prefix: prefix_kind,
cfg,
ignore_local_imports,
is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(),
from,
from_def_map: &from.def_map(db),
fuel: Cell::new(FIND_PATH_FUEL),
},
item,
MAX_PATH_LEN,
)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Stability {
Unstable,
Stable,
}
use Stability::*;
const MAX_PATH_LEN: usize = 15;
const FIND_PATH_FUEL: usize = 10000;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PrefixKind {
/// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
/// This is the same as plain, just that paths will start with `self` prepended if the path
/// starts with an identifier that is not a crate.
BySelf,
/// Causes paths to not use a self, super or crate prefix.
Plain,
/// Causes paths to start with `crate` where applicable, effectively forcing paths to be absolute.
ByCrate,
}
impl PrefixKind {
#[inline]
fn path_kind(self) -> PathKind {
match self {
PrefixKind::BySelf => PathKind::SELF,
PrefixKind::Plain => PathKind::Plain,
PrefixKind::ByCrate => PathKind::Crate,
}
}
}
struct FindPathCtx<'db> {
db: &'db dyn DefDatabase,
prefix: PrefixKind,
cfg: ImportPathConfig,
ignore_local_imports: bool,
is_std_item: bool,
from: ModuleId,
from_def_map: &'db DefMap,
fuel: Cell<usize>,
}
/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> {
// - if the item is a module, jump straight to module search
if !ctx.is_std_item {
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len)
.map(|choice| choice.path);
}
}
let may_be_in_scope = match ctx.prefix {
PrefixKind::Plain | PrefixKind::BySelf => true,
PrefixKind::ByCrate => ctx.from.is_crate_root(),
};
if may_be_in_scope {
// - if the item is already in scope, return the name under which it is
let scope_name =
find_in_scope(ctx.db, ctx.from_def_map, ctx.from, item, ctx.ignore_local_imports);
if let Some(scope_name) = scope_name {
return Some(ModPath::from_segments(ctx.prefix.path_kind(), iter::once(scope_name)));
}
}
// - if the item is in the prelude, return the name from there
if let Some(value) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) {
return Some(value.path);
}
if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
// - if the item is an enum variant, refer to it via the enum
if let Some(mut path) =
find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len)
{
path.push_segment(ctx.db.enum_variant_data(variant).name.clone());
return Some(path);
}
// If this doesn't work, it seems we have no way of referring to the
// enum; that's very weird, but there might still be a reexport of the
// variant somewhere
}
let mut best_choice = None;
calculate_best_path(ctx, &mut FxHashSet::default(), item, max_len, &mut best_choice);
best_choice.map(|choice| choice.path)
}
#[tracing::instrument(skip_all)]
fn find_path_for_module(
ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
module_id: ModuleId,
maybe_extern: bool,
max_len: usize,
) -> Option<Choice> {
if max_len == 0 {
// recursive base case, we can't find a path of length 0
return None;
}
if let Some(crate_root) = module_id.as_crate_root() {
if !maybe_extern || crate_root == ctx.from.derive_crate_root() {
// - if the item is the crate root, return `crate`
return Some(Choice {
path: ModPath::from_segments(PathKind::Crate, None),
path_text_len: 5,
stability: Stable,
prefer_due_to_prelude: false,
});
}
// - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude
let root_def_map = ctx.from.derive_crate_root().def_map(ctx.db);
// rev here so we prefer looking at renamed extern decls first
for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude().rev() {
if crate_root != def_id {
continue;
}
let name_already_occupied_in_type_ns = ctx
.from_def_map
.with_ancestor_maps(ctx.db, ctx.from.local_id, &mut |def_map, local_id| {
def_map[local_id]
.scope
.type_(name)
.filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into()))
})
.is_some();
let kind = if name_already_occupied_in_type_ns {
cov_mark::hit!(ambiguous_crate_start);
PathKind::Abs
} else if ctx.cfg.prefer_absolute {
PathKind::Abs
} else {
PathKind::Plain
};
return Some(Choice::new(ctx.cfg.prefer_prelude, kind, name.clone(), Stable));
}
}
let may_be_in_scope = match ctx.prefix {
PrefixKind::Plain | PrefixKind::BySelf => true,
PrefixKind::ByCrate => ctx.from.is_crate_root(),
};
if may_be_in_scope {
let scope_name = find_in_scope(
ctx.db,
ctx.from_def_map,
ctx.from,
ItemInNs::Types(module_id.into()),
ctx.ignore_local_imports,
);
if let Some(scope_name) = scope_name {
// - if the item is already in scope, return the name under which it is
return Some(Choice::new(
ctx.cfg.prefer_prelude,
ctx.prefix.path_kind(),
scope_name,
Stable,
));
}
}
// - if the module can be referenced as self, super or crate, do that
if let Some(kind) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) {
if ctx.prefix != PrefixKind::ByCrate || kind == PathKind::Crate {
return Some(Choice {
path: ModPath::from_segments(kind, None),
path_text_len: path_kind_len(kind),
stability: Stable,
prefer_due_to_prelude: false,
});
}
}
// - if the module is in the prelude, return it by that path
let item = ItemInNs::Types(module_id.into());
if let Some(choice) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) {
return Some(choice);
}
let mut best_choice = None;
if maybe_extern {
calculate_best_path(ctx, visited_modules, item, max_len, &mut best_choice);
} else {
calculate_best_path_local(ctx, visited_modules, item, max_len, &mut best_choice);
}
best_choice
}
fn find_in_scope(
db: &dyn DefDatabase,
def_map: &DefMap,
from: ModuleId,
item: ItemInNs,
ignore_local_imports: bool,
) -> Option<Name> {
// FIXME: We could have multiple applicable names here, but we currently only return the first
def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
def_map[local_id].scope.names_of(item, |name, _, declared| {
(declared || !ignore_local_imports).then(|| name.clone())
})
})
}
/// Returns single-segment path (i.e. without any prefix) if `item` is found in prelude and its
/// name doesn't clash in current scope.
fn find_in_prelude(
db: &dyn DefDatabase,
local_def_map: &DefMap,
item: ItemInNs,
from: ModuleId,
) -> Option<Choice> {
let (prelude_module, _) = local_def_map.prelude()?;
let prelude_def_map = prelude_module.def_map(db);
let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
let (name, vis, _declared) = prelude_scope.name_of(item)?;
if !vis.is_visible_from(db, from) {
return None;
}
// Check if the name is in current scope and it points to the same def.
let found_and_same_def =
local_def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
let per_ns = def_map[local_id].scope.get(name);
let same_def = match item {
ItemInNs::Types(it) => per_ns.take_types()? == it,
ItemInNs::Values(it) => per_ns.take_values()? == it,
ItemInNs::Macros(it) => per_ns.take_macros()? == it,
};
Some(same_def)
});
if found_and_same_def.unwrap_or(true) {
Some(Choice::new(false, PathKind::Plain, name.clone(), Stable))
} else {
None
}
}
fn is_kw_kind_relative_to_from(
def_map: &DefMap,
item: ModuleId,
from: ModuleId,
) -> Option<PathKind> {
if item.krate != from.krate || item.is_within_block() || from.is_within_block() {
return None;
}
let item = item.local_id;
let from = from.local_id;
if item == from {
// - if the item is the module we're in, use `self`
Some(PathKind::SELF)
} else if let Some(parent_id) = def_map[from].parent {
if item == parent_id {
// - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
Some(if parent_id == DefMap::ROOT { PathKind::Crate } else { PathKind::Super(1) })
} else {
None
}
} else {
None
}
}
#[tracing::instrument(skip_all)]
fn calculate_best_path(
ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
) {
let fuel = ctx.fuel.get();
if fuel == 0 {
// we ran out of fuel, so we stop searching here
tracing::warn!(
"ran out of fuel while searching for a path for item {item:?} of krate {:?} from krate {:?}",
item.krate(ctx.db),
ctx.from.krate()
);
return;
}
ctx.fuel.set(fuel - 1);
if item.krate(ctx.db) == Some(ctx.from.krate) {
// Item was defined in the same crate that wants to import it. It cannot be found in any
// dependency in this case.
calculate_best_path_local(ctx, visited_modules, item, max_len, best_choice)
} else if ctx.is_std_item {
// The item we are searching for comes from the sysroot libraries, so skip prefer looking in
// the sysroot libraries directly.
// We do need to fallback as the item in question could be re-exported by another crate
// while not being a transitive dependency of the current crate.
find_in_sysroot(ctx, visited_modules, item, max_len, best_choice)
} else {
// Item was defined in some upstream crate. This means that it must be exported from one,
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate
// that wants to import it here, but we always prefer to use the external path here.
ctx.db.crate_graph()[ctx.from.krate].dependencies.iter().for_each(|dep| {
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id)
});
}
}
fn find_in_sysroot(
ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
) {
let crate_graph = ctx.db.crate_graph();
let dependencies = &crate_graph[ctx.from.krate].dependencies;
let mut search = |lang, best_choice: &mut _| {
if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| {
match crate_graph[dep.crate_id].origin {
CrateOrigin::Lang(l) => l == lang,
_ => false,
}
}) {
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id);
}
};
if ctx.cfg.prefer_no_std {
search(LangCrateOrigin::Core, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return;
}
search(LangCrateOrigin::Std, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return;
}
} else {
search(LangCrateOrigin::Std, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return;
}
search(LangCrateOrigin::Core, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return;
}
}
dependencies
.iter()
.filter(|it| it.is_sysroot())
.chain(dependencies.iter().filter(|it| !it.is_sysroot()))
.for_each(|dep| {
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id);
});
}
fn find_in_dep(
ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
dep: CrateId,
) {
let import_map = ctx.db.import_map(dep);
let Some(import_info_for) = import_map.import_info_for(item) else {
return;
};
for info in import_info_for {
if info.is_doc_hidden {
// the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
continue;
}
// Determine best path for containing module and append last segment from `info`.
// FIXME: we should guide this to look up the path locally, or from the same crate again?
let choice = find_path_for_module(
ctx,
visited_modules,
info.container,
true,
best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
);
let Some(mut choice) = choice else {
continue;
};
cov_mark::hit!(partially_imported);
if info.is_unstable {
choice.stability = Unstable;
}
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, info.name.clone());
}
}
fn calculate_best_path_local(
ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
) {
// FIXME: cache the `find_local_import_locations` output?
find_local_import_locations(
ctx.db,
item,
ctx.from,
ctx.from_def_map,
visited_modules,
|visited_modules, name, module_id| {
// we are looking for paths of length up to best_path_len, any longer will make it be
// less optimal. The -1 is due to us pushing name onto it afterwards.
if let Some(choice) = find_path_for_module(
ctx,
visited_modules,
module_id,
false,
best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
) {
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone());
}
},
);
}
#[derive(Debug)]
struct Choice {
path: ModPath,
/// The length in characters of the path
path_text_len: usize,
/// The stability of the path
stability: Stability,
/// Whether this path contains a prelude segment and preference for it has been signaled
prefer_due_to_prelude: bool,
}
impl Choice {
fn new(prefer_prelude: bool, kind: PathKind, name: Name, stability: Stability) -> Self {
Self {
path_text_len: path_kind_len(kind) + name.as_str().len(),
stability,
prefer_due_to_prelude: prefer_prelude && name == sym::prelude,
path: ModPath::from_segments(kind, iter::once(name)),
}
}
fn push(mut self, prefer_prelude: bool, name: Name) -> Self {
self.path_text_len += name.as_str().len();
self.prefer_due_to_prelude |= prefer_prelude && name == sym::prelude;
self.path.push_segment(name);
self
}
fn try_select(
current: &mut Option<Choice>,
mut other: Choice,
prefer_prelude: bool,
name: Name,
) {
let Some(current) = current else {
*current = Some(other.push(prefer_prelude, name));
return;
};
match other
.stability
.cmp(&current.stability)
.then_with(|| other.prefer_due_to_prelude.cmp(&current.prefer_due_to_prelude))
.then_with(|| (current.path.len()).cmp(&(other.path.len() + 1)))
{
Ordering::Less => return,
Ordering::Equal => {
other.path_text_len += name.as_str().len();
if let Ordering::Less | Ordering::Equal =
current.path_text_len.cmp(&other.path_text_len)
{
return;
}
}
Ordering::Greater => {
other.path_text_len += name.as_str().len();
}
}
other.path.push_segment(name);
*current = other;
}
}
fn path_kind_len(kind: PathKind) -> usize {
match kind {
PathKind::Plain => 0,
PathKind::Super(0) => 4,
PathKind::Super(s) => s as usize * 5,
PathKind::Crate => 5,
PathKind::Abs => 2,
PathKind::DollarCrate(_) => 0,
}
}
/// Finds locations in `from.krate` from which `item` can be imported by `from`.
fn find_local_import_locations(
db: &dyn DefDatabase,
item: ItemInNs,
from: ModuleId,
def_map: &DefMap,
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleId)>, &Name, ModuleId),
) {
let _p = tracing::info_span!("find_local_import_locations").entered();
// `from` can import anything below `from` with visibility of at least `from`, and anything
// above `from` with any visibility. That means we do not need to descend into private siblings
// of `from` (and similar).
// Compute the initial worklist. We start with all direct child modules of `from` as well as all
// of its (recursive) parent modules.
let mut worklist = def_map[from.local_id]
.children
.values()
.map(|&child| def_map.module_id(child))
.chain(iter::successors(from.containing_module(db), |m| m.containing_module(db)))
.zip(iter::repeat(false))
.collect::<Vec<_>>();
let def_map = def_map.crate_root().def_map(db);
let mut block_def_map;
let mut cursor = 0;
while let Some(&mut (module, ref mut processed)) = worklist.get_mut(cursor) {
cursor += 1;
if !visited_modules.insert((item, module)) {
// already processed this module
continue;
}
*processed = true;
let data = if module.block.is_some() {
// Re-query the block's DefMap
block_def_map = module.def_map(db);
&block_def_map[module.local_id]
} else {
// Reuse the root DefMap
&def_map[module.local_id]
};
if let Some((name, vis, declared)) = data.scope.name_of(item) {
if vis.is_visible_from(db, from) {
let is_pub_or_explicit = match vis {
Visibility::Module(_, VisibilityExplicitness::Explicit) => {
cov_mark::hit!(explicit_private_imports);
true
}
Visibility::Module(_, VisibilityExplicitness::Implicit) => {
cov_mark::hit!(discount_private_imports);
false
}
Visibility::Public => true,
};
// Ignore private imports unless they are explicit. these could be used if we are
// in a submodule of this module, but that's usually not
// what the user wants; and if this module can import
// the item and we're a submodule of it, so can we.
// Also this keeps the cached data smaller.
if declared || is_pub_or_explicit {
cb(visited_modules, name, module);
}
}
}
// Descend into all modules visible from `from`.
for (module, vis) in data.scope.modules_in_scope() {
if module.krate != from.krate {
// We don't need to look at modules from other crates as our item has to be in the
// current crate
continue;
}
if visited_modules.contains(&(item, module)) {
continue;
}
if vis.is_visible_from(db, from) {
worklist.push((module, false));
}
}
}
worklist.into_iter().filter(|&(_, processed)| processed).for_each(|(module, _)| {
visited_modules.remove(&(item, module));
});
}
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
use hir_expand::db::ExpandDatabase;
use itertools::Itertools;
use span::Edition;
use stdx::format_to;
use syntax::ast::AstNode;
use test_fixture::WithFixture;
use crate::test_db::TestDB;
use super::*;
/// `code` needs to contain a cursor marker; checks that `find_path` for the
/// item the `path` refers to returns that same path when called from the
/// module the cursor is in.
#[track_caller]
fn check_found_path_(
ra_fixture: &str,
path: &str,
prefer_prelude: bool,
prefer_absolute: bool,
prefer_no_std: bool,
expect: Expect,
) {
let (db, pos) = TestDB::with_position(ra_fixture);
let module = db.module_at_position(pos);
let parsed_path_file =
syntax::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT);
let ast_path =
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
let mod_path = ModPath::from_src(&db, ast_path, &mut |range| {
db.span_map(pos.file_id.into()).as_ref().span_for_range(range).ctx
})
.unwrap();
let def_map = module.def_map(&db);
let resolved = def_map
.resolve_path(
&db,
module.local_id,
&mod_path,
crate::item_scope::BuiltinShadowMode::Module,
None,
)
.0;
let resolved = resolved
.take_types()
.map(ItemInNs::Types)
.or_else(|| resolved.take_values().map(ItemInNs::Values))
.expect("path does not resolve to a type or value");
let mut res = String::new();
for (prefix, ignore_local_imports) in
[PrefixKind::Plain, PrefixKind::ByCrate, PrefixKind::BySelf]
.into_iter()
.cartesian_product([false, true])
{
let found_path = find_path(
&db,
resolved,
module,
prefix,
ignore_local_imports,
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute },
);
format_to!(
res,
"{:7}(imports {}): {}\n",
format!("{:?}", prefix),
if ignore_local_imports { '✖' } else { '✔' },
found_path.map_or_else(
|| "<unresolvable>".to_owned(),
|it| it.display(&db, Edition::CURRENT).to_string()
),
);
}
expect.assert_eq(&res);
}
fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, false, false, false, expect);
}
fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, true, false, false, expect);
}
fn check_found_path_absolute(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, false, true, false, expect);
}
fn check_found_path_prefer_no_std(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, false, false, true, expect);
}
#[test]
fn same_module() {
check_found_path(
r#"
struct S;
$0
"#,
"S",
expect![[r#"
Plain (imports ✔): S
Plain (imports ✖): S
ByCrate(imports ✔): crate::S
ByCrate(imports ✖): crate::S
BySelf (imports ✔): self::S
BySelf (imports ✖): self::S
"#]],
);
}
#[test]
fn enum_variant() {
check_found_path(
r#"
enum E { A }
$0
"#,
"E::A",
expect![[r#"
Plain (imports ✔): E::A
Plain (imports ✖): E::A
ByCrate(imports ✔): crate::E::A
ByCrate(imports ✖): crate::E::A
BySelf (imports ✔): self::E::A
BySelf (imports ✖): self::E::A
"#]],
);
}
#[test]
fn sub_module() {
check_found_path(
r#"
mod foo {
pub struct S;
}
$0
"#,
"foo::S",
expect![[r#"
Plain (imports ✔): foo::S
Plain (imports ✖): foo::S
ByCrate(imports ✔): crate::foo::S
ByCrate(imports ✖): crate::foo::S
BySelf (imports ✔): self::foo::S
BySelf (imports ✖): self::foo::S
"#]],
);
}
#[test]
fn super_module() {
check_found_path(
r#"
//- /main.rs
mod foo;
//- /foo.rs
mod bar;
struct S;
//- /foo/bar.rs
$0
"#,
"super::S",
expect![[r#"
Plain (imports ✔): super::S
Plain (imports ✖): super::S
ByCrate(imports ✔): crate::foo::S
ByCrate(imports ✖): crate::foo::S
BySelf (imports ✔): super::S
BySelf (imports ✖): super::S
"#]],
);
}
#[test]
fn self_module() {
check_found_path(
r#"
//- /main.rs
mod foo;
//- /foo.rs
$0
"#,
"self",
expect![[r#"
Plain (imports ✔): self
Plain (imports ✖): self
ByCrate(imports ✔): crate::foo
ByCrate(imports ✖): crate::foo
BySelf (imports ✔): self
BySelf (imports ✖): self
"#]],
);
}
#[test]
fn crate_root() {
check_found_path(
r#"
//- /main.rs
mod foo;
//- /foo.rs
$0
"#,
"crate",
expect![[r#"
Plain (imports ✔): crate
Plain (imports ✖): crate
ByCrate(imports ✔): crate
ByCrate(imports ✖): crate
BySelf (imports ✔): crate
BySelf (imports ✖): crate
"#]],
);
}
#[test]
fn same_crate() {
check_found_path(
r#"
//- /main.rs
mod foo;
struct S;
//- /foo.rs
$0
"#,
"crate::S",
expect![[r#"
Plain (imports ✔): crate::S
Plain (imports ✖): crate::S
ByCrate(imports ✔): crate::S
ByCrate(imports ✖): crate::S
BySelf (imports ✔): crate::S
BySelf (imports ✖): crate::S
"#]],
);
}
#[test]
fn different_crate() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
$0
//- /std.rs crate:std
pub struct S;
"#,
"std::S",
expect![[r#"
Plain (imports ✔): std::S
Plain (imports ✖): std::S
ByCrate(imports ✔): std::S
ByCrate(imports ✖): std::S
BySelf (imports ✔): std::S
BySelf (imports ✖): std::S
"#]],
);
}
#[test]
fn different_crate_renamed() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
extern crate std as std_renamed;
$0
//- /std.rs crate:std
pub struct S;
"#,
"std_renamed::S",
expect![[r#"
Plain (imports ✔): std_renamed::S
Plain (imports ✖): std_renamed::S
ByCrate(imports ✔): std_renamed::S
ByCrate(imports ✖): std_renamed::S
BySelf (imports ✔): std_renamed::S
BySelf (imports ✖): std_renamed::S
"#]],
);
}
#[test]
fn partially_imported() {
cov_mark::check!(partially_imported);
// Tests that short paths are used even for external items, when parts of the path are
// already in scope.
check_found_path(
r#"
//- /main.rs crate:main deps:syntax
use syntax::ast;
$0
//- /lib.rs crate:syntax
pub mod ast {
pub enum ModuleItem {
A, B, C,
}
}
"#,
"syntax::ast::ModuleItem",
expect![[r#"
Plain (imports ✔): ast::ModuleItem
Plain (imports ✖): syntax::ast::ModuleItem
ByCrate(imports ✔): crate::ast::ModuleItem
ByCrate(imports ✖): syntax::ast::ModuleItem
BySelf (imports ✔): self::ast::ModuleItem
BySelf (imports ✖): syntax::ast::ModuleItem
"#]],
);
check_found_path(
r#"
//- /main.rs crate:main deps:syntax
$0
//- /lib.rs crate:syntax
pub mod ast {
pub enum ModuleItem {
A, B, C,
}
}
"#,
"syntax::ast::ModuleItem",
expect![[r#"
Plain (imports ✔): syntax::ast::ModuleItem
Plain (imports ✖): syntax::ast::ModuleItem
ByCrate(imports ✔): syntax::ast::ModuleItem
ByCrate(imports ✖): syntax::ast::ModuleItem
BySelf (imports ✔): syntax::ast::ModuleItem
BySelf (imports ✖): syntax::ast::ModuleItem
"#]],
);
}
#[test]
fn partially_imported_with_prefer_absolute() {
cov_mark::check!(partially_imported);
// Similar to partially_imported test case above, but with prefer_absolute enabled.
// Even if the actual imported item is in external crate, if the path to that item
// is starting from the imported name, then the path should not start from "::".
// i.e. The first line in the expected output should not start from "::".
check_found_path_absolute(
r#"
//- /main.rs crate:main deps:syntax
use syntax::ast;
$0
//- /lib.rs crate:syntax
pub mod ast {
pub enum ModuleItem {
A, B, C,
}
}
"#,
"syntax::ast::ModuleItem",
expect![[r#"
Plain (imports ✔): ast::ModuleItem
Plain (imports ✖): ::syntax::ast::ModuleItem
ByCrate(imports ✔): crate::ast::ModuleItem
ByCrate(imports ✖): ::syntax::ast::ModuleItem
BySelf (imports ✔): self::ast::ModuleItem
BySelf (imports ✖): ::syntax::ast::ModuleItem
"#]],
);
}
#[test]
fn same_crate_reexport() {
check_found_path(
r#"
mod bar {
mod foo { pub(super) struct S; }
pub(crate) use foo::*;
}
$0
"#,
"bar::S",
expect![[r#"
Plain (imports ✔): bar::S
Plain (imports ✖): bar::S
ByCrate(imports ✔): crate::bar::S
ByCrate(imports ✖): crate::bar::S
BySelf (imports ✔): self::bar::S
BySelf (imports ✖): self::bar::S
"#]],
);
}
#[test]
fn same_crate_reexport_rename() {
check_found_path(
r#"
mod bar {
mod foo { pub(super) struct S; }
pub(crate) use foo::S as U;
}
$0
"#,
"bar::U",
expect![[r#"
Plain (imports ✔): bar::U
Plain (imports ✖): bar::U
ByCrate(imports ✔): crate::bar::U
ByCrate(imports ✖): crate::bar::U
BySelf (imports ✔): self::bar::U
BySelf (imports ✖): self::bar::U
"#]],
);
}
#[test]
fn different_crate_reexport() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
$0
//- /std.rs crate:std deps:core
pub use core::S;
//- /core.rs crate:core
pub struct S;
"#,
"std::S",
expect![[r#"
Plain (imports ✔): std::S
Plain (imports ✖): std::S
ByCrate(imports ✔): std::S
ByCrate(imports ✖): std::S
BySelf (imports ✔): std::S
BySelf (imports ✖): std::S
"#]],
);
}
#[test]
fn prelude() {
check_found_path(
r#"
//- /main.rs edition:2018 crate:main deps:std
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"S",
expect![[r#"
Plain (imports ✔): S
Plain (imports ✖): S
ByCrate(imports ✔): S
ByCrate(imports ✖): S
BySelf (imports ✔): S
BySelf (imports ✖): S
"#]],
);
}
#[test]
fn shadowed_prelude() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
struct S;
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"std::prelude::rust_2018::S",
expect![[r#"
Plain (imports ✔): std::prelude::rust_2018::S
Plain (imports ✖): std::prelude::rust_2018::S
ByCrate(imports ✔): std::prelude::rust_2018::S
ByCrate(imports ✖): std::prelude::rust_2018::S
BySelf (imports ✔): std::prelude::rust_2018::S
BySelf (imports ✖): std::prelude::rust_2018::S
"#]],
);
}
#[test]
fn imported_prelude() {
check_found_path(
r#"
//- /main.rs edition:2018 crate:main deps:std
use S;
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"S",
expect![[r#"
Plain (imports ✔): S
Plain (imports ✖): S
ByCrate(imports ✔): crate::S
ByCrate(imports ✖): S
BySelf (imports ✔): self::S
BySelf (imports ✖): S
"#]],
);
}
#[test]
fn enum_variant_from_prelude() {
let code = r#"
//- /main.rs edition:2018 crate:main deps:std
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub enum Option<T> { Some(T), None }
pub use Option::*;
}
}
"#;
check_found_path(
code,
"None",
expect![[r#"
Plain (imports ✔): None
Plain (imports ✖): None
ByCrate(imports ✔): None
ByCrate(imports ✖): None
BySelf (imports ✔): None
BySelf (imports ✖): None
"#]],
);
check_found_path(
code,
"Some",
expect![[r#"
Plain (imports ✔): Some
Plain (imports ✖): Some
ByCrate(imports ✔): Some
ByCrate(imports ✖): Some
BySelf (imports ✔): Some
BySelf (imports ✖): Some
"#]],
);
}
#[test]
fn shortest_path() {
check_found_path(
r#"
//- /main.rs
pub mod foo;
pub mod baz;
struct S;
$0
//- /foo.rs
pub mod bar { pub struct S; }
//- /baz.rs
pub use crate::foo::bar::S;
"#,
"baz::S",
expect![[r#"
Plain (imports ✔): baz::S
Plain (imports ✖): baz::S
ByCrate(imports ✔): crate::baz::S
ByCrate(imports ✖): crate::baz::S
BySelf (imports ✔): self::baz::S
BySelf (imports ✖): self::baz::S
"#]],
);
}
#[test]
fn discount_private_imports() {
cov_mark::check!(discount_private_imports);
check_found_path(
r#"
//- /main.rs
mod foo;
pub mod bar { pub struct S; }
use bar::S;
//- /foo.rs
$0
"#,
// crate::S would be shorter, but using private imports seems wrong
"crate::bar::S",
expect![[r#"
Plain (imports ✔): crate::bar::S
Plain (imports ✖): crate::bar::S
ByCrate(imports ✔): crate::bar::S
ByCrate(imports ✖): crate::bar::S
BySelf (imports ✔): crate::bar::S
BySelf (imports ✖): crate::bar::S
"#]],
);
}
#[test]
fn explicit_private_imports_crate() {
cov_mark::check!(explicit_private_imports);
check_found_path(
r#"
//- /main.rs
mod foo;
pub mod bar { pub struct S; }
pub(crate) use bar::S;
//- /foo.rs
$0
"#,
"crate::S",
expect![[r#"
Plain (imports ✔): crate::S
Plain (imports ✖): crate::S
ByCrate(imports ✔): crate::S
ByCrate(imports ✖): crate::S
BySelf (imports ✔): crate::S
BySelf (imports ✖): crate::S
"#]],
);
}
#[test]
fn explicit_private_imports() {
cov_mark::check!(explicit_private_imports);
check_found_path(
r#"
//- /main.rs
pub mod bar {
mod foo;
pub mod baz { pub struct S; }
pub(self) use baz::S;
}
//- /bar/foo.rs
$0
"#,
"super::S",
expect![[r#"
Plain (imports ✔): super::S
Plain (imports ✖): super::S
ByCrate(imports ✔): crate::bar::S
ByCrate(imports ✖): crate::bar::S
BySelf (imports ✔): super::S
BySelf (imports ✖): super::S
"#]],
);
}
#[test]
fn import_cycle() {
check_found_path(
r#"
//- /main.rs
pub mod foo;
pub mod bar;
pub mod baz;
//- /bar.rs
$0
//- /foo.rs
pub use super::baz;
pub struct S;
//- /baz.rs
pub use super::foo;
"#,
"crate::foo::S",
expect![[r#"
Plain (imports ✔): crate::foo::S
Plain (imports ✖): crate::foo::S
ByCrate(imports ✔): crate::foo::S
ByCrate(imports ✖): crate::foo::S
BySelf (imports ✔): crate::foo::S
BySelf (imports ✖): crate::foo::S
"#]],
);
}
#[test]
fn prefer_std_paths_over_alloc() {
check_found_path(
r#"
//- /main.rs crate:main deps:alloc,std
$0
//- /std.rs crate:std deps:alloc
pub mod sync {
pub use alloc::sync::Arc;
}
//- /zzz.rs crate:alloc
pub mod sync {
pub struct Arc;
}
"#,
"std::sync::Arc",
expect![[r#"
Plain (imports ✔): std::sync::Arc
Plain (imports ✖): std::sync::Arc
ByCrate(imports ✔): std::sync::Arc
ByCrate(imports ✖): std::sync::Arc
BySelf (imports ✔): std::sync::Arc
BySelf (imports ✖): std::sync::Arc
"#]],
);
}
#[test]
fn prefer_core_paths_over_std_for_mod_reexport() {
check_found_path_prefer_no_std(
r#"
//- /main.rs crate:main deps:core,std
$0
//- /stdlib.rs crate:std deps:core
pub use core::pin;
//- /corelib.rs crate:core
pub mod pin {
pub struct Pin;
}
"#,
"std::pin::Pin",
expect![[r#"
Plain (imports ✔): core::pin::Pin
Plain (imports ✖): core::pin::Pin
ByCrate(imports ✔): core::pin::Pin
ByCrate(imports ✖): core::pin::Pin
BySelf (imports ✔): core::pin::Pin
BySelf (imports ✖): core::pin::Pin
"#]],
);
}
#[test]
fn prefer_core_paths_over_std() {
check_found_path_prefer_no_std(
r#"
//- /main.rs crate:main deps:core,std
$0
//- /std.rs crate:std deps:core
pub mod fmt {
pub use core::fmt::Error;
}
//- /zzz.rs crate:core
pub mod fmt {
pub struct Error;
}
"#,
"core::fmt::Error",
expect![[r#"
Plain (imports ✔): core::fmt::Error
Plain (imports ✖): core::fmt::Error
ByCrate(imports ✔): core::fmt::Error
ByCrate(imports ✖): core::fmt::Error
BySelf (imports ✔): core::fmt::Error
BySelf (imports ✖): core::fmt::Error
"#]],
);
check_found_path(
r#"
//- /main.rs crate:main deps:core,std
#![no_std]
$0
//- /std.rs crate:std deps:core
pub mod fmt {
pub use core::fmt::Error;
}
//- /zzz.rs crate:core
pub mod fmt {
pub struct Error;
}
"#,
"core::fmt::Error",
expect![[r#"
Plain (imports ✔): core::fmt::Error
Plain (imports ✖): core::fmt::Error
ByCrate(imports ✔): core::fmt::Error
ByCrate(imports ✖): core::fmt::Error
BySelf (imports ✔): core::fmt::Error
BySelf (imports ✖): core::fmt::Error
"#]],
);
// Should also work (on a best-effort basis) if `no_std` is conditional.
check_found_path(
r#"
//- /main.rs crate:main deps:core,std
#![cfg_attr(not(test), no_std)]
$0
//- /std.rs crate:std deps:core
pub mod fmt {
pub use core::fmt::Error;
}
//- /zzz.rs crate:core
pub mod fmt {
pub struct Error;
}
"#,
"core::fmt::Error",
expect![[r#"
Plain (imports ✔): core::fmt::Error
Plain (imports ✖): core::fmt::Error
ByCrate(imports ✔): core::fmt::Error
ByCrate(imports ✖): core::fmt::Error
BySelf (imports ✔): core::fmt::Error
BySelf (imports ✖): core::fmt::Error
"#]],
);
}
#[test]
fn prefer_alloc_paths_over_std() {
check_found_path(
r#"
//- /main.rs crate:main deps:alloc,std
#![no_std]
extern crate alloc;
$0
//- /std.rs crate:std deps:alloc
pub mod sync {
pub use alloc::sync::Arc;
}
//- /zzz.rs crate:alloc
pub mod sync {
pub struct Arc;
}
"#,
"alloc::sync::Arc",
expect![[r#"
Plain (imports ✔): alloc::sync::Arc
Plain (imports ✖): alloc::sync::Arc
ByCrate(imports ✔): alloc::sync::Arc
ByCrate(imports ✖): alloc::sync::Arc
BySelf (imports ✔): alloc::sync::Arc
BySelf (imports ✖): alloc::sync::Arc
"#]],
);
}
#[test]
fn prefer_shorter_paths_if_not_alloc() {
check_found_path(
r#"
//- /main.rs crate:main deps:megaalloc,std
$0
//- /std.rs crate:std deps:megaalloc
pub mod sync {
pub use megaalloc::sync::Arc;
}
//- /zzz.rs crate:megaalloc
pub struct Arc;
"#,
"megaalloc::Arc",
expect![[r#"
Plain (imports ✔): megaalloc::Arc
Plain (imports ✖): megaalloc::Arc
ByCrate(imports ✔): megaalloc::Arc
ByCrate(imports ✖): megaalloc::Arc
BySelf (imports ✔): megaalloc::Arc
BySelf (imports ✖): megaalloc::Arc
"#]],
);
}
#[test]
fn builtins_are_in_scope() {
let code = r#"
$0
pub mod primitive {
pub use u8;
}
"#;
check_found_path(
code,
"u8",
expect![[r#"
Plain (imports ✔): u8
Plain (imports ✖): u8
ByCrate(imports ✔): u8
ByCrate(imports ✖): u8
BySelf (imports ✔): u8
BySelf (imports ✖): u8
"#]],
);
check_found_path(
code,
"u16",
expect![[r#"
Plain (imports ✔): u16
Plain (imports ✖): u16
ByCrate(imports ✔): u16
ByCrate(imports ✖): u16
BySelf (imports ✔): u16
BySelf (imports ✖): u16
"#]],
);
}
#[test]
fn inner_items() {
check_found_path(
r#"
fn main() {
struct Inner {}
$0
}
"#,
"Inner",
expect![[r#"
Plain (imports ✔): Inner
Plain (imports ✖): Inner
ByCrate(imports ✔): Inner
ByCrate(imports ✖): Inner
BySelf (imports ✔): Inner
BySelf (imports ✖): Inner
"#]],
);
}
#[test]
fn inner_items_from_outer_scope() {
check_found_path(
r#"
fn main() {
struct Struct {}
{
$0
}
}
"#,
"Struct",
expect![[r#"
Plain (imports ✔): Struct
Plain (imports ✖): Struct
ByCrate(imports ✔): Struct
ByCrate(imports ✖): Struct
BySelf (imports ✔): Struct
BySelf (imports ✖): Struct
"#]],
);
}
#[test]
fn inner_items_from_inner_module() {
check_found_path(
r#"
fn main() {
mod module {
pub struct Struct {}
}
{
$0
}
}
"#,
"module::Struct",
expect![[r#"
Plain (imports ✔): module::Struct
Plain (imports ✖): module::Struct
ByCrate(imports ✔): module::Struct
ByCrate(imports ✖): module::Struct
BySelf (imports ✔): module::Struct
BySelf (imports ✖): module::Struct
"#]],
);
}
#[test]
fn outer_items_with_inner_items_present() {
check_found_path(
r#"
mod module {
pub struct CompleteMe;
}
fn main() {
fn inner() {}
$0
}
"#,
"module::CompleteMe",
expect![[r#"
Plain (imports ✔): module::CompleteMe
Plain (imports ✖): module::CompleteMe
ByCrate(imports ✔): crate::module::CompleteMe
ByCrate(imports ✖): crate::module::CompleteMe
BySelf (imports ✔): self::module::CompleteMe
BySelf (imports ✖): self::module::CompleteMe
"#]],
)
}
#[test]
fn from_inside_module() {
check_found_path(
r#"
mod baz {
pub struct Foo {}
}
mod bar {
fn bar() {
$0
}
}
"#,
"crate::baz::Foo",
expect![[r#"
Plain (imports ✔): crate::baz::Foo
Plain (imports ✖): crate::baz::Foo
ByCrate(imports ✔): crate::baz::Foo
ByCrate(imports ✖): crate::baz::Foo
BySelf (imports ✔): crate::baz::Foo
BySelf (imports ✖): crate::baz::Foo
"#]],
)
}
#[test]
fn from_inside_module2() {
check_found_path(
r#"
mod qux {
pub mod baz {
pub struct Foo {}
}
mod bar {
fn bar() {
$0;
}
}
}
"#,
"crate::qux::baz::Foo",
expect![[r#"
Plain (imports ✔): super::baz::Foo
Plain (imports ✖): super::baz::Foo
ByCrate(imports ✔): crate::qux::baz::Foo
ByCrate(imports ✖): crate::qux::baz::Foo
BySelf (imports ✔): super::baz::Foo
BySelf (imports ✖): super::baz::Foo
"#]],
)
}
#[test]
fn from_inside_module_with_inner_items() {
check_found_path(
r#"
mod baz {
pub struct Foo {}
}
mod bar {
fn bar() {
fn inner() {}
$0
}
}
"#,
"crate::baz::Foo",
expect![[r#"
Plain (imports ✔): crate::baz::Foo
Plain (imports ✖): crate::baz::Foo
ByCrate(imports ✔): crate::baz::Foo
ByCrate(imports ✖): crate::baz::Foo
BySelf (imports ✔): crate::baz::Foo
BySelf (imports ✖): crate::baz::Foo
"#]],
)
}
#[test]
fn recursive_pub_mod_reexport() {
check_found_path(
r#"
fn main() {
let _ = 22_i32.as_name$0();
}
pub mod name {
pub trait AsName {
fn as_name(&self) -> String;
}
impl AsName for i32 {
fn as_name(&self) -> String {
format!("Name: {}", self)
}
}
pub use crate::name;
}
"#,
"name::AsName",
expect![[r#"
Plain (imports ✔): name::AsName
Plain (imports ✖): name::AsName
ByCrate(imports ✔): crate::name::AsName
ByCrate(imports ✖): crate::name::AsName
BySelf (imports ✔): self::name::AsName
BySelf (imports ✖): self::name::AsName
"#]],
);
}
#[test]
fn extern_crate() {
check_found_path(
r#"
//- /main.rs crate:main deps:dep
$0
//- /dep.rs crate:dep
"#,
"dep",
expect![[r#"
Plain (imports ✔): dep
Plain (imports ✖): dep
ByCrate(imports ✔): dep
ByCrate(imports ✖): dep
BySelf (imports ✔): dep
BySelf (imports ✖): dep
"#]],
);
check_found_path(
r#"
//- /main.rs crate:main deps:dep
fn f() {
fn inner() {}
$0
}
//- /dep.rs crate:dep
"#,
"dep",
expect![[r#"
Plain (imports ✔): dep
Plain (imports ✖): dep
ByCrate(imports ✔): dep
ByCrate(imports ✖): dep
BySelf (imports ✔): dep
BySelf (imports ✖): dep
"#]],
);
}
#[test]
fn prelude_with_inner_items() {
check_found_path(
r#"
//- /main.rs edition:2018 crate:main deps:std
fn f() {
fn inner() {}
$0
}
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub enum Option { None }
pub use Option::*;
}
}
"#,
"None",
expect![[r#"
Plain (imports ✔): None
Plain (imports ✖): None
ByCrate(imports ✔): None
ByCrate(imports ✖): None
BySelf (imports ✔): None
BySelf (imports ✖): None
"#]],
);
}
#[test]
fn different_crate_renamed_through_dep() {
check_found_path(
r#"
//- /main.rs crate:main deps:intermediate
$0
//- /intermediate.rs crate:intermediate deps:std
pub extern crate std as std_renamed;
//- /std.rs crate:std
pub struct S;
"#,
"intermediate::std_renamed::S",
expect![[r#"
Plain (imports ✔): intermediate::std_renamed::S
Plain (imports ✖): intermediate::std_renamed::S
ByCrate(imports ✔): intermediate::std_renamed::S
ByCrate(imports ✖): intermediate::std_renamed::S
BySelf (imports ✔): intermediate::std_renamed::S
BySelf (imports ✖): intermediate::std_renamed::S
"#]],
);
}
#[test]
fn different_crate_doc_hidden() {
check_found_path(
r#"
//- /main.rs crate:main deps:intermediate
$0
//- /intermediate.rs crate:intermediate deps:std
#[doc(hidden)]
pub extern crate std;
pub extern crate std as longer;
//- /std.rs crate:std
pub struct S;
"#,
"intermediate::longer::S",
expect![[r#"
Plain (imports ✔): intermediate::longer::S
Plain (imports ✖): intermediate::longer::S
ByCrate(imports ✔): intermediate::longer::S
ByCrate(imports ✖): intermediate::longer::S
BySelf (imports ✔): intermediate::longer::S
BySelf (imports ✖): intermediate::longer::S
"#]],
);
}
#[test]
fn respect_doc_hidden() {
check_found_path(
r#"
//- /main.rs crate:main deps:std,lazy_static
$0
//- /lazy_static.rs crate:lazy_static deps:core
#[doc(hidden)]
pub use core::ops::Deref as __Deref;
//- /std.rs crate:std deps:core
pub use core::ops;
//- /core.rs crate:core
pub mod ops {
pub trait Deref {}
}
"#,
"std::ops::Deref",
expect![[r#"
Plain (imports ✔): std::ops::Deref
Plain (imports ✖): std::ops::Deref
ByCrate(imports ✔): std::ops::Deref
ByCrate(imports ✖): std::ops::Deref
BySelf (imports ✔): std::ops::Deref
BySelf (imports ✖): std::ops::Deref
"#]],
);
}
#[test]
fn respect_unstable_modules() {
check_found_path_prefer_no_std(
r#"
//- /main.rs crate:main deps:std,core
extern crate std;
$0
//- /longer.rs crate:std deps:core
pub mod error {
pub use core::error::Error;
}
//- /core.rs crate:core
pub mod error {
#![unstable(feature = "error_in_core", issue = "103765")]
pub trait Error {}
}
"#,
"std::error::Error",
expect![[r#"
Plain (imports ✔): std::error::Error
Plain (imports ✖): std::error::Error
ByCrate(imports ✔): std::error::Error
ByCrate(imports ✖): std::error::Error
BySelf (imports ✔): std::error::Error
BySelf (imports ✖): std::error::Error
"#]],
);
}
#[test]
fn respects_prelude_setting() {
let ra_fixture = r#"
//- /main.rs crate:main deps:krate
$0
//- /krate.rs crate:krate
pub mod prelude {
pub use crate::foo::*;
}
pub mod foo {
pub struct Foo;
}
"#;
check_found_path(
ra_fixture,
"krate::foo::Foo",
expect![[r#"
Plain (imports ✔): krate::foo::Foo
Plain (imports ✖): krate::foo::Foo
ByCrate(imports ✔): krate::foo::Foo
ByCrate(imports ✖): krate::foo::Foo
BySelf (imports ✔): krate::foo::Foo
BySelf (imports ✖): krate::foo::Foo
"#]],
);
check_found_path_prelude(
ra_fixture,
"krate::prelude::Foo",
expect![[r#"
Plain (imports ✔): krate::prelude::Foo
Plain (imports ✖): krate::prelude::Foo
ByCrate(imports ✔): krate::prelude::Foo
ByCrate(imports ✖): krate::prelude::Foo
BySelf (imports ✔): krate::prelude::Foo
BySelf (imports ✖): krate::prelude::Foo
"#]],
);
}
#[test]
fn respects_absolute_setting() {
let ra_fixture = r#"
//- /main.rs crate:main deps:krate
$0
//- /krate.rs crate:krate
pub mod foo {
pub struct Foo;
}
"#;
check_found_path(
ra_fixture,
"krate::foo::Foo",
expect![[r#"
Plain (imports ✔): krate::foo::Foo
Plain (imports ✖): krate::foo::Foo
ByCrate(imports ✔): krate::foo::Foo
ByCrate(imports ✖): krate::foo::Foo
BySelf (imports ✔): krate::foo::Foo
BySelf (imports ✖): krate::foo::Foo
"#]],
);
check_found_path_absolute(
ra_fixture,
"krate::foo::Foo",
expect![[r#"
Plain (imports ✔): ::krate::foo::Foo
Plain (imports ✖): ::krate::foo::Foo
ByCrate(imports ✔): ::krate::foo::Foo
ByCrate(imports ✖): ::krate::foo::Foo
BySelf (imports ✔): ::krate::foo::Foo
BySelf (imports ✖): ::krate::foo::Foo
"#]],
);
}
#[test]
fn respect_segment_length() {
check_found_path(
r#"
//- /main.rs crate:main deps:petgraph
$0
//- /petgraph.rs crate:petgraph
pub mod graph {
pub use crate::graph_impl::{
NodeIndex
};
}
mod graph_impl {
pub struct NodeIndex<Ix>(Ix);
}
pub mod stable_graph {
#[doc(no_inline)]
pub use crate::graph::{NodeIndex};
}
pub mod prelude {
#[doc(no_inline)]
pub use crate::graph::{NodeIndex};
}
"#,
"petgraph::graph::NodeIndex",
expect![[r#"
Plain (imports ✔): petgraph::graph::NodeIndex
Plain (imports ✖): petgraph::graph::NodeIndex
ByCrate(imports ✔): petgraph::graph::NodeIndex
ByCrate(imports ✖): petgraph::graph::NodeIndex
BySelf (imports ✔): petgraph::graph::NodeIndex
BySelf (imports ✖): petgraph::graph::NodeIndex
"#]],
);
}
#[test]
fn regression_17271() {
check_found_path(
r#"
//- /lib.rs crate:main
mod foo;
//- /foo.rs
mod bar;
pub fn b() {$0}
//- /foo/bar.rs
pub fn c() {}
"#,
"bar::c",
expect![[r#"
Plain (imports ✔): bar::c
Plain (imports ✖): bar::c
ByCrate(imports ✔): crate::foo::bar::c
ByCrate(imports ✖): crate::foo::bar::c
BySelf (imports ✔): self::bar::c
BySelf (imports ✖): self::bar::c
"#]],
);
}
#[test]
fn prefer_long_std_over_short_extern() {
check_found_path(
r#"
//- /lib.rs crate:main deps:futures_lite,std,core
$0
//- /futures_lite.rs crate:futures_lite deps:std,core
pub use crate::future::Future;
pub mod future {
pub use core::future::Future;
}
//- /std.rs crate:std deps:core
pub use core::future;
//- /core.rs crate:core
pub mod future {
pub trait Future {}
}
"#,
"core::future::Future",
expect![[r#"
Plain (imports ✔): std::future::Future
Plain (imports ✖): std::future::Future
ByCrate(imports ✔): std::future::Future
ByCrate(imports ✖): std::future::Future
BySelf (imports ✔): std::future::Future
BySelf (imports ✖): std::future::Future
"#]],
);
}
}