mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 09:27:27 +00:00
Merge #8746
8746: Don't store call-site text offsets in hygiene info r=matklad a=jonas-schievink This threads a lot more database references around in order to avoid storing a bare `TextOffset` in the hygiene info. This `TextOffset` made hygiene info and `ItemTree`s more volatile than they should be, leading to excessive recomputation of `ItemTree`s. The incremental test added in https://github.com/rust-analyzer/rust-analyzer/pull/8721 is now passing with these changes. closes https://github.com/rust-analyzer/rust-analyzer/pull/8721 Co-authored-by: Jonas Schievink <jonasschievink@gmail.com> Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
6fccb152b4
19 changed files with 180 additions and 81 deletions
|
@ -27,3 +27,5 @@ debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger.
|
|||
# chalk-recursive = { path = "../chalk/chalk-recursive" }
|
||||
|
||||
# ungrammar = { path = "../ungrammar" }
|
||||
|
||||
# salsa = { path = "../salsa" }
|
||||
|
|
|
@ -112,7 +112,7 @@ fn resolve_doc_path(
|
|||
AttrDefId::MacroDefId(_) => return None,
|
||||
};
|
||||
let path = ast::Path::parse(link).ok()?;
|
||||
let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap();
|
||||
let modpath = ModPath::from_src(db.upcast(), path, &Hygiene::new_unhygienic()).unwrap();
|
||||
let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
|
||||
if resolved == PerNs::none() {
|
||||
if let Some(trait_id) = resolver.resolve_module_path_in_trait_items(db.upcast(), &modpath) {
|
||||
|
|
|
@ -1666,7 +1666,7 @@ impl Impl {
|
|||
.value
|
||||
.attrs()
|
||||
.filter_map(|it| {
|
||||
let path = ModPath::from_src(it.path()?, &hygenic)?;
|
||||
let path = ModPath::from_src(db.upcast(), it.path()?, &hygenic)?;
|
||||
if path.as_ident()?.to_string() == "derive" {
|
||||
Some(it)
|
||||
} else {
|
||||
|
|
|
@ -283,7 +283,7 @@ impl SourceAnalyzer {
|
|||
|
||||
// This must be a normal source file rather than macro file.
|
||||
let hygiene = Hygiene::new(db.upcast(), self.file_id);
|
||||
let ctx = body::LowerCtx::with_hygiene(&hygiene);
|
||||
let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene);
|
||||
let hir_path = Path::from_src(path.clone(), &ctx)?;
|
||||
|
||||
// Case where path is a qualifier of another path, e.g. foo::bar::Baz where we
|
||||
|
|
|
@ -95,13 +95,17 @@ impl ops::Deref for AttrsWithOwner {
|
|||
impl RawAttrs {
|
||||
pub(crate) const EMPTY: Self = Self { entries: None };
|
||||
|
||||
pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self {
|
||||
pub(crate) fn new(
|
||||
db: &dyn DefDatabase,
|
||||
owner: &dyn ast::AttrsOwner,
|
||||
hygiene: &Hygiene,
|
||||
) -> Self {
|
||||
let entries = collect_attrs(owner)
|
||||
.enumerate()
|
||||
.flat_map(|(i, attr)| {
|
||||
let index = AttrId(i as u32);
|
||||
match attr {
|
||||
Either::Left(attr) => Attr::from_src(attr, hygiene, index),
|
||||
Either::Left(attr) => Attr::from_src(db, attr, hygiene, index),
|
||||
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
|
||||
id: index,
|
||||
input: Some(AttrInput::Literal(SmolStr::new(doc))),
|
||||
|
@ -116,7 +120,7 @@ impl RawAttrs {
|
|||
|
||||
fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self {
|
||||
let hygiene = Hygiene::new(db.upcast(), owner.file_id);
|
||||
Self::new(owner.value, &hygiene)
|
||||
Self::new(db, owner.value, &hygiene)
|
||||
}
|
||||
|
||||
pub(crate) fn merge(&self, other: Self) -> Self {
|
||||
|
@ -170,7 +174,7 @@ impl RawAttrs {
|
|||
let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
|
||||
// FIXME hygiene
|
||||
let hygiene = Hygiene::new_unhygienic();
|
||||
Attr::from_src(attr, &hygiene, index)
|
||||
Attr::from_src(db, attr, &hygiene, index)
|
||||
});
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
|
@ -627,8 +631,13 @@ pub enum AttrInput {
|
|||
}
|
||||
|
||||
impl Attr {
|
||||
fn from_src(ast: ast::Attr, hygiene: &Hygiene, id: AttrId) -> Option<Attr> {
|
||||
let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?);
|
||||
fn from_src(
|
||||
db: &dyn DefDatabase,
|
||||
ast: ast::Attr,
|
||||
hygiene: &Hygiene,
|
||||
id: AttrId,
|
||||
) -> Option<Attr> {
|
||||
let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?);
|
||||
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
|
||||
let value = match lit.kind() {
|
||||
ast::LiteralKind::String(string) => string.value()?.into(),
|
||||
|
|
|
@ -72,7 +72,7 @@ impl CfgExpander {
|
|||
}
|
||||
|
||||
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> Attrs {
|
||||
RawAttrs::new(owner, &self.hygiene).filter(db, self.krate)
|
||||
RawAttrs::new(db, owner, &self.hygiene).filter(db, self.krate)
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> bool {
|
||||
|
@ -192,8 +192,8 @@ impl Expander {
|
|||
self.current_file_id
|
||||
}
|
||||
|
||||
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
|
||||
let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene);
|
||||
fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
|
||||
let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
|
||||
Path::from_src(path, &ctx)
|
||||
}
|
||||
|
||||
|
|
|
@ -40,23 +40,25 @@ use crate::{
|
|||
|
||||
use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
|
||||
|
||||
pub struct LowerCtx {
|
||||
pub struct LowerCtx<'a> {
|
||||
pub db: &'a dyn DefDatabase,
|
||||
hygiene: Hygiene,
|
||||
file_id: Option<HirFileId>,
|
||||
source_ast_id_map: Option<Arc<AstIdMap>>,
|
||||
}
|
||||
|
||||
impl LowerCtx {
|
||||
pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
|
||||
impl<'a> LowerCtx<'a> {
|
||||
pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
|
||||
LowerCtx {
|
||||
db,
|
||||
hygiene: Hygiene::new(db.upcast(), file_id),
|
||||
file_id: Some(file_id),
|
||||
source_ast_id_map: Some(db.ast_id_map(file_id)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hygiene(hygiene: &Hygiene) -> Self {
|
||||
LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None }
|
||||
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
|
||||
LowerCtx { db, hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None }
|
||||
}
|
||||
|
||||
pub(crate) fn hygiene(&self) -> &Hygiene {
|
||||
|
@ -145,7 +147,7 @@ impl ExprCollector<'_> {
|
|||
(self.body, self.source_map)
|
||||
}
|
||||
|
||||
fn ctx(&self) -> LowerCtx {
|
||||
fn ctx(&self) -> LowerCtx<'_> {
|
||||
LowerCtx::new(self.db, self.expander.current_file_id)
|
||||
}
|
||||
|
||||
|
@ -376,7 +378,7 @@ impl ExprCollector<'_> {
|
|||
ast::Expr::PathExpr(e) => {
|
||||
let path = e
|
||||
.path()
|
||||
.and_then(|path| self.expander.parse_path(path))
|
||||
.and_then(|path| self.expander.parse_path(self.db, path))
|
||||
.map(Expr::Path)
|
||||
.unwrap_or(Expr::Missing);
|
||||
self.alloc_expr(path, syntax_ptr)
|
||||
|
@ -408,7 +410,8 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::RecordExpr(e) => {
|
||||
let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
|
||||
let path =
|
||||
e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
let record_lit = if let Some(nfl) = e.record_expr_field_list() {
|
||||
let fields = nfl
|
||||
.fields()
|
||||
|
@ -791,7 +794,8 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
ast::Pat::TupleStructPat(p) => {
|
||||
let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
|
||||
let path =
|
||||
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
let (args, ellipsis) = self.collect_tuple_pat(p.fields());
|
||||
Pat::TupleStruct { path, args, ellipsis }
|
||||
}
|
||||
|
@ -801,7 +805,8 @@ impl ExprCollector<'_> {
|
|||
Pat::Ref { pat, mutability }
|
||||
}
|
||||
ast::Pat::PathPat(p) => {
|
||||
let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
|
||||
let path =
|
||||
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
path.map(Pat::Path).unwrap_or(Pat::Missing)
|
||||
}
|
||||
ast::Pat::OrPat(p) => {
|
||||
|
@ -815,7 +820,8 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
ast::Pat::WildcardPat(_) => Pat::Wild,
|
||||
ast::Pat::RecordPat(p) => {
|
||||
let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
|
||||
let path =
|
||||
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
let args: Vec<_> = p
|
||||
.record_pat_field_list()
|
||||
.expect("every struct should have a field list")
|
||||
|
|
|
@ -386,7 +386,7 @@ mod tests {
|
|||
let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
|
||||
let ast_path =
|
||||
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
|
||||
let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
|
||||
let mod_path = ModPath::from_src(&db, ast_path, &Hygiene::new_unhygienic()).unwrap();
|
||||
|
||||
let def_map = module.def_map(&db);
|
||||
let resolved = def_map
|
||||
|
|
|
@ -88,7 +88,7 @@ impl ItemTree {
|
|||
let mut item_tree = match_ast! {
|
||||
match syntax {
|
||||
ast::SourceFile(file) => {
|
||||
top_attrs = Some(RawAttrs::new(&file, &hygiene));
|
||||
top_attrs = Some(RawAttrs::new(db, &file, &hygiene));
|
||||
ctx.lower_module_items(&file)
|
||||
},
|
||||
ast::MacroItems(items) => {
|
||||
|
|
|
@ -31,18 +31,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) struct Ctx {
|
||||
pub(super) struct Ctx<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
tree: ItemTree,
|
||||
hygiene: Hygiene,
|
||||
file: HirFileId,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
body_ctx: crate::body::LowerCtx,
|
||||
body_ctx: crate::body::LowerCtx<'a>,
|
||||
forced_visibility: Option<RawVisibilityId>,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
|
||||
impl<'a> Ctx<'a> {
|
||||
pub(super) fn new(db: &'a dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
|
||||
Self {
|
||||
db,
|
||||
tree: ItemTree::default(),
|
||||
hygiene,
|
||||
file,
|
||||
|
@ -126,7 +128,7 @@ impl Ctx {
|
|||
| ast::Item::MacroDef(_) => {}
|
||||
};
|
||||
|
||||
let attrs = RawAttrs::new(item, &self.hygiene);
|
||||
let attrs = RawAttrs::new(self.db, item, &self.hygiene);
|
||||
let items = match item {
|
||||
ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into),
|
||||
ast::Item::Union(ast) => self.lower_union(ast).map(Into::into),
|
||||
|
@ -256,7 +258,7 @@ impl Ctx {
|
|||
for field in fields.fields() {
|
||||
if let Some(data) = self.lower_record_field(&field) {
|
||||
let idx = self.data().fields.alloc(data);
|
||||
self.add_attrs(idx.into(), RawAttrs::new(&field, &self.hygiene));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene));
|
||||
}
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
|
@ -276,7 +278,7 @@ impl Ctx {
|
|||
for (i, field) in fields.fields().enumerate() {
|
||||
let data = self.lower_tuple_field(i, &field);
|
||||
let idx = self.data().fields.alloc(data);
|
||||
self.add_attrs(idx.into(), RawAttrs::new(&field, &self.hygiene));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene));
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
IdRange::new(start..end)
|
||||
|
@ -321,7 +323,7 @@ impl Ctx {
|
|||
for variant in variants.variants() {
|
||||
if let Some(data) = self.lower_variant(&variant) {
|
||||
let idx = self.data().variants.alloc(data);
|
||||
self.add_attrs(idx.into(), RawAttrs::new(&variant, &self.hygiene));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &variant, &self.hygiene));
|
||||
}
|
||||
}
|
||||
let end = self.next_variant_idx();
|
||||
|
@ -364,7 +366,7 @@ impl Ctx {
|
|||
};
|
||||
let ty = Interned::new(self_type);
|
||||
let idx = self.data().params.alloc(Param::Normal(ty));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(&self_param, &self.hygiene));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &self_param, &self.hygiene));
|
||||
has_self_param = true;
|
||||
}
|
||||
for param in param_list.params() {
|
||||
|
@ -376,7 +378,7 @@ impl Ctx {
|
|||
self.data().params.alloc(Param::Normal(ty))
|
||||
}
|
||||
};
|
||||
self.add_attrs(idx.into(), RawAttrs::new(¶m, &self.hygiene));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, ¶m, &self.hygiene));
|
||||
}
|
||||
}
|
||||
let end_param = self.next_param_idx();
|
||||
|
@ -522,10 +524,11 @@ impl Ctx {
|
|||
let is_unsafe = trait_def.unsafe_token().is_some();
|
||||
let bounds = self.lower_type_bounds(trait_def);
|
||||
let items = trait_def.assoc_item_list().map(|list| {
|
||||
let db = self.db;
|
||||
self.with_inherited_visibility(visibility, |this| {
|
||||
list.assoc_items()
|
||||
.filter_map(|item| {
|
||||
let attrs = RawAttrs::new(&item, &this.hygiene);
|
||||
let attrs = RawAttrs::new(db, &item, &this.hygiene);
|
||||
this.collect_inner_items(item.syntax());
|
||||
this.lower_assoc_item(&item).map(|item| {
|
||||
this.add_attrs(ModItem::from(item).into(), attrs);
|
||||
|
@ -567,7 +570,7 @@ impl Ctx {
|
|||
.filter_map(|item| {
|
||||
self.collect_inner_items(item.syntax());
|
||||
let assoc = self.lower_assoc_item(&item)?;
|
||||
let attrs = RawAttrs::new(&item, &self.hygiene);
|
||||
let attrs = RawAttrs::new(self.db, &item, &self.hygiene);
|
||||
self.add_attrs(ModItem::from(assoc).into(), attrs);
|
||||
Some(assoc)
|
||||
})
|
||||
|
@ -585,6 +588,7 @@ impl Ctx {
|
|||
let mut imports = Vec::new();
|
||||
let tree = self.tree.data_mut();
|
||||
ModPath::expand_use_item(
|
||||
self.db,
|
||||
InFile::new(self.file, use_item.clone()),
|
||||
&self.hygiene,
|
||||
|path, _use_tree, is_glob, alias| {
|
||||
|
@ -618,7 +622,7 @@ impl Ctx {
|
|||
}
|
||||
|
||||
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
|
||||
let path = Interned::new(ModPath::from_src(m.path()?, &self.hygiene)?);
|
||||
let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?);
|
||||
let ast_id = self.source_ast_id_map.ast_id(m);
|
||||
let res = MacroCall { path, ast_id };
|
||||
Some(id(self.data().macro_calls.alloc(res)))
|
||||
|
@ -647,7 +651,7 @@ impl Ctx {
|
|||
list.extern_items()
|
||||
.filter_map(|item| {
|
||||
self.collect_inner_items(item.syntax());
|
||||
let attrs = RawAttrs::new(&item, &self.hygiene);
|
||||
let attrs = RawAttrs::new(self.db, &item, &self.hygiene);
|
||||
let id: ModItem = match item {
|
||||
ast::ExternItem::Fn(ast) => {
|
||||
let func_id = self.lower_function(&ast)?;
|
||||
|
@ -755,7 +759,7 @@ impl Ctx {
|
|||
fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
|
||||
let vis = match self.forced_visibility {
|
||||
Some(vis) => return vis,
|
||||
None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene),
|
||||
None => RawVisibility::from_ast_with_hygiene(self.db, item.visibility(), &self.hygiene),
|
||||
};
|
||||
|
||||
self.data().vis.alloc(vis)
|
||||
|
|
|
@ -654,7 +654,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
|||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
||||
let h = Hygiene::new(db.upcast(), self.file_id);
|
||||
let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h));
|
||||
let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
|
||||
|
||||
let path = match error_sink
|
||||
.option(path, || mbe::ExpandError::Other("malformed macro invocation".into()))
|
||||
|
@ -712,7 +712,7 @@ fn macro_call_as_call_id(
|
|||
krate,
|
||||
macro_call,
|
||||
def,
|
||||
&|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?),
|
||||
&|path: ast::Path| resolver(path::ModPath::from_src(db, path, &hygiene)?),
|
||||
error_sink,
|
||||
)
|
||||
.map(MacroCallId::from)
|
||||
|
|
|
@ -599,6 +599,7 @@ mod diagnostics {
|
|||
let mut cur = 0;
|
||||
let mut tree = None;
|
||||
ModPath::expand_use_item(
|
||||
db,
|
||||
InFile::new(ast.file_id, use_item),
|
||||
&hygiene,
|
||||
|_mod_path, use_tree, _is_glob, _alias| {
|
||||
|
|
|
@ -105,3 +105,55 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
|||
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typing_inside_a_function_should_not_invalidate_expansions() {
|
||||
let (mut db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /lib.rs
|
||||
macro_rules! m {
|
||||
($ident:ident) => {
|
||||
fn $ident() { };
|
||||
}
|
||||
}
|
||||
mod foo;
|
||||
|
||||
//- /foo/mod.rs
|
||||
pub mod bar;
|
||||
|
||||
//- /foo/bar.rs
|
||||
m!(X);
|
||||
fn quux() { 1$0 }
|
||||
m!(Y);
|
||||
m!(Z);
|
||||
"#,
|
||||
);
|
||||
let krate = db.test_crate();
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||
assert_eq!(module_data.scope.resolutions().count(), 4);
|
||||
});
|
||||
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
|
||||
assert_eq!(n_recalculated_item_trees, 6);
|
||||
}
|
||||
|
||||
let new_text = r#"
|
||||
m!(X);
|
||||
fn quux() { 92 }
|
||||
m!(Y);
|
||||
m!(Z);
|
||||
"#;
|
||||
db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||
assert_eq!(module_data.scope.resolutions().count(), 4);
|
||||
});
|
||||
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
|
||||
assert_eq!(n_recalculated_item_trees, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{body::LowerCtx, intern::Interned, type_ref::LifetimeRef};
|
||||
use crate::{body::LowerCtx, db::DefDatabase, intern::Interned, type_ref::LifetimeRef};
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
|
@ -47,8 +47,8 @@ pub enum ImportAlias {
|
|||
}
|
||||
|
||||
impl ModPath {
|
||||
pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
||||
let ctx = LowerCtx::with_hygiene(hygiene);
|
||||
pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
||||
let ctx = LowerCtx::with_hygiene(db, hygiene);
|
||||
lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
|
||||
}
|
||||
|
||||
|
@ -64,12 +64,13 @@ impl ModPath {
|
|||
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub(crate) fn expand_use_item(
|
||||
db: &dyn DefDatabase,
|
||||
item_src: InFile<ast::Use>,
|
||||
hygiene: &Hygiene,
|
||||
mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
|
||||
) {
|
||||
if let Some(tree) = item_src.value.use_tree() {
|
||||
lower::lower_use_tree(None, tree, hygiene, &mut cb);
|
||||
lower::lower_use_tree(db, None, tree, hygiene, &mut cb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
|
|||
match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
// FIXME: this should just return name
|
||||
match hygiene.name_ref_to_name(name_ref) {
|
||||
match hygiene.name_ref_to_name(ctx.db.upcast(), name_ref) {
|
||||
Either::Left(name) => {
|
||||
let args = segment
|
||||
.generic_arg_list()
|
||||
|
@ -133,7 +133,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
|
|||
// We follow what it did anyway :)
|
||||
if segments.len() == 1 && kind == PathKind::Plain {
|
||||
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||
if let Some(crate_id) = hygiene.local_inner_macros(path) {
|
||||
if let Some(crate_id) = hygiene.local_inner_macros(ctx.db.upcast(), path) {
|
||||
kind = PathKind::DollarCrate(crate_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,13 @@ use either::Either;
|
|||
use hir_expand::{hygiene::Hygiene, name::AsName};
|
||||
use syntax::ast::{self, NameOwner};
|
||||
|
||||
use crate::path::{ImportAlias, ModPath, PathKind};
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
path::{ImportAlias, ModPath, PathKind},
|
||||
};
|
||||
|
||||
pub(crate) fn lower_use_tree(
|
||||
db: &dyn DefDatabase,
|
||||
prefix: Option<ModPath>,
|
||||
tree: ast::UseTree,
|
||||
hygiene: &Hygiene,
|
||||
|
@ -21,13 +25,13 @@ pub(crate) fn lower_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, hygiene) {
|
||||
Some(path) => match convert_path(db, prefix, path, hygiene) {
|
||||
Some(it) => Some(it),
|
||||
None => return, // FIXME: report errors somewhere
|
||||
},
|
||||
};
|
||||
for child_tree in use_tree_list.use_trees() {
|
||||
lower_use_tree(prefix.clone(), child_tree, hygiene, cb);
|
||||
lower_use_tree(db, prefix.clone(), child_tree, hygiene, cb);
|
||||
}
|
||||
} else {
|
||||
let alias = tree.rename().map(|a| {
|
||||
|
@ -47,7 +51,7 @@ pub(crate) fn lower_use_tree(
|
|||
}
|
||||
}
|
||||
}
|
||||
if let Some(path) = convert_path(prefix, ast_path, hygiene) {
|
||||
if let Some(path) = convert_path(db, prefix, ast_path, hygiene) {
|
||||
cb(path, &tree, is_glob, alias)
|
||||
}
|
||||
// FIXME: report errors somewhere
|
||||
|
@ -61,9 +65,14 @@ pub(crate) fn lower_use_tree(
|
|||
}
|
||||
}
|
||||
|
||||
fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
||||
fn convert_path(
|
||||
db: &dyn DefDatabase,
|
||||
prefix: Option<ModPath>,
|
||||
path: ast::Path,
|
||||
hygiene: &Hygiene,
|
||||
) -> Option<ModPath> {
|
||||
let prefix = if let Some(qual) = path.qualifier() {
|
||||
Some(convert_path(prefix, qual, hygiene)?)
|
||||
Some(convert_path(db, prefix, qual, hygiene)?)
|
||||
} else {
|
||||
prefix
|
||||
};
|
||||
|
@ -71,7 +80,7 @@ fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) ->
|
|||
let segment = path.segment()?;
|
||||
let res = match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
match hygiene.name_ref_to_name(name_ref) {
|
||||
match hygiene.name_ref_to_name(db.upcast(), name_ref) {
|
||||
Either::Left(name) => {
|
||||
// no type args in use
|
||||
let mut res = prefix.unwrap_or_else(|| {
|
||||
|
|
|
@ -33,17 +33,19 @@ impl RawVisibility {
|
|||
db: &dyn DefDatabase,
|
||||
node: InFile<Option<ast::Visibility>>,
|
||||
) -> RawVisibility {
|
||||
Self::from_ast_with_hygiene(node.value, &Hygiene::new(db.upcast(), node.file_id))
|
||||
Self::from_ast_with_hygiene(db, node.value, &Hygiene::new(db.upcast(), node.file_id))
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast_with_hygiene(
|
||||
db: &dyn DefDatabase,
|
||||
node: Option<ast::Visibility>,
|
||||
hygiene: &Hygiene,
|
||||
) -> RawVisibility {
|
||||
Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene)
|
||||
Self::from_ast_with_hygiene_and_default(db, node, RawVisibility::private(), hygiene)
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast_with_hygiene_and_default(
|
||||
db: &dyn DefDatabase,
|
||||
node: Option<ast::Visibility>,
|
||||
default: RawVisibility,
|
||||
hygiene: &Hygiene,
|
||||
|
@ -54,7 +56,7 @@ impl RawVisibility {
|
|||
};
|
||||
match node.kind() {
|
||||
ast::VisibilityKind::In(path) => {
|
||||
let path = ModPath::from_src(path, hygiene);
|
||||
let path = ModPath::from_src(db, path, hygiene);
|
||||
let path = match path {
|
||||
None => return RawVisibility::private(),
|
||||
Some(path) => path,
|
||||
|
|
|
@ -32,10 +32,14 @@ impl Hygiene {
|
|||
}
|
||||
|
||||
// FIXME: this should just return name
|
||||
pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
|
||||
pub fn name_ref_to_name(
|
||||
&self,
|
||||
db: &dyn AstDatabase,
|
||||
name_ref: ast::NameRef,
|
||||
) -> Either<Name, CrateId> {
|
||||
if let Some(frames) = &self.frames {
|
||||
if name_ref.text() == "$crate" {
|
||||
if let Some(krate) = frames.root_crate(name_ref.syntax()) {
|
||||
if let Some(krate) = frames.root_crate(db, name_ref.syntax()) {
|
||||
return Either::Right(krate);
|
||||
}
|
||||
}
|
||||
|
@ -44,15 +48,19 @@ impl Hygiene {
|
|||
Either::Left(name_ref.as_name())
|
||||
}
|
||||
|
||||
pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
|
||||
pub fn local_inner_macros(&self, db: &dyn AstDatabase, path: ast::Path) -> Option<CrateId> {
|
||||
let mut token = path.syntax().first_token()?.text_range();
|
||||
let frames = self.frames.as_ref()?;
|
||||
let mut current = frames.0.clone();
|
||||
|
||||
loop {
|
||||
let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(token)?;
|
||||
let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(db, token)?;
|
||||
if origin == Origin::Def {
|
||||
return if current.local_inner { frames.root_crate(path.syntax()) } else { None };
|
||||
return if current.local_inner {
|
||||
frames.root_crate(db, path.syntax())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
current = current.call_site.as_ref()?.clone();
|
||||
token = mapped.value;
|
||||
|
@ -82,13 +90,13 @@ impl HygieneFrames {
|
|||
HygieneFrames(Arc::new(HygieneFrame::new(db, file_id)))
|
||||
}
|
||||
|
||||
fn root_crate(&self, node: &SyntaxNode) -> Option<CrateId> {
|
||||
fn root_crate(&self, db: &dyn AstDatabase, node: &SyntaxNode) -> Option<CrateId> {
|
||||
let mut token = node.first_token()?.text_range();
|
||||
let mut result = self.0.krate;
|
||||
let mut current = self.0.clone();
|
||||
|
||||
while let Some((mapped, origin)) =
|
||||
current.expansion.as_ref().and_then(|it| it.map_ident_up(token))
|
||||
current.expansion.as_ref().and_then(|it| it.map_ident_up(db, token))
|
||||
{
|
||||
result = current.krate;
|
||||
|
||||
|
@ -112,7 +120,7 @@ impl HygieneFrames {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct HygieneInfo {
|
||||
arg_start: InFile<TextSize>,
|
||||
file: MacroFile,
|
||||
/// The `macro_rules!` arguments.
|
||||
def_start: Option<InFile<TextSize>>,
|
||||
|
||||
|
@ -122,12 +130,24 @@ struct HygieneInfo {
|
|||
}
|
||||
|
||||
impl HygieneInfo {
|
||||
fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> {
|
||||
fn map_ident_up(
|
||||
&self,
|
||||
db: &dyn AstDatabase,
|
||||
token: TextRange,
|
||||
) -> Option<(InFile<TextRange>, Origin)> {
|
||||
let token_id = self.exp_map.token_by_range(token)?;
|
||||
|
||||
let (token_id, origin) = self.macro_def.map_id_up(token_id);
|
||||
let (token_map, tt) = match origin {
|
||||
mbe::Origin::Call => (&self.macro_arg.1, self.arg_start),
|
||||
mbe::Origin::Call => {
|
||||
let call_id = match self.file.macro_call_id {
|
||||
MacroCallId::LazyMacro(lazy) => lazy,
|
||||
MacroCallId::EagerMacro(_) => unreachable!(),
|
||||
};
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro(call_id);
|
||||
let arg_start = loc.kind.arg(db)?.text_range().start();
|
||||
(&self.macro_arg.1, InFile::new(loc.kind.file_id(), arg_start))
|
||||
}
|
||||
mbe::Origin::Def => match (&*self.macro_def, self.def_start) {
|
||||
(TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt))
|
||||
| (TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) => {
|
||||
|
@ -147,8 +167,6 @@ fn make_hygiene_info(
|
|||
macro_file: MacroFile,
|
||||
loc: &MacroCallLoc,
|
||||
) -> Option<HygieneInfo> {
|
||||
let arg_tt = loc.kind.arg(db)?;
|
||||
|
||||
let def_offset = loc.def.ast_id().left().and_then(|id| {
|
||||
let def_tt = match id.to_node(db) {
|
||||
ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(),
|
||||
|
@ -161,13 +179,7 @@ fn make_hygiene_info(
|
|||
let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
|
||||
|
||||
Some(HygieneInfo {
|
||||
arg_start: InFile::new(loc.kind.file_id(), arg_tt.text_range().start()),
|
||||
def_start: def_offset,
|
||||
macro_arg,
|
||||
macro_def,
|
||||
exp_map,
|
||||
})
|
||||
Some(HygieneInfo { file: macro_file, def_start: def_offset, macro_arg, macro_def, exp_map })
|
||||
}
|
||||
|
||||
impl HygieneFrame {
|
||||
|
@ -178,7 +190,8 @@ impl HygieneFrame {
|
|||
MacroCallId::EagerMacro(_id) => (None, None, false),
|
||||
MacroCallId::LazyMacro(id) => {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let info = make_hygiene_info(db, macro_file, &loc);
|
||||
let info = make_hygiene_info(db, macro_file, &loc)
|
||||
.map(|info| (loc.kind.file_id(), info));
|
||||
match loc.def.kind {
|
||||
MacroDefKind::Declarative(_) => {
|
||||
(info, Some(loc.def.krate), loc.def.local_inner)
|
||||
|
@ -192,7 +205,7 @@ impl HygieneFrame {
|
|||
},
|
||||
};
|
||||
|
||||
let info = match info {
|
||||
let (calling_file, info) = match info {
|
||||
None => {
|
||||
return HygieneFrame {
|
||||
expansion: None,
|
||||
|
@ -206,7 +219,7 @@ impl HygieneFrame {
|
|||
};
|
||||
|
||||
let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id));
|
||||
let call_site = Some(db.hygiene_frame(info.arg_start.file_id));
|
||||
let call_site = Some(db.hygiene_frame(calling_file));
|
||||
|
||||
HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site }
|
||||
}
|
||||
|
|
|
@ -1000,7 +1000,7 @@ impl HirDisplay for TypeRef {
|
|||
}
|
||||
TypeRef::Macro(macro_call) => {
|
||||
let macro_call = macro_call.to_node(f.db.upcast());
|
||||
let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic());
|
||||
let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic());
|
||||
match macro_call.path() {
|
||||
Some(path) => match Path::from_src(path, &ctx) {
|
||||
Some(path) => path.hir_fmt(f)?,
|
||||
|
|
Loading…
Reference in a new issue