Merge #10778
10778: internal: Skip test/bench attr expansion in resolution instead of collection r=Veykril a=Veykril This way we skip any path resolving to the test and bench attributes instead of just the lone identifiers(which could very well point to non-builtin attributes). bors r+ Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
1c49667c56
@ -34,11 +34,6 @@ macro_rules! rustc_attr {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: We shouldn't special case these at all, but as of now expanding attributes severely degrades
|
|
||||||
// user experience due to lacking support.
|
|
||||||
/// Built-in macro-like attributes.
|
|
||||||
pub const EXTRA_ATTRIBUTES: &[BuiltinAttribute] = &["test", "bench"];
|
|
||||||
|
|
||||||
/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc.
|
/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
|
@ -778,21 +778,18 @@ fn attr_macro_as_call_id(
|
|||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
) -> Result<MacroCallId, UnresolvedMacro> {
|
) -> Result<MacroCallId, UnresolvedMacro> {
|
||||||
let def: MacroDefId = resolver(item_attr.path.clone())
|
let attr_path = &item_attr.path;
|
||||||
|
|
||||||
|
let def = resolver(attr_path.clone())
|
||||||
.filter(MacroDefId::is_attribute)
|
.filter(MacroDefId::is_attribute)
|
||||||
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
|
.ok_or_else(|| UnresolvedMacro { path: attr_path.clone() })?;
|
||||||
let last_segment = item_attr
|
let last_segment =
|
||||||
.path
|
attr_path.segments().last().ok_or_else(|| UnresolvedMacro { path: attr_path.clone() })?;
|
||||||
.segments()
|
let mut arg = match macro_attr.input.as_deref() {
|
||||||
.last()
|
Some(attr::AttrInput::TokenTree(tt, map)) => (tt.clone(), map.clone()),
|
||||||
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
|
_ => Default::default(),
|
||||||
let mut arg = match ¯o_attr.input {
|
|
||||||
Some(input) => match &**input {
|
|
||||||
attr::AttrInput::Literal(_) => Default::default(),
|
|
||||||
attr::AttrInput::TokenTree(tt, map) => (tt.clone(), map.clone()),
|
|
||||||
},
|
|
||||||
None => Default::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The parentheses are always disposed here.
|
// The parentheses are always disposed here.
|
||||||
arg.0.delimiter = None;
|
arg.0.delimiter = None;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use base_db::{CrateId, Edition, FileId, ProcMacroId};
|
|||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::FileAstId,
|
ast_id_map::FileAstId,
|
||||||
builtin_attr_macro::find_builtin_attr,
|
builtin_attr_macro::{find_builtin_attr, is_builtin_test_or_bench_attr},
|
||||||
builtin_derive_macro::find_builtin_derive,
|
builtin_derive_macro::find_builtin_derive,
|
||||||
builtin_fn_macro::find_builtin_macro,
|
builtin_fn_macro::find_builtin_macro,
|
||||||
name::{name, AsName, Name},
|
name::{name, AsName, Name},
|
||||||
@ -1142,7 +1142,30 @@ impl DefCollector<'_> {
|
|||||||
) {
|
) {
|
||||||
Ok(call_id) => {
|
Ok(call_id) => {
|
||||||
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
|
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
|
||||||
if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind {
|
|
||||||
|
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
||||||
|
// due to duplicating functions into macro expansions
|
||||||
|
if is_builtin_test_or_bench_attr(loc.def) {
|
||||||
|
let file_id = ast_id.ast_id.file_id;
|
||||||
|
let item_tree = self.db.file_item_tree(file_id);
|
||||||
|
let mod_dir = self.mod_dirs[&directive.module_id].clone();
|
||||||
|
self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
|
||||||
|
ModCollector {
|
||||||
|
def_collector: &mut *self,
|
||||||
|
macro_depth: directive.depth,
|
||||||
|
module_id: directive.module_id,
|
||||||
|
tree_id: TreeId::new(file_id, None),
|
||||||
|
item_tree: &item_tree,
|
||||||
|
mod_dir,
|
||||||
|
}
|
||||||
|
.collect(&[*mod_item]);
|
||||||
|
|
||||||
|
// Remove the original directive since we resolved it.
|
||||||
|
res = ReachedFixedPoint::No;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
|
||||||
if exp.is_dummy() {
|
if exp.is_dummy() {
|
||||||
// Proc macros that cannot be expanded are treated as not
|
// Proc macros that cannot be expanded are treated as not
|
||||||
// resolved, in order to fall back later.
|
// resolved, in order to fall back later.
|
||||||
@ -1774,7 +1797,6 @@ impl ModCollector<'_, '_> {
|
|||||||
let name = name.to_smol_str();
|
let name = name.to_smol_str();
|
||||||
let is_inert = builtin_attr::INERT_ATTRIBUTES
|
let is_inert = builtin_attr::INERT_ATTRIBUTES
|
||||||
.iter()
|
.iter()
|
||||||
.chain(builtin_attr::EXTRA_ATTRIBUTES)
|
|
||||||
.copied()
|
.copied()
|
||||||
.chain(self.def_collector.registered_attrs.iter().map(AsRef::as_ref))
|
.chain(self.def_collector.registered_attrs.iter().map(AsRef::as_ref))
|
||||||
.any(|attr| name == *attr);
|
.any(|attr| name == *attr);
|
||||||
|
@ -46,6 +46,16 @@ register_builtin! {
|
|||||||
(test_case, TestCase) => dummy_attr_expand
|
(test_case, TestCase) => dummy_attr_expand
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_builtin_test_or_bench_attr(makro: MacroDefId) -> bool {
|
||||||
|
match makro.kind {
|
||||||
|
MacroDefKind::BuiltInAttr(expander, ..) => {
|
||||||
|
BuiltinAttrExpander::find_by_name(&name!(test)) == Some(expander)
|
||||||
|
|| BuiltinAttrExpander::find_by_name(&name!(bench)) == Some(expander)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find_builtin_attr(
|
pub fn find_builtin_attr(
|
||||||
ident: &name::Name,
|
ident: &name::Name,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
|
@ -701,16 +701,6 @@ impl<N: AstNode> InFile<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InFile<ast::Fn> {
|
|
||||||
pub fn map_out_of_test_attr(self, db: &dyn db::AstDatabase) -> InFile<ast::Fn> {
|
|
||||||
(|| {
|
|
||||||
let InFile { file_id, value } = self.file_id.call_node(db)?;
|
|
||||||
ast::Fn::cast(value).map(|n| InFile::new(file_id, n))
|
|
||||||
})()
|
|
||||||
.unwrap_or(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In Rust, macros expand token trees to token trees. When we want to turn a
|
/// In Rust, macros expand token trees to token trees. When we want to turn a
|
||||||
/// token tree into an AST node, we need to figure out what kind of AST node we
|
/// token tree into an AST node, we need to figure out what kind of AST node we
|
||||||
/// want: something like `foo` can be a type, an expression, or a pattern.
|
/// want: something like `foo` can be a type, an expression, or a pattern.
|
||||||
|
@ -237,8 +237,7 @@ fn find_related_tests(
|
|||||||
.map(|f| hir::InFile::new(sema.hir_file_for(f.syntax()), f));
|
.map(|f| hir::InFile::new(sema.hir_file_for(f.syntax()), f));
|
||||||
|
|
||||||
for fn_def in functions {
|
for fn_def in functions {
|
||||||
// #[test/bench] expands to just the item causing us to lose the attribute, so recover them by going out of the attribute
|
let InFile { value: fn_def, .. } = &fn_def;
|
||||||
let InFile { value: fn_def, .. } = &fn_def.map_out_of_test_attr(sema.db);
|
|
||||||
if let Some(runnable) = as_test_runnable(sema, fn_def) {
|
if let Some(runnable) = as_test_runnable(sema, fn_def) {
|
||||||
// direct test
|
// direct test
|
||||||
tests.insert(runnable);
|
tests.insert(runnable);
|
||||||
@ -294,8 +293,7 @@ fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
|
pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
|
||||||
// #[test/bench] expands to just the item causing us to lose the attribute, so recover them by going out of the attribute
|
let func = def.source(sema.db)?;
|
||||||
let func = def.source(sema.db)?.map_out_of_test_attr(sema.db);
|
|
||||||
let name_string = def.name(sema.db).to_string();
|
let name_string = def.name(sema.db).to_string();
|
||||||
|
|
||||||
let root = def.module(sema.db).krate().root_module(sema.db);
|
let root = def.module(sema.db).krate().root_module(sema.db);
|
||||||
@ -504,8 +502,6 @@ fn has_test_function_or_multiple_test_submodules(
|
|||||||
match item {
|
match item {
|
||||||
hir::ModuleDef::Function(f) => {
|
hir::ModuleDef::Function(f) => {
|
||||||
if let Some(it) = f.source(sema.db) {
|
if let Some(it) = f.source(sema.db) {
|
||||||
// #[test/bench] expands to just the item causing us to lose the attribute, so recover them by going out of the attribute
|
|
||||||
let it = it.map_out_of_test_attr(sema.db);
|
|
||||||
if test_related_attribute(&it.value).is_some() {
|
if test_related_attribute(&it.value).is_some() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user