Auto merge of #52841 - petrochenkov:premacro, r=alexcrichton

resolve: Implement prelude search for macro paths, implement tool attributes

When identifier is macro path is resolved in scopes (i.e. the first path segment - `foo` in `foo::mac!()` or `foo!()`), scopes are searched in the same order as for non-macro paths - items in modules, extern prelude, tool prelude (see later), standard library prelude, language prelude, but with some extra shadowing restrictions (names from globs and macro expansions cannot shadow names from outer scopes). See the comment in `fn resolve_lexical_macro_path_segment` for more details.

"Tool prelude" currently contains two "tool modules" `rustfmt` and `clippy`, and is searched immediately after extern prelude.
This makes the [possible long-term solution](https://github.com/rust-lang/rfcs/blob/master/text/2103-tool-attributes.md#long-term-solution) for tool attributes exactly equivalent to the existing extern prelude scheme, except that `--extern=my_crate` making crate names available in scope is replaced with something like `--tool=my_tool` making tool names available in scope.

The `tool_attributes` feature is still unstable and `#![feature(tool_attributes)]` now implicitly enables `#![feature(use_extern_macros)]`. `use_extern_macros` is a prerequisite for `tool_attributes`, so their stabilization will happen in the same order.
If `use_extern_macros` is not enabled, then tool attributes are treated as custom attributes (this is temporary, anyway).

Fixes https://github.com/rust-lang/rust/issues/52576
Fixes https://github.com/rust-lang/rust/issues/52512
Fixes https://github.com/rust-lang/rust/issues/51277
cc https://github.com/rust-lang/rust/issues/52269
This commit is contained in:
bors 2018-08-02 21:39:14 +00:00
commit 40e4b6ee3d
42 changed files with 683 additions and 143 deletions

View File

@ -49,6 +49,7 @@ pub enum Def {
PrimTy(hir::PrimTy),
TyParam(DefId),
SelfTy(Option<DefId> /* trait */, Option<DefId> /* impl */),
ToolMod, // e.g. `rustfmt` in `#[rustfmt::skip]`
// Value namespace
Fn(DefId),
@ -67,6 +68,7 @@ pub enum Def {
// Macro namespace
Macro(DefId, MacroKind),
NonMacroAttr, // e.g. `#[inline]` or `#[rustfmt::skip]`
GlobalAsm(DefId),
@ -259,6 +261,8 @@ impl Def {
Def::Label(..) |
Def::PrimTy(..) |
Def::SelfTy(..) |
Def::ToolMod |
Def::NonMacroAttr |
Def::Err => {
bug!("attempted .def_id() on invalid def: {:?}", self)
}
@ -299,6 +303,8 @@ impl Def {
Def::SelfTy(..) => "self type",
Def::Macro(.., macro_kind) => macro_kind.descr(),
Def::GlobalAsm(..) => "global asm",
Def::ToolMod => "tool module",
Def::NonMacroAttr => "non-macro attribute",
Def::Err => "unresolved item",
}
}

View File

@ -1016,6 +1016,8 @@ impl_stable_hash_for!(enum hir::def::Def {
Label(node_id),
Macro(def_id, macro_kind),
GlobalAsm(def_id),
ToolMod,
NonMacroAttr,
Err
});

View File

@ -629,7 +629,8 @@ impl<'a> Resolver<'a> {
pub fn get_macro(&mut self, def: Def) -> Lrc<SyntaxExtension> {
let def_id = match def {
Def::Macro(def_id, ..) => def_id,
_ => panic!("Expected Def::Macro(..)"),
Def::NonMacroAttr => return Lrc::new(SyntaxExtension::NonMacroAttr),
_ => panic!("Expected Def::Macro(..) or Def::NonMacroAttr"),
};
if let Some(ext) = self.macro_map.get(&def_id) {
return ext.clone();

View File

@ -131,8 +131,7 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
directive.vis.get() == ty::Visibility::Public ||
directive.span.is_dummy() => {
if let ImportDirectiveSubclass::MacroUse = directive.subclass {
if resolver.session.features_untracked().use_extern_macros &&
!directive.span.is_dummy() {
if resolver.use_extern_macros && !directive.span.is_dummy() {
resolver.session.buffer_lint(
lint::builtin::MACRO_USE_EXTERN_CRATE,
directive.id,

View File

@ -86,6 +86,10 @@ mod check_unused;
mod build_reduced_graph;
mod resolve_imports;
fn is_known_tool(name: Name) -> bool {
["clippy", "rustfmt"].contains(&&*name.as_str())
}
/// A free importable items suggested in case of resolution failure.
struct ImportSuggestion {
path: Path,
@ -200,16 +204,11 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
err.span_label(typaram_span, "type variable from outer function");
}
},
Def::Mod(..) | Def::Struct(..) | Def::Union(..) | Def::Enum(..) | Def::Variant(..) |
Def::Trait(..) | Def::TyAlias(..) | Def::TyForeign(..) | Def::TraitAlias(..) |
Def::AssociatedTy(..) | Def::PrimTy(..) | Def::Fn(..) | Def::Const(..) |
Def::Static(..) | Def::StructCtor(..) | Def::VariantCtor(..) | Def::Method(..) |
Def::AssociatedConst(..) | Def::Local(..) | Def::Upvar(..) | Def::Label(..) |
Def::Existential(..) | Def::AssociatedExistential(..) |
Def::Macro(..) | Def::GlobalAsm(..) | Def::Err =>
_ => {
bug!("TypeParametersFromOuterFunction should only be used with Def::SelfTy or \
Def::TyParam")
}
}
// Try to retrieve the span of the function signature and generate a new message with
// a local type parameter
@ -1711,9 +1710,7 @@ impl<'a> Resolver<'a> {
vis: ty::Visibility::Public,
}),
// The `proc_macro` and `decl_macro` features imply `use_extern_macros`
use_extern_macros:
features.use_extern_macros || features.decl_macro,
use_extern_macros: features.use_extern_macros(),
crate_loader,
macro_names: FxHashSet(),
@ -1846,6 +1843,7 @@ impl<'a> Resolver<'a> {
path_span: Span)
-> Option<LexicalScopeBinding<'a>> {
let record_used = record_used_id.is_some();
assert!(ns == TypeNS || ns == ValueNS);
if ns == TypeNS {
ident.span = if ident.name == keywords::SelfType.name() {
// FIXME(jseyfried) improve `Self` hygiene
@ -1922,8 +1920,9 @@ impl<'a> Resolver<'a> {
return Some(LexicalScopeBinding::Item(binding))
}
_ if poisoned.is_some() => break,
Err(Undetermined) => return None,
Err(Determined) => {}
Err(Determined) => continue,
Err(Undetermined) =>
span_bug!(ident.span, "undetermined resolution during main resolution pass"),
}
}
@ -1945,6 +1944,11 @@ impl<'a> Resolver<'a> {
ident.span, Mark::root()).to_name_binding(self.arenas);
return Some(LexicalScopeBinding::Item(binding));
}
if ns == TypeNS && is_known_tool(ident.name) {
let binding = (Def::ToolMod, ty::Visibility::Public,
ident.span, Mark::root()).to_name_binding(self.arenas);
return Some(LexicalScopeBinding::Item(binding));
}
if let Some(prelude) = self.prelude {
if let Ok(binding) = self.resolve_ident_in_module_unadjusted(prelude, ident, ns,
false, false, path_span) {
@ -3505,6 +3509,8 @@ impl<'a> Resolver<'a> {
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(def);
if let Some(next_module) = binding.module() {
module = Some(next_module);
} else if def == Def::ToolMod && i + 1 != path.len() {
return PathResult::NonModule(PathResolution::new(Def::NonMacroAttr))
} else if def == Def::Err {
return PathResult::NonModule(err_path_resolution());
} else if opt_ns.is_some() && (is_last || maybe_assoc) {

View File

@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use {AmbiguityError, CrateLint, Resolver, ResolutionError, resolve_error};
use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult};
use Namespace::{self, MacroNS};
use {AmbiguityError, CrateLint, Resolver, ResolutionError, is_known_tool, resolve_error};
use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, ToNameBinding};
use Namespace::{self, TypeNS, MacroNS};
use build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport};
use resolve_imports::ImportResolver;
use rustc::hir::def_id::{DefId, BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, DefIndex,
@ -27,7 +27,7 @@ use syntax::ext::expand::{self, AstFragment, AstFragmentKind, Invocation, Invoca
use syntax::ext::hygiene::{self, Mark};
use syntax::ext::placeholders::placeholder;
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{self, emit_feature_err, GateIssue};
use syntax::feature_gate::{self, feature_err, emit_feature_err, is_builtin_attr_name, GateIssue};
use syntax::fold::{self, Folder};
use syntax::parse::parser::PathStyle;
use syntax::parse::token::{self, Token};
@ -326,6 +326,18 @@ impl<'a> base::Resolver for Resolver<'a> {
if let Def::Macro(_, MacroKind::ProcMacroStub) = def {
self.report_proc_macro_stub(invoc.span());
return Err(Determinacy::Determined);
} else if let Def::NonMacroAttr = def {
if let InvocationKind::Attr { .. } = invoc.kind {
if !self.session.features_untracked().tool_attributes {
feature_err(&self.session.parse_sess, "tool_attributes",
invoc.span(), GateIssue::Language,
"tool attributes are unstable").emit();
}
return Ok(Some(Lrc::new(SyntaxExtension::NonMacroAttr)));
} else {
self.report_non_macro_attr(invoc.path_span());
return Err(Determinacy::Determined);
}
}
let def_id = def.def_id();
@ -348,6 +360,9 @@ impl<'a> base::Resolver for Resolver<'a> {
if let Def::Macro(_, MacroKind::ProcMacroStub) = def {
self.report_proc_macro_stub(path.span);
return Err(Determinacy::Determined);
} else if let Def::NonMacroAttr = def {
self.report_non_macro_attr(path.span);
return Err(Determinacy::Determined);
}
self.unused_macros.remove(&def.def_id());
Ok(self.get_macro(def))
@ -378,6 +393,11 @@ impl<'a> Resolver<'a> {
"can't use a procedural macro from the same crate that defines it");
}
fn report_non_macro_attr(&self, span: Span) {
self.session.span_err(span,
"expected a macro, found non-macro attribute");
}
fn resolve_invoc_to_def(&mut self, invoc: &mut Invocation, scope: Mark, force: bool)
-> Result<Def, Determinacy> {
let (attr, traits, item) = match invoc.kind {
@ -450,7 +470,15 @@ impl<'a> Resolver<'a> {
fn resolve_macro_to_def(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool)
-> Result<Def, Determinacy> {
if kind != MacroKind::Bang && path.segments.len() > 1 {
let def = self.resolve_macro_to_def_inner(scope, path, kind, force);
if def != Err(Determinacy::Undetermined) {
// Do not report duplicated errors on every undetermined resolution.
path.segments.iter().find(|segment| segment.args.is_some()).map(|segment| {
self.session.span_err(segment.args.as_ref().unwrap().span(),
"generic arguments in macro path");
});
}
if kind != MacroKind::Bang && path.segments.len() > 1 && def != Ok(Def::NonMacroAttr) {
if !self.session.features_untracked().proc_macro_path_invoc {
emit_feature_err(
&self.session.parse_sess,
@ -462,15 +490,6 @@ impl<'a> Resolver<'a> {
);
}
}
let def = self.resolve_macro_to_def_inner(scope, path, kind, force);
if def != Err(Determinacy::Undetermined) {
// Do not report duplicated errors on every undetermined resolution.
path.segments.iter().find(|segment| segment.args.is_some()).map(|segment| {
self.session.span_err(segment.args.as_ref().unwrap().span(),
"generic arguments in macro path");
});
}
def
}
@ -544,67 +563,226 @@ impl<'a> Resolver<'a> {
result
}
// Resolve the initial segment of a non-global macro path (e.g. `foo` in `foo::bar!();`)
// Resolve the initial segment of a non-global macro path
// (e.g. `foo` in `foo::bar!(); or `foo!();`).
// This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during
// expansion and import resolution (perhaps they can be merged in the future).
pub fn resolve_lexical_macro_path_segment(&mut self,
mut ident: Ident,
ns: Namespace,
record_used: bool,
path_span: Span)
-> Result<MacroBinding<'a>, Determinacy> {
ident = ident.modern();
let mut module = Some(self.current_module);
let mut potential_illegal_shadower = Err(Determinacy::Determined);
let determinacy =
if record_used { Determinacy::Determined } else { Determinacy::Undetermined };
loop {
let orig_current_module = self.current_module;
let result = if let Some(module) = module {
self.current_module = module; // Lexical resolutions can never be a privacy error.
// Since expanded macros may not shadow the lexical scope and
// globs may not shadow global macros (both enforced below),
// we resolve with restricted shadowing (indicated by the penultimate argument).
self.resolve_ident_in_module_unadjusted(
module, ident, ns, true, record_used, path_span,
).map(MacroBinding::Modern)
} else {
self.macro_prelude.get(&ident.name).cloned().ok_or(determinacy)
.map(MacroBinding::Global)
};
self.current_module = orig_current_module;
// General principles:
// 1. Not controlled (user-defined) names should have higher priority than controlled names
// built into the language or standard library. This way we can add new names into the
// language or standard library without breaking user code.
// 2. "Closed set" below means new names can appear after the current resolution attempt.
// Places to search (in order of decreasing priority):
// (Type NS)
// 1. FIXME: Ribs (type parameters), there's no necessary infrastructure yet
// (open set, not controlled).
// 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
// (open, not controlled).
// 3. Extern prelude (closed, not controlled).
// 4. Tool modules (closed, controlled right now, but not in the future).
// 5. Standard library prelude (de-facto closed, controlled).
// 6. Language prelude (closed, controlled).
// (Macro NS)
// 1. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
// (open, not controlled).
// 2. Macro prelude (language, standard library, user-defined legacy plugins lumped into
// one set) (open, the open part is from macro expansions, not controlled).
// 2a. User-defined prelude from macro-use
// (open, the open part is from macro expansions, not controlled).
// 2b. Standard library prelude, currently just a macro-use (closed, controlled)
// 2c. Language prelude, perhaps including builtin attributes
// (closed, controlled, except for legacy plugins).
// 3. Builtin attributes (closed, controlled).
match result.map(MacroBinding::binding) {
Ok(binding) => {
if !record_used {
return result;
assert!(ns == TypeNS || ns == MacroNS);
ident = ident.modern();
// Names from inner scope that can't shadow names from outer scopes, e.g.
// mod m { ... }
// {
// use prefix::*; // if this imports another `m`, then it can't shadow the outer `m`
// // and we have and ambiguity error
// m::mac!();
// }
// This includes names from globs and from macro expansions.
let mut potentially_ambiguous_result: Option<MacroBinding> = None;
enum WhereToResolve<'a> {
Module(Module<'a>),
MacroPrelude,
BuiltinAttrs,
ExternPrelude,
ToolPrelude,
StdLibPrelude,
PrimitiveTypes,
}
if let Ok(MacroBinding::Modern(shadower)) = potential_illegal_shadower {
if shadower.def() != binding.def() {
let name = ident.name;
// Go through all the scopes and try to resolve the name.
let mut where_to_resolve = WhereToResolve::Module(self.current_module);
let mut use_prelude = !self.current_module.no_implicit_prelude;
loop {
let result = match where_to_resolve {
WhereToResolve::Module(module) => {
let orig_current_module = mem::replace(&mut self.current_module, module);
let binding = self.resolve_ident_in_module_unadjusted(
module, ident, ns, true, record_used, path_span,
);
self.current_module = orig_current_module;
binding.map(MacroBinding::Modern)
}
WhereToResolve::MacroPrelude => {
match self.macro_prelude.get(&ident.name).cloned() {
Some(binding) => Ok(MacroBinding::Global(binding)),
None => Err(Determinacy::Determined),
}
}
WhereToResolve::BuiltinAttrs => {
if is_builtin_attr_name(ident.name) {
let binding = (Def::NonMacroAttr, ty::Visibility::Public,
ident.span, Mark::root()).to_name_binding(self.arenas);
Ok(MacroBinding::Global(binding))
} else {
Err(Determinacy::Determined)
}
}
WhereToResolve::ExternPrelude => {
if use_prelude && self.extern_prelude.contains(&ident.name) {
if !self.session.features_untracked().extern_prelude &&
!self.ignore_extern_prelude_feature {
feature_err(&self.session.parse_sess, "extern_prelude",
ident.span, GateIssue::Language,
"access to extern crates through prelude is experimental")
.emit();
}
let crate_id =
self.crate_loader.process_path_extern(ident.name, ident.span);
let crate_root =
self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
self.populate_module_if_necessary(crate_root);
let binding = (crate_root, ty::Visibility::Public,
ident.span, Mark::root()).to_name_binding(self.arenas);
Ok(MacroBinding::Global(binding))
} else {
Err(Determinacy::Determined)
}
}
WhereToResolve::ToolPrelude => {
if use_prelude && is_known_tool(ident.name) {
let binding = (Def::ToolMod, ty::Visibility::Public,
ident.span, Mark::root()).to_name_binding(self.arenas);
Ok(MacroBinding::Global(binding))
} else {
Err(Determinacy::Determined)
}
}
WhereToResolve::StdLibPrelude => {
let mut result = Err(Determinacy::Determined);
if use_prelude {
if let Some(prelude) = self.prelude {
if let Ok(binding) =
self.resolve_ident_in_module_unadjusted(prelude, ident, ns,
false, false, path_span) {
result = Ok(MacroBinding::Global(binding));
}
}
}
result
}
WhereToResolve::PrimitiveTypes => {
if let Some(prim_ty) =
self.primitive_type_table.primitive_types.get(&ident.name).cloned() {
let binding = (Def::PrimTy(prim_ty), ty::Visibility::Public,
ident.span, Mark::root()).to_name_binding(self.arenas);
Ok(MacroBinding::Global(binding))
} else {
Err(Determinacy::Determined)
}
}
};
macro_rules! continue_search { () => {
where_to_resolve = match where_to_resolve {
WhereToResolve::Module(module) => {
match self.hygienic_lexical_parent(module, &mut ident.span) {
Some(parent_module) => WhereToResolve::Module(parent_module),
None => {
use_prelude = !module.no_implicit_prelude;
if ns == MacroNS {
WhereToResolve::MacroPrelude
} else {
WhereToResolve::ExternPrelude
}
}
}
}
WhereToResolve::MacroPrelude => WhereToResolve::BuiltinAttrs,
WhereToResolve::BuiltinAttrs => break, // nowhere else to search
WhereToResolve::ExternPrelude => WhereToResolve::ToolPrelude,
WhereToResolve::ToolPrelude => WhereToResolve::StdLibPrelude,
WhereToResolve::StdLibPrelude => WhereToResolve::PrimitiveTypes,
WhereToResolve::PrimitiveTypes => break, // nowhere else to search
};
continue;
}}
match result {
Ok(result) => {
if !record_used {
return Ok(result);
}
let binding = result.binding();
// Found a solution that is ambiguous with a previously found solution.
// Push an ambiguity error for later reporting and
// return something for better recovery.
if let Some(previous_result) = potentially_ambiguous_result {
if binding.def() != previous_result.binding().def() {
self.ambiguity_errors.push(AmbiguityError {
span: path_span,
name,
b1: shadower,
name: ident.name,
b1: previous_result.binding(),
b2: binding,
lexical: true,
});
return potential_illegal_shadower;
return Ok(previous_result);
}
}
if binding.is_glob_import() || binding.expansion != Mark::root() {
potential_illegal_shadower = result;
} else {
return result;
}
},
Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
Err(Determinacy::Determined) => {}
}
module = match module {
Some(module) => self.hygienic_lexical_parent(module, &mut ident.span),
None => return potential_illegal_shadower,
// Found a solution that's not an ambiguity yet, but is "suspicious" and
// can participate in ambiguities later on.
// Remember it and go search for other solutions in outer scopes.
if binding.is_glob_import() || binding.expansion != Mark::root() {
potentially_ambiguous_result = Some(result);
continue_search!();
}
// Found a solution that can't be ambiguous, great success.
return Ok(result);
},
Err(Determinacy::Determined) => {
continue_search!();
}
Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
}
}
// Previously found potentially ambiguous result turned out to not be ambiguous after all.
if let Some(previous_result) = potentially_ambiguous_result {
return Ok(previous_result);
}
if record_used { Err(Determinacy::Determined) } else { Err(Determinacy::Undetermined) }
}
pub fn resolve_legacy_scope(&mut self,

View File

@ -811,6 +811,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
HirDef::Label(..) |
HirDef::Macro(..) |
HirDef::GlobalAsm(..) |
HirDef::ToolMod |
HirDef::NonMacroAttr |
HirDef::Err => None,
}
}

View File

@ -65,17 +65,8 @@ pub fn is_known(attr: &Attribute) -> bool {
})
}
const RUST_KNOWN_TOOL: &[&str] = &["clippy", "rustfmt"];
const RUST_KNOWN_LINT_TOOL: &[&str] = &["clippy"];
pub fn is_known_tool(attr: &Attribute) -> bool {
let tool_name =
attr.path.segments.iter().next().expect("empty path in attribute").ident.name;
RUST_KNOWN_TOOL.contains(&tool_name.as_str().as_ref())
}
pub fn is_known_lint_tool(m_item: Ident) -> bool {
RUST_KNOWN_LINT_TOOL.contains(&m_item.as_str().as_ref())
["clippy"].contains(&m_item.as_str().as_ref())
}
impl NestedMetaItem {
@ -221,10 +212,6 @@ impl Attribute {
pub fn is_value_str(&self) -> bool {
self.value_str().is_some()
}
pub fn is_scoped(&self) -> bool {
self.path.segments.len() > 1
}
}
impl MetaItem {

View File

@ -588,6 +588,9 @@ impl MacroKind {
/// An enum representing the different kinds of syntax extensions.
pub enum SyntaxExtension {
/// A trivial "extension" that does nothing, only keeps the attribute and marks it as known.
NonMacroAttr,
/// A syntax extension that is attached to an item and creates new items
/// based upon it.
///
@ -667,6 +670,7 @@ impl SyntaxExtension {
SyntaxExtension::IdentTT(..) |
SyntaxExtension::ProcMacro { .. } =>
MacroKind::Bang,
SyntaxExtension::NonMacroAttr |
SyntaxExtension::MultiDecorator(..) |
SyntaxExtension::MultiModifier(..) |
SyntaxExtension::AttrProcMacro(..) =>
@ -696,6 +700,7 @@ impl SyntaxExtension {
SyntaxExtension::AttrProcMacro(.., edition) |
SyntaxExtension::ProcMacroDerive(.., edition) => edition,
// Unstable legacy stuff
SyntaxExtension::NonMacroAttr |
SyntaxExtension::IdentTT(..) |
SyntaxExtension::MultiDecorator(..) |
SyntaxExtension::MultiModifier(..) |

View File

@ -37,7 +37,7 @@ use visit::{self, Visitor};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::mem;
use std::{iter, mem};
use std::rc::Rc;
use std::path::PathBuf;
@ -244,6 +244,15 @@ impl Invocation {
}
}
pub fn path_span(&self) -> Span {
match self.kind {
InvocationKind::Bang { ref mac, .. } => mac.node.path.span,
InvocationKind::Attr { attr: Some(ref attr), .. } => attr.path.span,
InvocationKind::Attr { attr: None, .. } => DUMMY_SP,
InvocationKind::Derive { ref path, .. } => path.span,
}
}
pub fn attr_id(&self) -> Option<ast::AttrId> {
match self.kind {
InvocationKind::Attr { attr: Some(ref attr), .. } => Some(attr.id),
@ -568,6 +577,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
});
match *ext {
NonMacroAttr => {
attr::mark_known(&attr);
let item = item.map_attrs(|mut attrs| { attrs.push(attr); attrs });
Some(invoc.fragment_kind.expect_from_annotatables(iter::once(item)))
}
MultiModifier(ref mac) => {
let meta = attr.parse_meta(self.cx.parse_sess)
.map_err(|mut e| { e.emit(); }).ok()?;
@ -812,7 +826,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
}
MultiDecorator(..) | MultiModifier(..) | AttrProcMacro(..) => {
MultiDecorator(..) | MultiModifier(..) |
AttrProcMacro(..) | SyntaxExtension::NonMacroAttr => {
self.cx.span_err(path.span,
&format!("`{}` can only be used in attributes", path));
self.cx.trace_macros_diag();
@ -1669,13 +1684,16 @@ impl<'feat> ExpansionConfig<'feat> {
fn enable_allow_internal_unstable = allow_internal_unstable,
fn enable_custom_derive = custom_derive,
fn enable_format_args_nl = format_args_nl,
fn use_extern_macros_enabled = use_extern_macros,
fn macros_in_extern_enabled = macros_in_extern,
fn proc_macro_mod = proc_macro_mod,
fn proc_macro_gen = proc_macro_gen,
fn proc_macro_expr = proc_macro_expr,
fn proc_macro_non_items = proc_macro_non_items,
}
pub fn use_extern_macros_enabled(&self) -> bool {
self.features.map_or(false, |features| features.use_extern_macros())
}
}
// A Marker adds the given mark to the syntax context.

View File

@ -80,6 +80,11 @@ macro_rules! declare_features {
{
$(f(stringify!($feature), self.$feature);)+
}
pub fn use_extern_macros(&self) -> bool {
// The `decl_macro` and `tool_attributes` features imply `use_extern_macros`.
self.use_extern_macros || self.decl_macro || self.tool_attributes
}
}
};
@ -689,6 +694,10 @@ pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, Att
BUILTIN_ATTRIBUTES.iter().filter(|a| a.2.is_deprecated()).collect()
}
pub fn is_builtin_attr_name(name: ast::Name) -> bool {
BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| name == builtin_name)
}
pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| attr.check_name(builtin_name)) ||
attr.name().as_str().starts_with("rustc_")
@ -1198,28 +1207,9 @@ impl<'a> Context<'a> {
// before the plugin attributes are registered
// so we skip this then
if !is_macro {
if attr.is_scoped() {
gate_feature!(self, tool_attributes, attr.span,
&format!("scoped attribute `{}` is experimental", attr.path));
if attr::is_known_tool(attr) {
attr::mark_used(attr);
} else {
span_err!(
self.parse_sess.span_diagnostic,
attr.span,
E0694,
"an unknown tool name found in scoped attribute: `{}`.",
attr.path
);
}
} else {
gate_feature!(self, custom_attribute, attr.span,
&format!("The attribute `{}` is currently \
unknown to the compiler and \
may have meaning \
added to it in the future",
attr.path));
}
let msg = format!("The attribute `{}` is currently unknown to the compiler and \
may have meaning added to it in the future", attr.path);
gate_feature!(self, custom_attribute, attr.span, &msg);
}
}
}
@ -1529,7 +1519,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
}
if self.context.features.use_extern_macros && attr::is_known(attr) {
if self.context.features.use_extern_macros() && attr::is_known(attr) {
return
}
@ -2004,7 +1994,7 @@ impl FeatureChecker {
// the branching can be eliminated by modifying `set!()` to set these spans
// only for the features that need to be checked for mutual exclusion.
fn collect(&mut self, features: &Features, span: Span) {
if features.use_extern_macros {
if features.use_extern_macros() {
// If self.use_extern_macros is None, set to Some(span)
self.use_extern_macros = self.use_extern_macros.or(Some(span));
}

View File

@ -8,9 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(tool_attributes)]
#![feature(use_extern_macros, proc_macro_path_invoc)]
#![foo::bar] //~ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
#[foo::bar] //~ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
#[foo::bar] //~ ERROR failed to resolve. Use of undeclared type or module `foo`
fn main() {}

View File

@ -11,7 +11,7 @@
// aux-build:issue-42708.rs
// ignore-stage1
#![feature(decl_macro, use_extern_macros, proc_macro_path_invoc)]
#![feature(decl_macro, proc_macro_path_invoc)]
#![allow(unused)]
extern crate issue_42708;

View File

@ -11,7 +11,7 @@
// aux-build:issue-50061.rs
// ignore-stage1
#![feature(use_extern_macros, proc_macro_path_invoc, decl_macro)]
#![feature(proc_macro_path_invoc, decl_macro)]
extern crate issue_50061;

View File

@ -11,7 +11,7 @@
// aux-build:parent-source-spans.rs
// ignore-stage1
#![feature(use_extern_macros, decl_macro, proc_macro_non_items)]
#![feature(decl_macro, proc_macro_non_items)]
extern crate parent_source_spans;

View File

@ -12,3 +12,8 @@
macro_rules! mac {
($ident:ident) => { let $ident = 42; }
}
#[macro_export]
macro_rules! inline {
() => ()
}

View File

@ -1,4 +1,4 @@
error[E0658]: macro invocations in `extern {}` blocks are experimental. (see issue #49476)
error[E0658]: macro and proc-macro invocations in `extern {}` blocks are experimental. (see issue #49476)
--> $DIR/feature-gate-macros_in_extern.rs:29:5
|
LL | returns_isize!(rust_get_test_int);
@ -6,7 +6,7 @@ LL | returns_isize!(rust_get_test_int);
|
= help: add #![feature(macros_in_extern)] to the crate attributes to enable
error[E0658]: macro invocations in `extern {}` blocks are experimental. (see issue #49476)
error[E0658]: macro and proc-macro invocations in `extern {}` blocks are experimental. (see issue #49476)
--> $DIR/feature-gate-macros_in_extern.rs:31:5
|
LL | takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
@ -14,7 +14,7 @@ LL | takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
|
= help: add #![feature(macros_in_extern)] to the crate attributes to enable
error[E0658]: macro invocations in `extern {}` blocks are experimental. (see issue #49476)
error[E0658]: macro and proc-macro invocations in `extern {}` blocks are experimental. (see issue #49476)
--> $DIR/feature-gate-macros_in_extern.rs:33:5
|
LL | emits_nothing!();

View File

@ -8,8 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(use_extern_macros)]
fn main() {
#[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental
#[rustfmt::skip] //~ ERROR tool attributes are unstable
let x = 3
;
}

View File

@ -1,7 +1,7 @@
error[E0658]: scoped attribute `rustfmt::skip` is experimental (see issue #44690)
--> $DIR/feature-gate-tool_attributes.rs:12:5
error[E0658]: tool attributes are unstable (see issue #44690)
--> $DIR/feature-gate-tool_attributes.rs:14:5
|
LL | #[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental
LL | #[rustfmt::skip] //~ ERROR tool attributes are unstable
| ^^^^^^^^^^^^^^^^
|
= help: add #![feature(tool_attributes)] to the crate attributes to enable

View File

@ -9,6 +9,6 @@
// except according to those terms.
fn main() {
print!(test!());
print!(testo!());
//~^ ERROR: format argument must be a string literal
}

View File

@ -1,11 +1,11 @@
error: format argument must be a string literal
--> $DIR/issue-11692-1.rs:12:12
|
LL | print!(test!());
| ^^^^^^^
LL | print!(testo!());
| ^^^^^^^^
help: you might be missing a string literal to format with
|
LL | print!("{}", test!());
LL | print!("{}", testo!());
| ^^^^^
error: aborting due to previous error

View File

@ -10,5 +10,5 @@
fn main() {
concat!(test!());
//~^ ERROR cannot find macro `test!` in this scope
//~^ ERROR expected a macro, found non-macro attribute
}

View File

@ -1,4 +1,4 @@
error: cannot find macro `test!` in this scope
error: expected a macro, found non-macro attribute
--> $DIR/issue-11692-2.rs:12:13
|
LL | concat!(test!());

View File

@ -10,7 +10,7 @@
// compile-pass
#![feature(use_extern_macros, decl_macro)]
#![feature(decl_macro)]
mod type_ns {
pub type A = u8;

View File

@ -8,11 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Make sure that 'custom_attributes' feature does not allow scoped attributes.
#![feature(use_extern_macros, extern_prelude)]
#![feature(custom_attributes)]
mod m {
fn check() {
Vec::clone!(); //~ ERROR failed to resolve. Not a module `Vec`
u8::clone!(); //~ ERROR failed to resolve. Not a module `u8`
}
}
#[foo::bar]
//~^ ERROR scoped attribute `foo::bar` is experimental (see issue #44690) [E0658]
//~^^ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
fn main() {}

View File

@ -0,0 +1,15 @@
error[E0433]: failed to resolve. Not a module `Vec`
--> $DIR/macro-path-prelude-fail-1.rs:15:9
|
LL | Vec::clone!(); //~ ERROR failed to resolve. Not a module `Vec`
| ^^^ Not a module `Vec`
error[E0433]: failed to resolve. Not a module `u8`
--> $DIR/macro-path-prelude-fail-1.rs:16:9
|
LL | u8::clone!(); //~ ERROR failed to resolve. Not a module `u8`
| ^^ Not a module `u8`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0433`.

View File

@ -0,0 +1,19 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(use_extern_macros)]
mod m {
fn check() {
Result::Ok!(); //~ ERROR fail to resolve non-ident macro path
}
}
fn main() {}

View File

@ -0,0 +1,8 @@
error: fail to resolve non-ident macro path
--> $DIR/macro-path-prelude-fail-2.rs:15:9
|
LL | Result::Ok!(); //~ ERROR fail to resolve non-ident macro path
| ^^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,18 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(use_extern_macros)]
#[derive(inline)] //~ ERROR expected a macro, found non-macro attribute
struct S;
fn main() {
inline!(); //~ ERROR expected a macro, found non-macro attribute
}

View File

@ -0,0 +1,14 @@
error: expected a macro, found non-macro attribute
--> $DIR/macro-path-prelude-fail-3.rs:13:10
|
LL | #[derive(inline)] //~ ERROR expected a macro, found non-macro attribute
| ^^^^^^
error: expected a macro, found non-macro attribute
--> $DIR/macro-path-prelude-fail-3.rs:17:5
|
LL | inline!(); //~ ERROR expected a macro, found non-macro attribute
| ^^^^^^
error: aborting due to 2 previous errors

View File

@ -8,8 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
#[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental
let x =
3;
// compile-pass
#![feature(use_extern_macros, extern_prelude)]
mod m {
fn check() {
std::panic!(); // OK
}
}
fn main() {}

View File

@ -0,0 +1,41 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:macro-in-other-crate.rs
#![feature(decl_macro, extern_prelude)]
macro_rules! add_macro_expanded_things_to_macro_prelude {() => {
#[macro_use]
extern crate macro_in_other_crate;
}}
add_macro_expanded_things_to_macro_prelude!();
mod m1 {
fn check() {
inline!(); //~ ERROR `inline` is ambiguous
}
}
mod m2 {
pub mod std {
pub macro panic() {}
}
}
mod m3 {
use m2::*; // glob-import user-defined `std`
fn check() {
std::panic!(); //~ ERROR `std` is ambiguous
}
}
fn main() {}

View File

@ -0,0 +1,42 @@
error[E0659]: `inline` is ambiguous
--> $DIR/macro-path-prelude-shadowing.rs:24:9
|
LL | inline!(); //~ ERROR `inline` is ambiguous
| ^^^^^^
|
note: `inline` could refer to the name imported here
--> $DIR/macro-path-prelude-shadowing.rs:16:5
|
LL | #[macro_use]
| ^^^^^^^^^^^^
...
LL | add_macro_expanded_things_to_macro_prelude!();
| ---------------------------------------------- in this macro invocation
note: `inline` could also refer to the name defined here
--> $DIR/macro-path-prelude-shadowing.rs:24:9
|
LL | inline!(); //~ ERROR `inline` is ambiguous
| ^^^^^^
= note: macro-expanded macro imports do not shadow
error[E0659]: `std` is ambiguous
--> $DIR/macro-path-prelude-shadowing.rs:37:9
|
LL | std::panic!(); //~ ERROR `std` is ambiguous
| ^^^^^^^^^^
|
note: `std` could refer to the name imported here
--> $DIR/macro-path-prelude-shadowing.rs:35:9
|
LL | use m2::*; // glob-import user-defined `std`
| ^^^^^
note: `std` could also refer to the name defined here
--> $DIR/macro-path-prelude-shadowing.rs:37:9
|
LL | std::panic!(); //~ ERROR `std` is ambiguous
| ^^^
= note: consider adding an explicit import of `std` to disambiguate
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0659`.

View File

@ -0,0 +1,15 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// If macro modularization (`use_extern_macros`) is not enabled,
// then tool attributes are treated as custom attributes.
#[rustfmt::bar] //~ ERROR The attribute `rustfmt::bar` is currently unknown to the compiler
fn main() {}

View File

@ -0,0 +1,11 @@
error[E0658]: The attribute `rustfmt::bar` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
--> $DIR/tool-attributes-disabled-1.rs:14:1
|
LL | #[rustfmt::bar] //~ ERROR The attribute `rustfmt::bar` is currently unknown to the compiler
| ^^^^^^^^^^^^^^^
|
= help: add #![feature(custom_attribute)] to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,19 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// If macro modularization (`use_extern_macros`) is not enabled,
// then tool attributes are treated as custom attributes.
// compile-pass
#![feature(custom_attribute)]
#[rustfmt::bar]
fn main() {}

View File

@ -0,0 +1,28 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(tool_attributes)]
type A = rustfmt; //~ ERROR expected type, found tool module `rustfmt`
type B = rustfmt::skip; //~ ERROR expected type, found non-macro attribute `rustfmt::skip`
#[derive(rustfmt)] //~ ERROR cannot find derive macro `rustfmt` in this scope
struct S;
#[rustfmt] //~ ERROR cannot find attribute macro `rustfmt` in this scope
fn check() {}
#[rustfmt::skip] // OK
fn main() {
rustfmt; //~ ERROR expected value, found tool module `rustfmt`
rustfmt!(); //~ ERROR cannot find macro `rustfmt!` in this scope
rustfmt::skip; //~ ERROR expected value, found non-macro attribute `rustfmt::skip`
}

View File

@ -0,0 +1,46 @@
error: cannot find derive macro `rustfmt` in this scope
--> $DIR/tool-attributes-misplaced-1.rs:16:10
|
LL | #[derive(rustfmt)] //~ ERROR cannot find derive macro `rustfmt` in this scope
| ^^^^^^^
error: cannot find attribute macro `rustfmt` in this scope
--> $DIR/tool-attributes-misplaced-1.rs:19:3
|
LL | #[rustfmt] //~ ERROR cannot find attribute macro `rustfmt` in this scope
| ^^^^^^^
error: cannot find macro `rustfmt!` in this scope
--> $DIR/tool-attributes-misplaced-1.rs:25:5
|
LL | rustfmt!(); //~ ERROR cannot find macro `rustfmt!` in this scope
| ^^^^^^^
error[E0573]: expected type, found tool module `rustfmt`
--> $DIR/tool-attributes-misplaced-1.rs:13:10
|
LL | type A = rustfmt; //~ ERROR expected type, found tool module `rustfmt`
| ^^^^^^^ not a type
error[E0573]: expected type, found non-macro attribute `rustfmt::skip`
--> $DIR/tool-attributes-misplaced-1.rs:14:10
|
LL | type B = rustfmt::skip; //~ ERROR expected type, found non-macro attribute `rustfmt::skip`
| ^^^^^^^^^^^^^ not a type
error[E0423]: expected value, found tool module `rustfmt`
--> $DIR/tool-attributes-misplaced-1.rs:24:5
|
LL | rustfmt; //~ ERROR expected value, found tool module `rustfmt`
| ^^^^^^^ not a value
error[E0423]: expected value, found non-macro attribute `rustfmt::skip`
--> $DIR/tool-attributes-misplaced-1.rs:27:5
|
LL | rustfmt::skip; //~ ERROR expected value, found non-macro attribute `rustfmt::skip`
| ^^^^^^^^^^^^^ not a value
error: aborting due to 7 previous errors
Some errors occurred: E0423, E0573.
For more information about an error, try `rustc --explain E0423`.

View File

@ -0,0 +1,18 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(tool_attributes)]
#[derive(rustfmt::skip)] //~ ERROR expected a macro, found non-macro attribute
struct S;
fn main() {
rustfmt::skip!(); //~ ERROR expected a macro, found non-macro attribute
}

View File

@ -0,0 +1,14 @@
error: expected a macro, found non-macro attribute
--> $DIR/tool-attributes-misplaced-2.rs:13:10
|
LL | #[derive(rustfmt::skip)] //~ ERROR expected a macro, found non-macro attribute
| ^^^^^^^^^^^^^
error: expected a macro, found non-macro attribute
--> $DIR/tool-attributes-misplaced-2.rs:17:5
|
LL | rustfmt::skip!(); //~ ERROR expected a macro, found non-macro attribute
| ^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View File

@ -0,0 +1,16 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(tool_attributes, proc_macro_path_invoc)]
mod rustfmt {}
#[rustfmt::skip] //~ ERROR failed to resolve. Could not find `skip` in `rustfmt`
fn main() {}

View File

@ -0,0 +1,9 @@
error[E0433]: failed to resolve. Could not find `skip` in `rustfmt`
--> $DIR/tool-attributes-shadowing.rs:15:12
|
LL | #[rustfmt::skip] //~ ERROR failed to resolve. Could not find `skip` in `rustfmt`
| ^^^^ Could not find `skip` in `rustfmt`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0433`.