refactor $crate handling

Introduce proper hygiene module, which should grow quite a bit
eventually.
This commit is contained in:
Aleksey Kladov 2019-10-30 18:41:50 +03:00
parent e34e71c62d
commit 0bc7d28518
7 changed files with 118 additions and 89 deletions

View File

@ -1,6 +1,7 @@
//! FIXME: write short doc here
use hir_def::{
hygiene::Hygiene,
name::{self, AsName, Name},
path::GenericArgs,
type_ref::TypeRef,
@ -597,7 +598,8 @@ where
}
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
Path::from_src(Source { ast: path, file_id: self.current_file_id }, self.db)
let hygiene = Hygiene::new(self.db, self.current_file_id);
Path::from_src(path, &hygiene)
}
}

View File

@ -3,7 +3,7 @@
use rustc_hash::FxHashMap;
use std::sync::Arc;
use hir_def::{attr::Attr, type_ref::TypeRef};
use hir_def::{attr::Attr, hygiene::Hygiene, type_ref::TypeRef};
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
use ra_cfg::CfgOptions;
use ra_syntax::{
@ -227,10 +227,11 @@ impl ModuleImplBlocks {
owner: &dyn ast::ModuleItemOwner,
file_id: HirFileId,
) {
let hygiene = Hygiene::new(db, file_id);
for item in owner.items_with_macros() {
match item {
ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => {
let attrs = Attr::from_attrs_owner(file_id, &impl_block_ast, db);
let attrs = Attr::from_attrs_owner(&impl_block_ast, &hygiene);
if attrs.map_or(false, |attrs| {
attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false))
}) {
@ -247,7 +248,7 @@ impl ModuleImplBlocks {
}
ast::ItemOrMacro::Item(_) => (),
ast::ItemOrMacro::Macro(macro_call) => {
let attrs = Attr::from_attrs_owner(file_id, &macro_call, db);
let attrs = Attr::from_attrs_owner(&macro_call, &hygiene);
if attrs.map_or(false, |attrs| {
attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false))
}) {
@ -256,9 +257,8 @@ impl ModuleImplBlocks {
//FIXME: we should really cut down on the boilerplate required to process a macro
let ast_id = AstId::new(file_id, db.ast_id_map(file_id).ast_id(&macro_call));
if let Some(path) = macro_call
.path()
.and_then(|path| Path::from_src(Source { ast: path, file_id }, db))
if let Some(path) =
macro_call.path().and_then(|path| Path::from_src(path, &hygiene))
{
if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path)
{

View File

@ -2,7 +2,6 @@
use std::sync::Arc;
use hir_expand::db::AstDatabase;
use mbe::ast_to_token_tree;
use ra_cfg::CfgOptions;
use ra_syntax::{
@ -11,7 +10,7 @@ use ra_syntax::{
};
use tt::Subtree;
use crate::{path::Path, HirFileId, Source};
use crate::{hygiene::Hygiene, path::Path};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attr {
@ -26,11 +25,8 @@ pub enum AttrInput {
}
impl Attr {
pub(crate) fn from_src(
Source { file_id, ast }: Source<ast::Attr>,
db: &impl AstDatabase,
) -> Option<Attr> {
let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?;
pub(crate) fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
let path = Path::from_src(ast.path()?, hygiene)?;
let input = match ast.input() {
None => None,
Some(ast::AttrInput::Literal(lit)) => {
@ -46,17 +42,13 @@ impl Attr {
Some(Attr { path, input })
}
pub fn from_attrs_owner(
file_id: HirFileId,
owner: &dyn AttrsOwner,
db: &impl AstDatabase,
) -> Option<Arc<[Attr]>> {
pub fn from_attrs_owner(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Option<Arc<[Attr]>> {
let mut attrs = owner.attrs().peekable();
if attrs.peek().is_none() {
// Avoid heap allocation
return None;
}
Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect())
Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
}
pub fn is_simple_atom(&self, name: &str) -> bool {

View File

@ -0,0 +1,40 @@
//! This modules handles hygiene information.
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
// Should this be moved to `hir_expand`? Seems like it.
use hir_expand::{db::AstDatabase, HirFileId};
use ra_db::CrateId;
use ra_syntax::ast;
use crate::{
either::Either,
name::{AsName, Name},
};
#[derive(Debug)]
pub struct Hygiene {
// This is what `$crate` expands to
def_crate: Option<CrateId>,
}
impl Hygiene {
pub fn new(db: &impl AstDatabase, file_id: HirFileId) -> Hygiene {
Hygiene { def_crate: file_id.macro_crate(db) }
}
pub(crate) fn new_unhygienic() -> Hygiene {
Hygiene { def_crate: None }
}
// FIXME: this should just return name
pub(crate) fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
if let Some(def_crate) = self.def_crate {
if name_ref.text() == "$crate" {
return Either::B(def_crate);
}
}
Either::A(name_ref.as_name())
}
}

View File

@ -13,6 +13,7 @@ pub mod attr;
pub mod name;
pub mod path;
pub mod type_ref;
pub mod hygiene;
// FIXME: this should be private
pub mod nameres;

View File

@ -13,6 +13,7 @@ use crate::{
attr::Attr,
db::DefDatabase2,
either::Either,
hygiene::Hygiene,
name::{AsName, Name},
path::Path,
FileAstId, HirFileId, ModuleSource, Source,
@ -78,7 +79,7 @@ impl RawItems {
source_ast_id_map: db.ast_id_map(file_id),
source_map: ImportSourceMap::default(),
file_id,
db,
hygiene: Hygiene::new(db, file_id),
};
if let Some(node) = db.parse_or_expand(file_id) {
if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
@ -204,15 +205,15 @@ pub struct MacroData {
pub export: bool,
}
struct RawItemsCollector<DB> {
struct RawItemsCollector {
raw_items: RawItems,
source_ast_id_map: Arc<AstIdMap>,
source_map: ImportSourceMap,
file_id: HirFileId,
db: DB,
hygiene: Hygiene,
}
impl<DB: AstDatabase> RawItemsCollector<&DB> {
impl RawItemsCollector {
fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
for item_or_macro in body.items_with_macros() {
match item_or_macro {
@ -309,9 +310,10 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
let is_prelude = use_item.has_atom_attr("prelude_import");
let attrs = self.parse_attrs(&use_item);
let mut buf = Vec::new();
Path::expand_use_item(
Source { ast: use_item, file_id: self.file_id },
self.db,
&self.hygiene,
|path, use_tree, is_glob, alias| {
let import_data = ImportData {
path,
@ -321,14 +323,12 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
is_extern_crate: false,
is_macro_use: false,
};
self.push_import(
current_module,
attrs.clone(),
import_data,
Either::A(AstPtr::new(use_tree)),
);
buf.push((import_data, Either::A(AstPtr::new(use_tree))));
},
)
);
for (import_data, ptr) in buf {
self.push_import(current_module, attrs.clone(), import_data, ptr);
}
}
fn add_extern_crate_item(
@ -361,10 +361,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
let attrs = self.parse_attrs(&m);
let path = match m
.path()
.and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db))
{
let path = match m.path().and_then(|path| Path::from_src(path, &self.hygiene)) {
Some(it) => it,
_ => return,
};
@ -402,6 +399,6 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
}
fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
Attr::from_attrs_owner(self.file_id, item, self.db)
Attr::from_attrs_owner(item, &self.hygiene)
}
}

View File

@ -2,7 +2,6 @@
use std::{iter, sync::Arc};
use hir_expand::db::AstDatabase;
use ra_db::CrateId;
use ra_syntax::{
ast::{self, NameOwner, TypeAscriptionOwner},
@ -10,6 +9,8 @@ use ra_syntax::{
};
use crate::{
either::Either,
hygiene::Hygiene,
name::{self, AsName, Name},
type_ref::TypeRef,
Source,
@ -68,11 +69,11 @@ impl Path {
/// Calls `cb` with all paths, represented by this use item.
pub fn expand_use_item(
item_src: Source<ast::UseItem>,
db: &impl AstDatabase,
hygiene: &Hygiene,
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
) {
if let Some(tree) = item_src.ast.use_tree() {
expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb);
expand_use_tree(None, tree, hygiene, &mut cb);
}
}
@ -89,17 +90,12 @@ impl Path {
/// Converts an `ast::Path` to `Path`. Works with use trees.
/// DEPRECATED: It does not handle `$crate` from macro call.
pub fn from_ast(path: ast::Path) -> Option<Path> {
Path::parse(path, &|| None)
Path::from_src(path, &Hygiene::new_unhygienic())
}
/// Converts an `ast::Path` to `Path`. Works with use trees.
/// It correctly handles `$crate` based path from macro call.
pub fn from_src(source: Source<ast::Path>, db: &impl AstDatabase) -> Option<Path> {
let file_id = source.file_id;
Path::parse(source.ast, &|| file_id.macro_crate(db))
}
fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<CrateId>) -> Option<Path> {
pub fn from_src(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut segments = Vec::new();
loop {
@ -110,26 +106,28 @@ impl Path {
}
match segment.kind()? {
ast::PathSegmentKind::Name(name) => {
if name.text() == "$crate" {
if let Some(macro_crate) = macro_crate() {
kind = PathKind::DollarCrate(macro_crate);
ast::PathSegmentKind::Name(name_ref) => {
// FIXME: this should just return name
match hygiene.name_ref_to_name(name_ref) {
Either::A(name) => {
let args = segment
.type_arg_list()
.and_then(GenericArgs::from_ast)
.or_else(|| {
GenericArgs::from_fn_like_path_ast(
segment.param_list(),
segment.ret_type(),
)
})
.map(Arc::new);
let segment = PathSegment { name, args_and_bindings: args };
segments.push(segment);
}
Either::B(crate_id) => {
kind = PathKind::DollarCrate(crate_id);
break;
}
}
let args = segment
.type_arg_list()
.and_then(GenericArgs::from_ast)
.or_else(|| {
GenericArgs::from_fn_like_path_ast(
segment.param_list(),
segment.ret_type(),
)
})
.map(Arc::new);
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
segments.push(segment);
}
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
assert!(path.qualifier().is_none()); // this can only occur at the first segment
@ -143,7 +141,7 @@ impl Path {
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => {
let path = Path::parse(trait_ref.path()?, macro_crate)?;
let path = Path::from_src(trait_ref.path()?, hygiene)?;
kind = path.kind;
let mut prefix_segments = path.segments;
prefix_segments.reverse();
@ -294,7 +292,7 @@ impl From<Name> for Path {
fn expand_use_tree(
prefix: Option<Path>,
tree: ast::UseTree,
macro_crate: &impl Fn() -> Option<CrateId>,
hygiene: &Hygiene,
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
) {
if let Some(use_tree_list) = tree.use_tree_list() {
@ -303,13 +301,13 @@ fn expand_use_tree(
None => prefix,
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
Some(path) => match convert_path(prefix, path, macro_crate) {
Some(path) => match convert_path(prefix, path, hygiene) {
Some(it) => Some(it),
None => return, // FIXME: report errors somewhere
},
};
for child_tree in use_tree_list.use_trees() {
expand_use_tree(prefix.clone(), child_tree, macro_crate, cb);
expand_use_tree(prefix.clone(), child_tree, hygiene, cb);
}
} else {
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
@ -326,7 +324,7 @@ fn expand_use_tree(
}
}
}
if let Some(path) = convert_path(prefix, ast_path, macro_crate) {
if let Some(path) = convert_path(prefix, ast_path, hygiene) {
let is_glob = tree.has_star();
cb(path, &tree, is_glob, alias)
}
@ -336,37 +334,36 @@ fn expand_use_tree(
}
}
fn convert_path(
prefix: Option<Path>,
path: ast::Path,
macro_crate: &impl Fn() -> Option<CrateId>,
) -> Option<Path> {
fn convert_path(prefix: Option<Path>, path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
let prefix = if let Some(qual) = path.qualifier() {
Some(convert_path(prefix, qual, macro_crate)?)
Some(convert_path(prefix, qual, hygiene)?)
} else {
prefix
};
let segment = path.segment()?;
let res = match segment.kind()? {
ast::PathSegmentKind::Name(name) => {
if name.text() == "$crate" {
if let Some(krate) = macro_crate() {
ast::PathSegmentKind::Name(name_ref) => {
match hygiene.name_ref_to_name(name_ref) {
Either::A(name) => {
// no type args in use
let mut res = prefix.unwrap_or_else(|| Path {
kind: PathKind::Plain,
segments: Vec::with_capacity(1),
});
res.segments.push(PathSegment {
name,
args_and_bindings: None, // no type args in use
});
res
}
Either::B(crate_id) => {
return Some(Path::from_simple_segments(
PathKind::DollarCrate(krate),
PathKind::DollarCrate(crate_id),
iter::empty(),
));
))
}
}
// no type args in use
let mut res = prefix
.unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
res.segments.push(PathSegment {
name: name.as_name(),
args_and_bindings: None, // no type args in use
});
res
}
ast::PathSegmentKind::CrateKw => {
if prefix.is_some() {