mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-24 04:46:58 +00:00
Merge #1147
1147: Handle macros in type checking / HIR r=matklad a=Lapz An other attempt at #1102. I will need to flatten the nested if statements and im also not sure if the way that i get the resolver and module will always work Co-authored-by: Lenard Pratt <l3np27@gmail.com>
This commit is contained in:
commit
a094d5c621
@ -5,13 +5,14 @@ use rustc_hash::FxHashMap;
|
|||||||
|
|
||||||
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
|
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
SyntaxNodePtr, AstPtr, AstNode,
|
SyntaxNodePtr, AstPtr, AstNode,TreeArc,
|
||||||
ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind,ArrayExprKind, TypeAscriptionOwner}
|
ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind,ArrayExprKind, TypeAscriptionOwner}
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Path, Name, HirDatabase, Resolver,DefWithBody, Either,
|
Path, Name, HirDatabase, Resolver,DefWithBody, Either,
|
||||||
name::AsName,
|
name::AsName,
|
||||||
|
ids::{MacroCallId},
|
||||||
type_ref::{Mutability, TypeRef},
|
type_ref::{Mutability, TypeRef},
|
||||||
};
|
};
|
||||||
use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}};
|
use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}};
|
||||||
@ -478,38 +479,54 @@ impl Pat {
|
|||||||
|
|
||||||
// Queries
|
// Queries
|
||||||
|
|
||||||
pub(crate) struct ExprCollector {
|
pub(crate) struct ExprCollector<DB> {
|
||||||
|
db: DB,
|
||||||
owner: DefWithBody,
|
owner: DefWithBody,
|
||||||
exprs: Arena<ExprId, Expr>,
|
exprs: Arena<ExprId, Expr>,
|
||||||
pats: Arena<PatId, Pat>,
|
pats: Arena<PatId, Pat>,
|
||||||
source_map: BodySourceMap,
|
source_map: BodySourceMap,
|
||||||
params: Vec<PatId>,
|
params: Vec<PatId>,
|
||||||
body_expr: Option<ExprId>,
|
body_expr: Option<ExprId>,
|
||||||
|
resolver: Resolver,
|
||||||
|
// FIXEME: Its a quick hack,see issue #1196
|
||||||
|
is_in_macro: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprCollector {
|
impl<'a, DB> ExprCollector<&'a DB>
|
||||||
fn new(owner: DefWithBody) -> Self {
|
where
|
||||||
|
DB: HirDatabase,
|
||||||
|
{
|
||||||
|
fn new(owner: DefWithBody, resolver: Resolver, db: &'a DB) -> Self {
|
||||||
ExprCollector {
|
ExprCollector {
|
||||||
owner,
|
owner,
|
||||||
|
resolver,
|
||||||
|
db,
|
||||||
exprs: Arena::default(),
|
exprs: Arena::default(),
|
||||||
pats: Arena::default(),
|
pats: Arena::default(),
|
||||||
source_map: BodySourceMap::default(),
|
source_map: BodySourceMap::default(),
|
||||||
params: Vec::new(),
|
params: Vec::new(),
|
||||||
body_expr: None,
|
body_expr: None,
|
||||||
|
is_in_macro: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_expr(&mut self, expr: Expr, syntax_ptr: SyntaxNodePtr) -> ExprId {
|
fn alloc_expr(&mut self, expr: Expr, syntax_ptr: SyntaxNodePtr) -> ExprId {
|
||||||
let id = self.exprs.alloc(expr);
|
let id = self.exprs.alloc(expr);
|
||||||
self.source_map.expr_map.insert(syntax_ptr, id);
|
if !self.is_in_macro {
|
||||||
self.source_map.expr_map_back.insert(id, syntax_ptr);
|
self.source_map.expr_map.insert(syntax_ptr, id);
|
||||||
|
self.source_map.expr_map_back.insert(id, syntax_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
||||||
let id = self.pats.alloc(pat);
|
let id = self.pats.alloc(pat);
|
||||||
self.source_map.pat_map.insert(ptr, id);
|
|
||||||
self.source_map.pat_map_back.insert(id, ptr);
|
if !self.is_in_macro {
|
||||||
|
self.source_map.pat_map.insert(ptr, id);
|
||||||
|
self.source_map.pat_map_back.insert(id, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,7 +811,26 @@ impl ExprCollector {
|
|||||||
ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||||
ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||||
ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||||
ast::ExprKind::MacroCall(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
ast::ExprKind::MacroCall(e) => {
|
||||||
|
// very hacky.FIXME change to use the macro resolution
|
||||||
|
let path = e.path().and_then(Path::from_ast);
|
||||||
|
|
||||||
|
if let Some(call_id) = self.resolver.resolve_macro_call(self.db, path, e) {
|
||||||
|
if let Some(expr) = expand_macro_to_expr(self.db, call_id, e.token_tree()) {
|
||||||
|
log::debug!("macro expansion {}", expr.syntax().debug_dump());
|
||||||
|
let old = std::mem::replace(&mut self.is_in_macro, true);
|
||||||
|
let id = self.collect_expr(&expr);
|
||||||
|
self.is_in_macro = old;
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
// FIXME: Instead of just dropping the error from expansion
|
||||||
|
// report it
|
||||||
|
self.alloc_expr(Expr::Missing, syntax_ptr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.alloc_expr(Expr::Missing, syntax_ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,11 +988,25 @@ impl ExprCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expand_macro_to_expr(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
macro_call: MacroCallId,
|
||||||
|
args: Option<&ast::TokenTree>,
|
||||||
|
) -> Option<TreeArc<ast::Expr>> {
|
||||||
|
let rules = db.macro_def(macro_call.loc(db).def)?;
|
||||||
|
|
||||||
|
let args = mbe::ast_to_token_tree(args?)?.0;
|
||||||
|
|
||||||
|
let expanded = rules.expand(&args).ok()?;
|
||||||
|
|
||||||
|
mbe::token_tree_to_expr(&expanded).ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn body_with_source_map_query(
|
pub(crate) fn body_with_source_map_query(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
def: DefWithBody,
|
def: DefWithBody,
|
||||||
) -> (Arc<Body>, Arc<BodySourceMap>) {
|
) -> (Arc<Body>, Arc<BodySourceMap>) {
|
||||||
let mut collector = ExprCollector::new(def);
|
let mut collector = ExprCollector::new(def, def.resolver(db), db);
|
||||||
|
|
||||||
match def {
|
match def {
|
||||||
DefWithBody::Const(ref c) => collector.collect_const_body(&c.source(db).1),
|
DefWithBody::Const(ref c) => collector.collect_const_body(&c.source(db).1),
|
||||||
|
@ -104,6 +104,7 @@ pub struct CrateDefMap {
|
|||||||
/// However, do we want to put it as a global variable?
|
/// However, do we want to put it as a global variable?
|
||||||
poison_macros: FxHashSet<MacroDefId>,
|
poison_macros: FxHashSet<MacroDefId>,
|
||||||
|
|
||||||
|
local_macros: FxHashMap<Name, MacroDefId>,
|
||||||
diagnostics: Vec<DefDiagnostic>,
|
diagnostics: Vec<DefDiagnostic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +210,7 @@ impl CrateDefMap {
|
|||||||
modules,
|
modules,
|
||||||
public_macros: FxHashMap::default(),
|
public_macros: FxHashMap::default(),
|
||||||
poison_macros: FxHashSet::default(),
|
poison_macros: FxHashSet::default(),
|
||||||
|
local_macros: FxHashMap::default(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -270,6 +272,10 @@ impl CrateDefMap {
|
|||||||
(res.resolved_def, res.segment_index)
|
(res.resolved_def, res.segment_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_macro(&self, name: &Name) -> Option<&MacroDefId> {
|
||||||
|
self.public_macros.get(name).or(self.local_macros.get(name))
|
||||||
|
}
|
||||||
|
|
||||||
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
||||||
// the result.
|
// the result.
|
||||||
fn resolve_path_fp(
|
fn resolve_path_fp(
|
||||||
|
@ -131,6 +131,8 @@ where
|
|||||||
fn define_macro(&mut self, name: Name, macro_id: MacroDefId, export: bool) {
|
fn define_macro(&mut self, name: Name, macro_id: MacroDefId, export: bool) {
|
||||||
if export {
|
if export {
|
||||||
self.def_map.public_macros.insert(name.clone(), macro_id);
|
self.def_map.public_macros.insert(name.clone(), macro_id);
|
||||||
|
} else {
|
||||||
|
self.def_map.local_macros.insert(name.clone(), macro_id);
|
||||||
}
|
}
|
||||||
self.global_macro_scope.insert(name, macro_id);
|
self.global_macro_scope.insert(name, macro_id);
|
||||||
}
|
}
|
||||||
@ -517,12 +519,12 @@ where
|
|||||||
|
|
||||||
// Case 2: try to expand macro_rules from this crate, triggering
|
// Case 2: try to expand macro_rules from this crate, triggering
|
||||||
// recursive item collection.
|
// recursive item collection.
|
||||||
if let Some(¯o_id) =
|
if let Some(macro_id) =
|
||||||
mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name))
|
mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(&name))
|
||||||
{
|
{
|
||||||
let macro_call_id = MacroCallLoc { def: macro_id, ast_id }.id(self.def_collector.db);
|
let macro_call_id = MacroCallLoc { def: *macro_id, ast_id }.id(self.def_collector.db);
|
||||||
|
|
||||||
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_id);
|
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, *macro_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,6 +616,7 @@ mod tests {
|
|||||||
modules,
|
modules,
|
||||||
public_macros: FxHashMap::default(),
|
public_macros: FxHashMap::default(),
|
||||||
poison_macros: FxHashSet::default(),
|
poison_macros: FxHashSet::default(),
|
||||||
|
local_macros: FxHashMap::default(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -126,6 +126,10 @@ impl Path {
|
|||||||
}
|
}
|
||||||
self.segments.first().map(|s| &s.name)
|
self.segments.first().map(|s| &s.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expand_macro_expr(&self) -> Option<Name> {
|
||||||
|
self.as_ident().and_then(|name| Some(name.clone()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenericArgs {
|
impl GenericArgs {
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
//! Name resolution.
|
//! Name resolution.
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ra_syntax::ast;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ModuleDef,
|
ModuleDef,
|
||||||
code_model_api::Crate,
|
code_model_api::Crate,
|
||||||
|
MacroCallId,
|
||||||
|
MacroCallLoc,
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
name::{Name, KnownName},
|
name::{Name, KnownName},
|
||||||
nameres::{PerNs, CrateDefMap, CrateModuleId},
|
nameres::{PerNs, CrateDefMap, CrateModuleId},
|
||||||
@ -130,6 +134,31 @@ impl Resolver {
|
|||||||
resolution
|
resolution
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_macro_call(
|
||||||
|
&self,
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
path: Option<Path>,
|
||||||
|
call: &ast::MacroCall,
|
||||||
|
) -> Option<MacroCallId> {
|
||||||
|
let name = path.and_then(|path| path.expand_macro_expr()).unwrap_or_else(Name::missing);
|
||||||
|
let macro_def_id = self.module().and_then(|(module, _)| module.find_macro(&name));
|
||||||
|
if let Some(def_id) = macro_def_id {
|
||||||
|
self.module().and_then(|(module, _)| {
|
||||||
|
// we do this to get the ast_id for the macro call
|
||||||
|
// if we used the ast_id from the def_id variable
|
||||||
|
// it gives us the ast_id of the defenition site
|
||||||
|
let module = module.mk_module(module.root());
|
||||||
|
let hir_file_id = module.definition_source(db).0;
|
||||||
|
let ast_id = db.ast_id_map(hir_file_id).ast_id(call).with_file_id(hir_file_id);
|
||||||
|
let call_loc = MacroCallLoc { def: *def_id, ast_id }.id(db);
|
||||||
|
|
||||||
|
Some(call_loc)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the resolved path segments
|
/// Returns the resolved path segments
|
||||||
/// Which may be fully resolved, empty or partially resolved.
|
/// Which may be fully resolved, empty or partially resolved.
|
||||||
pub(crate) fn resolve_path_segments(&self, db: &impl HirDatabase, path: &Path) -> PathResult {
|
pub(crate) fn resolve_path_segments(&self, db: &impl HirDatabase, path: &Path) -> PathResult {
|
||||||
|
@ -2417,6 +2417,30 @@ fn test() -> u64 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_macros_expanded() {
|
||||||
|
assert_snapshot_matches!(
|
||||||
|
infer(r#"
|
||||||
|
struct Foo(Vec<i32>);
|
||||||
|
|
||||||
|
macro_rules! foo {
|
||||||
|
($($item:expr),*) => {
|
||||||
|
{
|
||||||
|
Foo(vec![$($item,)*])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = foo!(1,2);
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[156; 182) '{ ...,2); }': ()
|
||||||
|
[166; 167) 'x': Foo"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[ignore]
|
#[ignore]
|
||||||
#[test]
|
#[test]
|
||||||
fn method_resolution_trait_before_autoref() {
|
fn method_resolution_trait_before_autoref() {
|
||||||
@ -2510,6 +2534,7 @@ fn type_at(content: &str) -> String {
|
|||||||
fn infer(content: &str) -> String {
|
fn infer(content: &str) -> String {
|
||||||
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
||||||
let source_file = db.parse(file_id);
|
let source_file = db.parse(file_id);
|
||||||
|
|
||||||
let mut acc = String::new();
|
let mut acc = String::new();
|
||||||
acc.push_str("\n");
|
acc.push_str("\n");
|
||||||
|
|
||||||
@ -2532,6 +2557,7 @@ fn infer(content: &str) -> String {
|
|||||||
};
|
};
|
||||||
types.push((syntax_ptr, ty));
|
types.push((syntax_ptr, ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort ranges for consistency
|
// sort ranges for consistency
|
||||||
types.sort_by_key(|(ptr, _)| (ptr.range().start(), ptr.range().end()));
|
types.sort_by_key(|(ptr, _)| (ptr.range().start(), ptr.range().end()));
|
||||||
for (syntax_ptr, ty) in &types {
|
for (syntax_ptr, ty) in &types {
|
||||||
|
Loading…
Reference in New Issue
Block a user