mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Merge #742
742: Extern crate r=matklad a=flodiebold This implements `extern crate` declarations by lowering them to (absolute) imports, and adds support for absolute paths. It also extracts the extern prelude from the per-module item map, and handles the special case of extern crates in the crate root adding to the extern prelude. This means we finally resolve `Arc`, so it fixes #523 😄 Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
4d4c46aff8
14 changed files with 190 additions and 48 deletions
|
@ -134,7 +134,7 @@ impl Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the crate this module is part of.
|
/// Returns the crate this module is part of.
|
||||||
pub fn krate(&self, _db: &impl HirDatabase) -> Option<Crate> {
|
pub fn krate(&self, _db: &impl PersistentHirDatabase) -> Option<Crate> {
|
||||||
Some(self.krate)
|
Some(self.krate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ use crate::{
|
||||||
/// module, the set of visible items.
|
/// module, the set of visible items.
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
pub struct ItemMap {
|
pub struct ItemMap {
|
||||||
|
pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
|
||||||
per_module: ArenaMap<ModuleId, ModuleScope>,
|
per_module: ArenaMap<ModuleId, ModuleScope>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +205,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve(mut self) -> ItemMap {
|
pub(crate) fn resolve(mut self) -> ItemMap {
|
||||||
|
self.populate_extern_prelude();
|
||||||
for (&module_id, items) in self.input.iter() {
|
for (&module_id, items) in self.input.iter() {
|
||||||
self.populate_module(module_id, Arc::clone(items));
|
self.populate_module(module_id, Arc::clone(items));
|
||||||
}
|
}
|
||||||
|
@ -227,29 +229,19 @@ where
|
||||||
self.result
|
self.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn populate_extern_prelude(&mut self) {
|
||||||
|
for dep in self.krate.dependencies(self.db) {
|
||||||
|
log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
|
||||||
|
if let Some(module) = dep.krate.root_module(self.db) {
|
||||||
|
self.result
|
||||||
|
.extern_prelude
|
||||||
|
.insert(dep.name.clone(), module.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) {
|
fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) {
|
||||||
let mut module_items = ModuleScope::default();
|
let mut module_items = ModuleScope::default();
|
||||||
|
|
||||||
// Populate extern crates prelude
|
|
||||||
{
|
|
||||||
let root_id = module_id.crate_root(&self.module_tree);
|
|
||||||
let file_id = root_id.file_id(&self.module_tree);
|
|
||||||
let crate_graph = self.db.crate_graph();
|
|
||||||
if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
|
|
||||||
{
|
|
||||||
let krate = Crate { crate_id };
|
|
||||||
for dep in krate.dependencies(self.db) {
|
|
||||||
if let Some(module) = dep.krate.root_module(self.db) {
|
|
||||||
let def = module.into();
|
|
||||||
self.add_module_item(
|
|
||||||
&mut module_items,
|
|
||||||
dep.name.clone(),
|
|
||||||
PerNs::types(def),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
for (import_id, import_data) in input.imports.iter() {
|
for (import_id, import_data) in input.imports.iter() {
|
||||||
if let Some(last_segment) = import_data.path.segments.iter().last() {
|
if let Some(last_segment) = import_data.path.segments.iter().last() {
|
||||||
if !import_data.is_glob {
|
if !import_data.is_glob {
|
||||||
|
@ -327,7 +319,16 @@ where
|
||||||
.alias
|
.alias
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| last_segment.name.clone());
|
.unwrap_or_else(|| last_segment.name.clone());
|
||||||
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def,);
|
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
|
||||||
|
|
||||||
|
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
|
||||||
|
if let Some(root_module) = self.krate.root_module(self.db) {
|
||||||
|
if import.is_extern_crate && module_id == root_module.module_id {
|
||||||
|
if let Some(def) = def.take_types() {
|
||||||
|
self.result.extern_prelude.insert(name.clone(), def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
self.update(module_id, |items| {
|
self.update(module_id, |items| {
|
||||||
let res = Resolution {
|
let res = Resolution {
|
||||||
def,
|
def,
|
||||||
|
@ -389,24 +390,53 @@ impl ItemMap {
|
||||||
original_module: Module,
|
original_module: Module,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
|
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
|
||||||
let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind {
|
let mut segments = path.segments.iter().enumerate();
|
||||||
PathKind::Crate => original_module.crate_root(db).into(),
|
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
|
||||||
PathKind::Self_ | PathKind::Plain => original_module.into(),
|
PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
|
||||||
|
PathKind::Self_ => PerNs::types(original_module.into()),
|
||||||
|
PathKind::Plain => {
|
||||||
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return (PerNs::none(), ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
// Resolve in:
|
||||||
|
// - current module / scope
|
||||||
|
// - extern prelude
|
||||||
|
match self[original_module.module_id].items.get(&segment.name) {
|
||||||
|
Some(res) if !res.def.is_none() => res.def,
|
||||||
|
_ => {
|
||||||
|
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
||||||
|
PerNs::types(*def)
|
||||||
|
} else {
|
||||||
|
return (PerNs::none(), ReachedFixedPoint::No);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
PathKind::Super => {
|
PathKind::Super => {
|
||||||
if let Some(p) = original_module.parent(db) {
|
if let Some(p) = original_module.parent(db) {
|
||||||
p.into()
|
PerNs::types(p.into())
|
||||||
} else {
|
} else {
|
||||||
log::debug!("super path in root module");
|
log::debug!("super path in root module");
|
||||||
return (PerNs::none(), ReachedFixedPoint::Yes);
|
return (PerNs::none(), ReachedFixedPoint::Yes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PathKind::Abs => {
|
PathKind::Abs => {
|
||||||
// TODO: absolute use is not supported
|
// 2018-style absolute path -- only extern prelude
|
||||||
return (PerNs::none(), ReachedFixedPoint::Yes);
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return (PerNs::none(), ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
||||||
|
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||||
|
PerNs::types(*def)
|
||||||
|
} else {
|
||||||
|
return (PerNs::none(), ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
for (i, segment) in path.segments.iter().enumerate() {
|
for (i, segment) in segments {
|
||||||
let curr = match curr_per_ns.as_ref().take_types() {
|
let curr = match curr_per_ns.as_ref().take_types() {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
SourceItemId, Path, ModuleSource, Name,
|
SourceItemId, Path, PathKind, ModuleSource, Name,
|
||||||
HirFileId, MacroCallLoc, AsName, PerNs, Function,
|
HirFileId, MacroCallLoc, AsName, PerNs, Function,
|
||||||
ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type,
|
ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type,
|
||||||
ids::LocationCtx, PersistentHirDatabase,
|
ids::LocationCtx, PersistentHirDatabase,
|
||||||
|
@ -23,6 +23,7 @@ pub(super) struct ImportData {
|
||||||
pub(super) path: Path,
|
pub(super) path: Path,
|
||||||
pub(super) alias: Option<Name>,
|
pub(super) alias: Option<Name>,
|
||||||
pub(super) is_glob: bool,
|
pub(super) is_glob: bool,
|
||||||
|
pub(super) is_extern_crate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of items and imports declared inside a module, without relation to
|
/// A set of items and imports declared inside a module, without relation to
|
||||||
|
@ -186,8 +187,22 @@ impl LoweredModule {
|
||||||
ast::ModuleItemKind::UseItem(it) => {
|
ast::ModuleItemKind::UseItem(it) => {
|
||||||
self.add_use_item(source_map, it);
|
self.add_use_item(source_map, it);
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::ExternCrateItem(_) => {
|
ast::ModuleItemKind::ExternCrateItem(it) => {
|
||||||
// TODO
|
// 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 alias = it.alias().and_then(|a| a.name()).map(AsName::as_name);
|
||||||
|
self.imports.alloc(ImportData {
|
||||||
|
path,
|
||||||
|
alias,
|
||||||
|
is_glob: false,
|
||||||
|
is_extern_crate: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::ConstDef(it) => {
|
ast::ModuleItemKind::ConstDef(it) => {
|
||||||
if let Some(name) = it.name() {
|
if let Some(name) = it.name() {
|
||||||
|
@ -215,6 +230,7 @@ impl LoweredModule {
|
||||||
path,
|
path,
|
||||||
alias,
|
alias,
|
||||||
is_glob: segment.is_none(),
|
is_glob: segment.is_none(),
|
||||||
|
is_extern_crate: false,
|
||||||
});
|
});
|
||||||
if let Some(segment) = segment {
|
if let Some(segment) = segment {
|
||||||
source_map.insert(import, segment)
|
source_map.insert(import, segment)
|
||||||
|
|
|
@ -329,7 +329,49 @@ fn item_map_across_crates() {
|
||||||
module.module_id,
|
module.module_id,
|
||||||
"
|
"
|
||||||
Baz: t v
|
Baz: t v
|
||||||
test_crate: t
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extern_crate_rename() {
|
||||||
|
let (mut db, sr) = 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;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let main_id = sr.files[RelativePath::new("/main.rs")];
|
||||||
|
let sync_id = sr.files[RelativePath::new("/sync.rs")];
|
||||||
|
let lib_id = sr.files[RelativePath::new("/lib.rs")];
|
||||||
|
|
||||||
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
let main_crate = crate_graph.add_crate_root(main_id);
|
||||||
|
let lib_crate = crate_graph.add_crate_root(lib_id);
|
||||||
|
crate_graph
|
||||||
|
.add_dep(main_crate, "alloc".into(), lib_crate)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
db.set_crate_graph(Arc::new(crate_graph));
|
||||||
|
|
||||||
|
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
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -361,8 +403,6 @@ fn import_across_source_roots() {
|
||||||
|
|
||||||
let main_id = sr2.files[RelativePath::new("/main.rs")];
|
let main_id = sr2.files[RelativePath::new("/main.rs")];
|
||||||
|
|
||||||
eprintln!("lib = {:?}, main = {:?}", lib_id, main_id);
|
|
||||||
|
|
||||||
let mut crate_graph = CrateGraph::default();
|
let mut crate_graph = CrateGraph::default();
|
||||||
let main_crate = crate_graph.add_crate_root(main_id);
|
let main_crate = crate_graph.add_crate_root(main_id);
|
||||||
let lib_crate = crate_graph.add_crate_root(lib_id);
|
let lib_crate = crate_graph.add_crate_root(lib_id);
|
||||||
|
@ -381,7 +421,6 @@ fn import_across_source_roots() {
|
||||||
module.module_id,
|
module.module_id,
|
||||||
"
|
"
|
||||||
C: t v
|
C: t v
|
||||||
test_crate: t
|
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -423,7 +462,6 @@ fn reexport_across_crates() {
|
||||||
module.module_id,
|
module.module_id,
|
||||||
"
|
"
|
||||||
Baz: t v
|
Baz: t v
|
||||||
test_crate: t
|
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,7 +197,10 @@ impl Scope {
|
||||||
.entries()
|
.entries()
|
||||||
.for_each(|(name, res)| {
|
.for_each(|(name, res)| {
|
||||||
f(name.clone(), res.def.map(Resolution::Def));
|
f(name.clone(), res.def.map(Resolution::Def));
|
||||||
})
|
});
|
||||||
|
m.item_map.extern_prelude.iter().for_each(|(name, def)| {
|
||||||
|
f(name.clone(), PerNs::types(Resolution::Def(*def)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Scope::GenericParams(gp) => {
|
Scope::GenericParams(gp) => {
|
||||||
for param in &gp.params {
|
for param in &gp.params {
|
||||||
|
|
|
@ -110,6 +110,20 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_extern_prelude() {
|
||||||
|
check_reference_completion(
|
||||||
|
"extern_prelude",
|
||||||
|
r"
|
||||||
|
//- /lib.rs
|
||||||
|
use <|>;
|
||||||
|
|
||||||
|
//- /other_crate/lib.rs
|
||||||
|
// nothing here
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_module_items_in_nested_modules() {
|
fn completes_module_items_in_nested_modules() {
|
||||||
check_reference_completion(
|
check_reference_completion(
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
created: "2019-02-04T21:08:32.615556587+00:00"
|
||||||
|
creator: insta@0.5.3
|
||||||
|
expression: kind_completions
|
||||||
|
source: crates/ra_ide_api/src/completion/completion_item.rs
|
||||||
|
---
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
completion_kind: Reference,
|
||||||
|
label: "other_crate",
|
||||||
|
kind: Some(
|
||||||
|
Module
|
||||||
|
),
|
||||||
|
detail: None,
|
||||||
|
documentation: None,
|
||||||
|
lookup: None,
|
||||||
|
insert_text: None,
|
||||||
|
insert_text_format: PlainText,
|
||||||
|
source_range: [4; 4),
|
||||||
|
text_edit: None
|
||||||
|
}
|
||||||
|
]
|
|
@ -86,17 +86,25 @@ impl MockAnalysis {
|
||||||
let mut change = AnalysisChange::new();
|
let mut change = AnalysisChange::new();
|
||||||
change.add_root(source_root, true);
|
change.add_root(source_root, true);
|
||||||
let mut crate_graph = CrateGraph::default();
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
let mut root_crate = None;
|
||||||
for (i, (path, contents)) in self.files.into_iter().enumerate() {
|
for (i, (path, contents)) in self.files.into_iter().enumerate() {
|
||||||
assert!(path.starts_with('/'));
|
assert!(path.starts_with('/'));
|
||||||
let path = RelativePathBuf::from_path(&path[1..]).unwrap();
|
let path = RelativePathBuf::from_path(&path[1..]).unwrap();
|
||||||
let file_id = FileId(i as u32 + 1);
|
let file_id = FileId(i as u32 + 1);
|
||||||
if path == "/lib.rs" || path == "/main.rs" {
|
if path == "/lib.rs" || path == "/main.rs" {
|
||||||
crate_graph.add_crate_root(file_id);
|
root_crate = Some(crate_graph.add_crate_root(file_id));
|
||||||
|
} else if path.ends_with("/lib.rs") {
|
||||||
|
let other_crate = crate_graph.add_crate_root(file_id);
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
change.add_file(source_root, file_id, path, Arc::new(contents));
|
change.add_file(source_root, file_id, path, Arc::new(contents));
|
||||||
}
|
}
|
||||||
change.set_crate_graph(crate_graph);
|
change.set_crate_graph(crate_graph);
|
||||||
// change.set_file_resolver(Arc::new(file_map));
|
|
||||||
host.apply_change(change);
|
host.apply_change(change);
|
||||||
host
|
host
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,7 @@ rustc_tsan
|
||||||
syntax";
|
syntax";
|
||||||
|
|
||||||
const STD_DEPS: &str = "
|
const STD_DEPS: &str = "
|
||||||
|
alloc
|
||||||
alloc_jemalloc
|
alloc_jemalloc
|
||||||
alloc_system
|
alloc_system
|
||||||
core
|
core
|
||||||
|
|
|
@ -970,7 +970,15 @@ impl ToOwned for ExternCrateItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl ExternCrateItem {}
|
impl ExternCrateItem {
|
||||||
|
pub fn name_ref(&self) -> Option<&NameRef> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alias(&self) -> Option<&Alias> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FalseKw
|
// FalseKw
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -601,7 +601,9 @@ Grammar(
|
||||||
"UseTreeList": (
|
"UseTreeList": (
|
||||||
collections: [["use_trees", "UseTree"]]
|
collections: [["use_trees", "UseTree"]]
|
||||||
),
|
),
|
||||||
"ExternCrateItem": (),
|
"ExternCrateItem": (
|
||||||
|
options: ["NameRef", "Alias"],
|
||||||
|
),
|
||||||
"ArgList": (
|
"ArgList": (
|
||||||
collections: [
|
collections: [
|
||||||
["args", "Expr"]
|
["args", "Expr"]
|
||||||
|
|
|
@ -247,7 +247,7 @@ fn extern_crate_item(p: &mut Parser) {
|
||||||
p.bump();
|
p.bump();
|
||||||
assert!(p.at(CRATE_KW));
|
assert!(p.at(CRATE_KW));
|
||||||
p.bump();
|
p.bump();
|
||||||
name(p);
|
name_ref(p);
|
||||||
opt_alias(p);
|
opt_alias(p);
|
||||||
p.expect(SEMI);
|
p.expect(SEMI);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ SOURCE_FILE@[0; 18)
|
||||||
WHITESPACE@[6; 7)
|
WHITESPACE@[6; 7)
|
||||||
CRATE_KW@[7; 12)
|
CRATE_KW@[7; 12)
|
||||||
WHITESPACE@[12; 13)
|
WHITESPACE@[12; 13)
|
||||||
NAME@[13; 16)
|
NAME_REF@[13; 16)
|
||||||
IDENT@[13; 16) "foo"
|
IDENT@[13; 16) "foo"
|
||||||
SEMI@[16; 17)
|
SEMI@[16; 17)
|
||||||
WHITESPACE@[17; 18)
|
WHITESPACE@[17; 18)
|
||||||
|
|
|
@ -4,7 +4,7 @@ SOURCE_FILE@[0; 43)
|
||||||
WHITESPACE@[6; 7)
|
WHITESPACE@[6; 7)
|
||||||
CRATE_KW@[7; 12)
|
CRATE_KW@[7; 12)
|
||||||
WHITESPACE@[12; 13)
|
WHITESPACE@[12; 13)
|
||||||
NAME@[13; 16)
|
NAME_REF@[13; 16)
|
||||||
IDENT@[13; 16) "foo"
|
IDENT@[13; 16) "foo"
|
||||||
SEMI@[16; 17)
|
SEMI@[16; 17)
|
||||||
WHITESPACE@[17; 18)
|
WHITESPACE@[17; 18)
|
||||||
|
@ -13,7 +13,7 @@ SOURCE_FILE@[0; 43)
|
||||||
WHITESPACE@[24; 25)
|
WHITESPACE@[24; 25)
|
||||||
CRATE_KW@[25; 30)
|
CRATE_KW@[25; 30)
|
||||||
WHITESPACE@[30; 31)
|
WHITESPACE@[30; 31)
|
||||||
NAME@[31; 34)
|
NAME_REF@[31; 34)
|
||||||
IDENT@[31; 34) "foo"
|
IDENT@[31; 34) "foo"
|
||||||
WHITESPACE@[34; 35)
|
WHITESPACE@[34; 35)
|
||||||
ALIAS@[35; 41)
|
ALIAS@[35; 41)
|
||||||
|
|
Loading…
Reference in a new issue