816: Prelude & Edition 2015 import resolution r=matklad a=flodiebold

I implemented the prelude import, but it turned out to be useless without being able to resolve any of the imports in the prelude 😅 So I had to add some edition handling and handle 2015-style imports (at least the simplified scheme proposed in rust-lang/rust#57745). So now finally `Option` resolves 😄 

One remaining problem is that we don't actually know the edition for sysroot crates. They're currently hardcoded to 2015, but there's already a bunch of PRs upgrading the editions of various rustc crates, so we'll have to detect the edition somehow, or just change the hardcoding to 2018 later, I guess...

~Also currently missing is completion for prelude names, though that shouldn't be hard to add. And `Vec` still doesn't resolve, so I need to look into that.~

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2019-02-13 20:14:39 +00:00
commit cb4327b3a9
18 changed files with 400 additions and 61 deletions

View File

@ -56,15 +56,31 @@ pub struct CyclicDependencies;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CrateId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Edition {
Edition2018,
Edition2015,
}
impl Edition {
pub fn from_string(s: &str) -> Edition {
match s {
"2015" => Edition::Edition2015,
"2018" | _ => Edition::Edition2018,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct CrateData {
file_id: FileId,
edition: Edition,
dependencies: Vec<Dependency>,
}
impl CrateData {
fn new(file_id: FileId) -> CrateData {
CrateData { file_id, dependencies: Vec::new() }
fn new(file_id: FileId, edition: Edition) -> CrateData {
CrateData { file_id, edition, dependencies: Vec::new() }
}
fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) {
@ -85,9 +101,9 @@ impl Dependency {
}
impl CrateGraph {
pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
pub fn add_crate_root(&mut self, file_id: FileId, edition: Edition) -> CrateId {
let crate_id = CrateId(self.arena.len() as u32);
let prev = self.arena.insert(crate_id, CrateData::new(file_id));
let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition));
assert!(prev.is_none());
crate_id
}
@ -112,6 +128,10 @@ impl CrateGraph {
self.arena[&crate_id].file_id
}
pub fn edition(&self, crate_id: CrateId) -> Edition {
self.arena[&crate_id].edition
}
// TODO: this only finds one crate with the given root; we could have multiple
pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?;
@ -159,14 +179,14 @@ impl CrateGraph {
#[cfg(test)]
mod tests {
use super::{CrateGraph, FileId, SmolStr};
use super::{CrateGraph, FileId, SmolStr, Edition::Edition2018};
#[test]
fn it_should_painc_because_of_cycle_dependencies() {
fn it_should_panic_because_of_cycle_dependencies() {
let mut graph = CrateGraph::default();
let crate1 = graph.add_crate_root(FileId(1u32));
let crate2 = graph.add_crate_root(FileId(2u32));
let crate3 = graph.add_crate_root(FileId(3u32));
let crate1 = graph.add_crate_root(FileId(1u32), Edition2018);
let crate2 = graph.add_crate_root(FileId(2u32), Edition2018);
let crate3 = graph.add_crate_root(FileId(3u32), Edition2018);
assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok());
assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok());
assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err());
@ -175,9 +195,9 @@ mod tests {
#[test]
fn it_works() {
let mut graph = CrateGraph::default();
let crate1 = graph.add_crate_root(FileId(1u32));
let crate2 = graph.add_crate_root(FileId(2u32));
let crate3 = graph.add_crate_root(FileId(3u32));
let crate1 = graph.add_crate_root(FileId(1u32), Edition2018);
let crate2 = graph.add_crate_root(FileId(2u32), Edition2018);
let crate3 = graph.add_crate_root(FileId(3u32), Edition2018);
assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok());
assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok());
}

View File

@ -14,7 +14,7 @@ pub use ::salsa as salsa;
pub use crate::{
cancellation::Canceled,
input::{
FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, Dependency,
FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, Dependency, Edition,
},
loc2id::LocationIntener,
};

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
use relative_path::RelativePathBuf;
use ra_db::{CrateId, FileId, SourceRootId};
use ra_db::{CrateId, FileId, SourceRootId, Edition};
use ra_syntax::{ast::self, TreeArc, SyntaxNode};
use crate::{
@ -38,13 +38,20 @@ impl Crate {
pub fn crate_id(&self) -> CrateId {
self.crate_id
}
pub fn dependencies(&self, db: &impl PersistentHirDatabase) -> Vec<CrateDependency> {
self.dependencies_impl(db)
}
pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
self.root_module_impl(db)
}
pub fn edition(&self, db: &impl PersistentHirDatabase) -> Edition {
let crate_graph = db.crate_graph();
crate_graph.edition(self.crate_id)
}
// TODO: should this be in source_binder?
pub fn source_root_crates(
db: &impl PersistentHirDatabase,

View File

@ -6,4 +6,5 @@ test_utils::marks!(
type_var_resolves_to_int_var
glob_enum
glob_across_crates
std_prelude
);

View File

@ -3,6 +3,7 @@ use std::{sync::Arc, panic};
use parking_lot::Mutex;
use ra_db::{
FilePosition, FileId, CrateGraph, SourceRoot, SourceRootId, SourceDatabase, salsa,
Edition,
};
use relative_path::RelativePathBuf;
use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
@ -58,12 +59,12 @@ impl MockDatabase {
pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) {
let mut ids = FxHashMap::default();
let mut crate_graph = CrateGraph::default();
for (crate_name, (crate_root, _)) in graph.0.iter() {
for (crate_name, (crate_root, edition, _)) in graph.0.iter() {
let crate_root = self.file_id_of(&crate_root);
let crate_id = crate_graph.add_crate_root(crate_root);
let crate_id = crate_graph.add_crate_root(crate_root, *edition);
ids.insert(crate_name, crate_id);
}
for (crate_name, (_, deps)) in graph.0.iter() {
for (crate_name, (_, _, deps)) in graph.0.iter() {
let from = ids[crate_name];
for dep in deps {
let to = ids[dep];
@ -144,7 +145,7 @@ impl MockDatabase {
if is_crate_root {
let mut crate_graph = CrateGraph::default();
crate_graph.add_crate_root(file_id);
crate_graph.add_crate_root(file_id, Edition::Edition2018);
self.set_crate_graph(Arc::new(crate_graph));
}
file_id
@ -232,16 +233,19 @@ impl MockDatabase {
}
#[derive(Default)]
pub struct CrateGraphFixture(pub FxHashMap<String, (String, Vec<String>)>);
pub struct CrateGraphFixture(pub FxHashMap<String, (String, Edition, Vec<String>)>);
#[macro_export]
macro_rules! crate_graph {
($($crate_name:literal: ($crate_path:literal, [$($dep:literal),*]),)*) => {{
($($crate_name:literal: ($crate_path:literal, $($edition:literal,)? [$($dep:literal),*]),)*) => {{
let mut res = $crate::mock::CrateGraphFixture::default();
$(
#[allow(unused_mut, unused_assignments)]
let mut edition = ra_db::Edition::Edition2018;
$(edition = ra_db::Edition::from_string($edition);)?
res.0.insert(
$crate_name.to_string(),
($crate_path.to_string(), vec![$($dep.to_string()),*])
($crate_path.to_string(), edition, vec![$($dep.to_string()),*])
);
)*
res

View File

@ -18,10 +18,12 @@ pub(crate) mod lower;
use std::{time, sync::Arc};
use ra_arena::map::ArenaMap;
use test_utils::tested_by;
use rustc_hash::{FxHashMap, FxHashSet};
use ra_arena::map::ArenaMap;
use ra_db::Edition;
use test_utils::tested_by;
use crate::{
Module, ModuleDef,
Path, PathKind, PersistentHirDatabase,
@ -32,8 +34,13 @@ use crate::{
/// `ItemMap` is the result of module name resolution. It contains, for each
/// module, the set of visible items.
#[derive(Default, Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct ItemMap {
edition: Edition,
/// The prelude module for this crate. This either comes from an import
/// marked with the `prelude_import` attribute, or (in the normal case) from
/// a dependency (`std` or `core`).
pub(crate) prelude: Option<Module>,
pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
per_module: ArenaMap<ModuleId, ModuleScope>,
}
@ -176,7 +183,12 @@ where
module_tree,
processed_imports: FxHashSet::default(),
glob_imports: FxHashMap::default(),
result: ItemMap::default(),
result: ItemMap {
edition: krate.edition(db),
prelude: None,
extern_prelude: FxHashMap::default(),
per_module: ArenaMap::default(),
},
}
}
@ -211,6 +223,13 @@ where
if let Some(module) = dep.krate.root_module(self.db) {
self.result.extern_prelude.insert(dep.name.clone(), module.into());
}
// look for the prelude
if self.result.prelude.is_none() {
let item_map = self.db.item_map(dep.krate);
if item_map.prelude.is_some() {
self.result.prelude = item_map.prelude;
}
}
}
}
@ -266,10 +285,20 @@ where
import_id: ImportId,
import: &ImportData,
) -> ReachedFixedPoint {
log::debug!("resolving import: {:?}", import);
log::debug!("resolving import: {:?} ({:?})", import, self.result.edition);
let original_module = Module { krate: self.krate, module_id };
let (def, reached_fixedpoint) =
self.result.resolve_path_fp(self.db, original_module, &import.path);
let (def, reached_fixedpoint) = if import.is_extern_crate {
let res = self.result.resolve_name_in_extern_prelude(
&import
.path
.as_ident()
.expect("extern crate should have been desugared to one-element path"),
);
(res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes })
} else {
self.result.resolve_path_fp(self.db, ResolveMode::Import, original_module, &import.path)
};
if reached_fixedpoint != ReachedFixedPoint::Yes {
return reached_fixedpoint;
@ -279,7 +308,10 @@ where
log::debug!("glob import: {:?}", import);
match def.take_types() {
Some(ModuleDef::Module(m)) => {
if m.krate != self.krate {
if import.is_prelude {
tested_by!(std_prelude);
self.result.prelude = Some(m);
} else if m.krate != self.krate {
tested_by!(glob_across_crates);
// glob import from other crate => we can just import everything once
let item_map = self.db.item_map(m.krate);
@ -403,6 +435,12 @@ where
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ResolveMode {
Import,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReachedFixedPoint {
Yes,
@ -431,15 +469,61 @@ impl ItemMap {
original_module: Module,
path: &Path,
) -> PerNs<ModuleDef> {
self.resolve_path_fp(db, original_module, path).0
self.resolve_path_fp(db, ResolveMode::Other, original_module, path).0
}
pub(crate) fn resolve_name_in_module(&self, module: Module, name: &Name) -> PerNs<ModuleDef> {
fn resolve_in_prelude(
&self,
db: &impl PersistentHirDatabase,
original_module: Module,
name: &Name,
) -> PerNs<ModuleDef> {
if let Some(prelude) = self.prelude {
let resolution = if prelude.krate == original_module.krate {
self[prelude.module_id].items.get(name).cloned()
} else {
db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned()
};
resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
} else {
PerNs::none()
}
}
pub(crate) fn resolve_name_in_module(
&self,
db: &impl PersistentHirDatabase,
module: Module,
name: &Name,
) -> PerNs<ModuleDef> {
// Resolve in:
// - current module / scope
// - extern prelude
// - std prelude
let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
let from_extern_prelude =
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
let from_prelude = self.resolve_in_prelude(db, module, name);
from_scope.or(from_extern_prelude)
from_scope.or(from_extern_prelude).or(from_prelude)
}
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
}
fn resolve_name_in_crate_root_or_extern_prelude(
&self,
db: &impl PersistentHirDatabase,
module: Module,
name: &Name,
) -> PerNs<ModuleDef> {
let crate_root = module.crate_root(db);
let from_crate_root =
self[crate_root.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
from_crate_root.or(from_extern_prelude)
}
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
@ -447,6 +531,7 @@ impl ItemMap {
fn resolve_path_fp(
&self,
db: &impl PersistentHirDatabase,
mode: ResolveMode,
original_module: Module,
path: &Path,
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
@ -454,12 +539,32 @@ impl ItemMap {
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
PathKind::Self_ => PerNs::types(original_module.into()),
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
// TODO there must be a nicer way to write this condition
PathKind::Plain | PathKind::Abs
if self.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
{
let segment = match segments.next() {
Some((_, segment)) => segment,
None => return (PerNs::none(), ReachedFixedPoint::Yes),
};
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
self.resolve_name_in_crate_root_or_extern_prelude(
db,
original_module,
&segment.name,
)
}
PathKind::Plain => {
let segment = match segments.next() {
Some((_, segment)) => segment,
None => return (PerNs::none(), ReachedFixedPoint::Yes),
};
self.resolve_name_in_module(original_module, &segment.name)
log::debug!("resolving {:?} in module", segment);
self.resolve_name_in_module(db, original_module, &segment.name)
}
PathKind::Super => {
if let Some(p) = original_module.parent(db) {

View File

@ -2,13 +2,13 @@ use std::sync::Arc;
use ra_syntax::{
AstNode, SourceFile, TreeArc, AstPtr,
ast::{self, ModuleItemOwner, NameOwner},
ast::{self, ModuleItemOwner, NameOwner, AttrsOwner},
};
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
use rustc_hash::FxHashMap;
use crate::{
SourceItemId, Path, PathKind, ModuleSource, Name,
SourceItemId, Path, ModuleSource, Name,
HirFileId, MacroCallLoc, AsName, PerNs, Function,
ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type,
ids::LocationCtx, PersistentHirDatabase,
@ -23,6 +23,7 @@ pub(super) struct ImportData {
pub(super) path: Path,
pub(super) alias: Option<Name>,
pub(super) is_glob: bool,
pub(super) is_prelude: bool,
pub(super) is_extern_crate: bool,
}
@ -179,18 +180,14 @@ impl LoweredModule {
self.add_use_item(source_map, it);
}
ast::ModuleItemKind::ExternCrateItem(it) => {
// Lower `extern crate x` to `use ::x`. This is kind of cheating
// and only works if we always interpret absolute paths in the
// 2018 style; otherwise `::x` could also refer to a module in
// the crate root.
if let Some(name_ref) = it.name_ref() {
let mut path = Path::from_name_ref(name_ref);
path.kind = PathKind::Abs;
let path = Path::from_name_ref(name_ref);
let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name);
self.imports.alloc(ImportData {
path,
alias,
is_glob: false,
is_prelude: false,
is_extern_crate: true,
});
}
@ -214,11 +211,14 @@ impl LoweredModule {
}
fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) {
let is_prelude =
item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false));
Path::expand_use_item(item, |path, segment, alias| {
let import = self.imports.alloc(ImportData {
path,
alias,
is_glob: segment.is_none(),
is_prelude,
is_extern_crate: false,
});
if let Some(segment) = segment {

View File

@ -265,6 +265,45 @@ fn glob_across_crates() {
);
}
#[test]
fn edition_2015_imports() {
let mut db = MockDatabase::with_files(
"
//- /main.rs
mod foo;
mod bar;
//- /bar.rs
struct Bar;
//- /foo.rs
use bar::Bar;
use other_crate::FromLib;
//- /lib.rs
struct FromLib;
",
);
db.set_crate_graph_from_fixture(crate_graph! {
"main": ("/main.rs", "2015", ["other_crate"]),
"other_crate": ("/lib.rs", "2018", []),
});
let foo_id = db.file_id_of("/foo.rs");
let module = crate::source_binder::module_from_file_id(&db, foo_id).unwrap();
let krate = module.krate(&db).unwrap();
let item_map = db.item_map(krate);
check_module_item_map(
&item_map,
module.module_id,
"
Bar: t v
FromLib: t v
",
);
}
#[test]
fn module_resolution_works_for_non_standard_filenames() {
let mut db = MockDatabase::with_files(
@ -296,6 +335,43 @@ fn module_resolution_works_for_non_standard_filenames() {
);
}
#[test]
fn std_prelude() {
covers!(std_prelude);
let mut db = MockDatabase::with_files(
"
//- /main.rs
use Foo::*;
//- /lib.rs
mod prelude;
#[prelude_import]
use prelude::*;
//- /prelude.rs
pub enum Foo { Bar, Baz };
",
);
db.set_crate_graph_from_fixture(crate_graph! {
"main": ("/main.rs", ["test_crate"]),
"test_crate": ("/lib.rs", []),
});
let main_id = db.file_id_of("/main.rs");
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
let krate = module.krate(&db).unwrap();
let item_map = db.item_map(krate);
check_module_item_map(
&item_map,
module.module_id,
"
Bar: t v
Baz: t v
",
);
}
#[test]
fn name_res_works_for_broken_modules() {
covers!(name_res_works_for_broken_modules);
@ -466,6 +542,42 @@ fn extern_crate_rename() {
);
}
#[test]
fn extern_crate_rename_2015_edition() {
let mut db = MockDatabase::with_files(
"
//- /main.rs
extern crate alloc as alloc_crate;
mod alloc;
mod sync;
//- /sync.rs
use alloc_crate::Arc;
//- /lib.rs
struct Arc;
",
);
db.set_crate_graph_from_fixture(crate_graph! {
"main": ("/main.rs", "2015", ["alloc"]),
"alloc": ("/lib.rs", []),
});
let sync_id = db.file_id_of("/sync.rs");
let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap();
let krate = module.krate(&db).unwrap();
let item_map = db.item_map(krate);
check_module_item_map(
&item_map,
module.module_id,
"
Arc: t v
",
);
}
#[test]
fn import_across_source_roots() {
let mut db = MockDatabase::with_files(

View File

@ -56,10 +56,10 @@ pub enum Resolution {
}
impl Resolver {
pub fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
pub fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
let mut resolution = PerNs::none();
for scope in self.scopes.iter().rev() {
resolution = resolution.or(scope.resolve_name(name));
resolution = resolution.or(scope.resolve_name(db, name));
if resolution.is_both() {
return resolution;
}
@ -69,9 +69,9 @@ impl Resolver {
pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
if let Some(name) = path.as_ident() {
self.resolve_name(name)
self.resolve_name(db, name)
} else if path.is_self() {
self.resolve_name(&Name::self_param())
self.resolve_name(db, &Name::self_param())
} else {
let (item_map, module) = match self.module() {
Some(m) => m,
@ -82,10 +82,10 @@ impl Resolver {
}
}
pub fn all_names(&self) -> FxHashMap<Name, PerNs<Resolution>> {
pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> {
let mut names = FxHashMap::default();
for scope in self.scopes.iter().rev() {
scope.collect_names(&mut |name, res| {
scope.collect_names(db, &mut |name, res| {
let current: &mut PerNs<Resolution> = names.entry(name).or_default();
if current.types.is_none() {
current.types = res.types;
@ -143,13 +143,13 @@ impl Resolver {
}
impl Scope {
fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
match self {
Scope::ModuleScope(m) => {
if let Some(KnownName::SelfParam) = name.as_known_name() {
PerNs::types(Resolution::Def(m.module.into()))
} else {
m.item_map.resolve_name_in_module(m.module, name).map(Resolution::Def)
m.item_map.resolve_name_in_module(db, m.module, name).map(Resolution::Def)
}
}
Scope::GenericParams(gp) => match gp.find_by_name(name) {
@ -174,7 +174,7 @@ impl Scope {
}
}
fn collect_names(&self, f: &mut dyn FnMut(Name, PerNs<Resolution>)) {
fn collect_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, PerNs<Resolution>)) {
match self {
Scope::ModuleScope(m) => {
// TODO: should we provide `self` here?
@ -190,6 +190,12 @@ impl Scope {
m.item_map.extern_prelude.iter().for_each(|(name, def)| {
f(name.clone(), PerNs::types(Resolution::Def(*def)));
});
if let Some(prelude) = m.item_map.prelude {
let prelude_item_map = db.item_map(prelude.krate);
prelude_item_map[prelude.module_id].entries().for_each(|(name, res)| {
f(name.clone(), res.def.map(Resolution::Def));
});
}
}
Scope::GenericParams(gp) => {
for param in &gp.params {

View File

@ -4,7 +4,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.is_trivial_path {
return;
}
let names = ctx.resolver.all_names();
let names = ctx.resolver.all_names(ctx.db);
names.into_iter().for_each(|(name, res)| {
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
@ -165,4 +165,23 @@ mod tests {
fn completes_self_in_methods() {
check_reference_completion("self_in_methods", r"impl S { fn foo(&self) { <|> } }")
}
#[test]
fn completes_prelude() {
check_reference_completion(
"completes_prelude",
"
//- /main.rs
fn foo() { let x: <|> }
//- /std/lib.rs
#[prelude_import]
use prelude::*;
mod prelude {
struct Option;
}
",
);
}
}

View File

@ -0,0 +1,54 @@
---
created: "2019-02-13T19:52:43.734834624Z"
creator: insta@0.6.2
source: crates/ra_ide_api/src/completion/completion_item.rs
expression: kind_completions
---
[
CompletionItem {
completion_kind: Reference,
label: "Option",
kind: Some(
Struct
),
detail: None,
documentation: None,
lookup: None,
insert_text: None,
insert_text_format: PlainText,
source_range: [18; 18),
text_edit: None
},
CompletionItem {
completion_kind: Reference,
label: "foo",
kind: Some(
Function
),
detail: Some(
"fn foo()"
),
documentation: None,
lookup: None,
insert_text: Some(
"foo()$0"
),
insert_text_format: Snippet,
source_range: [18; 18),
text_edit: None
},
CompletionItem {
completion_kind: Reference,
label: "std",
kind: Some(
Module
),
detail: None,
documentation: None,
lookup: None,
insert_text: None,
insert_text_format: PlainText,
source_range: [18; 18),
text_edit: None
}
]

View File

@ -62,7 +62,8 @@ pub use ra_ide_api_light::{
LineIndex, LineCol, translate_offset_with_edit,
};
pub use ra_db::{
Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId
Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId,
Edition
};
pub use hir::Documentation;

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use relative_path::RelativePathBuf;
use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
use crate::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FilePosition, FileRange, SourceRootId};
use crate::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FilePosition, FileRange, SourceRootId, Edition::Edition2018};
/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
/// from a set of in-memory files.
@ -89,9 +89,9 @@ impl MockAnalysis {
let path = RelativePathBuf::from_path(&path[1..]).unwrap();
let file_id = FileId(i as u32 + 1);
if path == "/lib.rs" || path == "/main.rs" {
root_crate = Some(crate_graph.add_crate_root(file_id));
root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018));
} else if path.ends_with("/lib.rs") {
let other_crate = crate_graph.add_crate_root(file_id);
let other_crate = crate_graph.add_crate_root(file_id, Edition2018);
let crate_name = path.parent().unwrap().file_name().unwrap();
if let Some(root_crate) = root_crate {
crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap();

View File

@ -1,7 +1,7 @@
use insta::assert_debug_snapshot_matches;
use ra_ide_api::{
mock_analysis::{single_file, single_file_with_position, MockAnalysis},
AnalysisChange, CrateGraph, FileId, Query, NavigationTarget,
AnalysisChange, CrateGraph, Edition::Edition2018, FileId, Query, NavigationTarget
};
use ra_syntax::{TextRange, SmolStr};
@ -36,7 +36,7 @@ fn test_resolve_crate_root() {
assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
let mut crate_graph = CrateGraph::default();
let crate_id = crate_graph.add_crate_root(root_file);
let crate_id = crate_graph.add_crate_root(root_file, Edition2018);
let mut change = AnalysisChange::new();
change.set_crate_graph(crate_graph);
host.apply_change(change);

View File

@ -4,6 +4,7 @@ use cargo_metadata::{MetadataCommand, CargoOpt};
use ra_arena::{Arena, RawId, impl_arena_id};
use rustc_hash::FxHashMap;
use failure::format_err;
use ra_db::Edition;
use crate::Result;
@ -35,6 +36,7 @@ struct PackageData {
targets: Vec<Target>,
is_member: bool,
dependencies: Vec<PackageDependency>,
edition: Edition,
}
#[derive(Debug, Clone)]
@ -84,6 +86,9 @@ impl Package {
pub fn root(self, ws: &CargoWorkspace) -> &Path {
ws.packages[self].manifest.parent().unwrap()
}
pub fn edition(self, ws: &CargoWorkspace) -> Edition {
ws.packages[self].edition
}
pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
ws.packages[self].targets.iter().cloned()
}
@ -135,6 +140,7 @@ impl CargoWorkspace {
manifest: meta_pkg.manifest_path.clone(),
targets: Vec::new(),
is_member,
edition: Edition::from_string(&meta_pkg.edition),
dependencies: Vec::new(),
});
let pkg_data = &mut packages[pkg];

View File

@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use failure::bail;
use rustc_hash::FxHashMap;
use ra_db::{CrateGraph, FileId};
use ra_db::{CrateGraph, FileId, Edition};
pub use crate::{
cargo_workspace::{CargoWorkspace, Package, Target, TargetKind},
@ -36,7 +36,8 @@ impl ProjectWorkspace {
let mut sysroot_crates = FxHashMap::default();
for krate in self.sysroot.crates() {
if let Some(file_id) = load(krate.root(&self.sysroot)) {
sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
sysroot_crates
.insert(krate, crate_graph.add_crate_root(file_id, Edition::Edition2015));
}
}
for from in self.sysroot.crates() {
@ -62,7 +63,8 @@ impl ProjectWorkspace {
for tgt in pkg.targets(&self.cargo) {
let root = tgt.root(&self.cargo);
if let Some(file_id) = load(root) {
let crate_id = crate_graph.add_crate_root(file_id);
let edition = pkg.edition(&self.cargo);
let crate_id = crate_graph.add_crate_root(file_id, edition);
if tgt.kind(&self.cargo) == TargetKind::Lib {
lib_tgt = Some(crate_id);
pkg_to_lib_crate.insert(pkg, crate_id);

View File

@ -4210,6 +4210,7 @@ impl ToOwned for UseItem {
}
impl ast::AttrsOwner for UseItem {}
impl UseItem {
pub fn use_tree(&self) -> Option<&UseTree> {
super::child_opt(self)

View File

@ -596,7 +596,8 @@ Grammar(
options: [ "Pat", "TypeRef" ],
),
"UseItem": (
options: [ "UseTree" ]
traits: ["AttrsOwner"],
options: [ "UseTree" ],
),
"UseTree": (
options: [ "Path", "UseTreeList", "Alias" ]