提交 329c0525 编写于 作者: V Vadim Petrochenkov

resolve: Visit all scopes to collect suggestion candidates for unresolved macros

上级 79f0d88d
......@@ -398,4 +398,12 @@ pub fn map_id<R>(self, mut map: impl FnMut(Id) -> R) -> Res<R> {
Res::Err => Res::Err,
}
}
pub fn macro_kind(self) -> Option<MacroKind> {
match self {
Res::Def(DefKind::Macro(kind), _) => Some(kind),
Res::NonMacroAttr(..) => Some(MacroKind::Attr),
_ => None,
}
}
}
......@@ -2,7 +2,7 @@
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
use log::debug;
use rustc::hir::def::{self, DefKind, CtorKind};
use rustc::hir::def::{self, DefKind, CtorKind, NonMacroAttrKind};
use rustc::hir::def::Namespace::{self, *};
use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
use rustc::hir::PrimTy;
......@@ -11,14 +11,15 @@
use rustc::util::nodemap::FxHashSet;
use syntax::ast::{self, Expr, ExprKind, Ident, NodeId, Path, Ty, TyKind};
use syntax::ext::base::MacroKind;
use syntax::feature_gate::BUILTIN_ATTRIBUTES;
use syntax::symbol::{Symbol, kw};
use syntax::util::lev_distance::find_best_match_for_name;
use syntax_pos::{BytePos, Span};
use crate::resolve_imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
use crate::{is_self_type, is_self_value, path_names_to_string};
use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, ParentScope, Resolver, RibKind, Segment};
use crate::{is_self_type, is_self_value, path_names_to_string, KNOWN_TOOLS};
use crate::{CrateLint, LegacyScope, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, ParentScope, Resolver, RibKind, Scope, ScopeSet, Segment};
type Res = def::Res<ast::NodeId>;
......@@ -42,12 +43,44 @@ struct TypoSuggestion {
article: &'static str,
}
impl TypoSuggestion {
fn from_res(candidate: Symbol, res: Res) -> TypoSuggestion {
TypoSuggestion { candidate, kind: res.descr(), article: res.article() }
}
}
/// A free importable items suggested in case of resolution failure.
crate struct ImportSuggestion {
did: Option<DefId>,
pub path: Path,
}
fn add_typo_suggestion(
err: &mut DiagnosticBuilder<'_>, suggestion: Option<TypoSuggestion>, span: Span
) -> bool {
if let Some(suggestion) = suggestion {
let msg = format!("{} {} with a similar name exists", suggestion.article, suggestion.kind);
err.span_suggestion(
span, &msg, suggestion.candidate.to_string(), Applicability::MaybeIncorrect
);
return true;
}
false
}
fn add_module_candidates(
module: Module<'_>, names: &mut Vec<TypoSuggestion>, filter_fn: &impl Fn(Res) -> bool
) {
for (&(ident, _), resolution) in module.resolutions.borrow().iter() {
if let Some(binding) = resolution.borrow().binding {
let res = binding.res();
if filter_fn(res) {
names.push(TypoSuggestion::from_res(ident.name, res));
}
}
}
}
impl<'a> Resolver<'a> {
/// Handles error reporting for `smart_resolve_path_fragment` function.
/// Creates base error and amends it with one short label and possibly some longer helps/notes.
......@@ -234,24 +267,10 @@ pub(crate) fn smart_resolve_report_errors(
}
}
let mut levenshtein_worked = false;
// Try Levenshtein algorithm.
let suggestion = self.lookup_typo_candidate(path, ns, is_expected, span);
if let Some(suggestion) = suggestion {
let msg = format!(
"{} {} with a similar name exists",
suggestion.article, suggestion.kind
);
err.span_suggestion(
ident_span,
&msg,
suggestion.candidate.to_string(),
Applicability::MaybeIncorrect,
);
levenshtein_worked = true;
}
let levenshtein_worked = add_typo_suggestion(
&mut err, self.lookup_typo_candidate(path, ns, is_expected, span), ident_span
);
// Try context-dependent help if relaxed lookup didn't work.
if let Some(res) = res {
......@@ -538,30 +557,169 @@ fn extract_node_id(t: &Ty) -> Option<NodeId> {
None
}
fn lookup_typo_candidate<FilterFn>(
/// Lookup typo candidate in scope for a macro or import.
fn early_lookup_typo_candidate(
&mut self,
path: &[Segment],
ns: Namespace,
filter_fn: FilterFn,
span: Span,
) -> Option<TypoSuggestion>
where
FilterFn: Fn(Res) -> bool,
{
let add_module_candidates = |module: Module<'_>, names: &mut Vec<TypoSuggestion>| {
for (&(ident, _), resolution) in module.resolutions.borrow().iter() {
if let Some(binding) = resolution.borrow().binding {
if filter_fn(binding.res()) {
names.push(TypoSuggestion {
candidate: ident.name,
article: binding.res().article(),
kind: binding.res().descr(),
});
scope_set: ScopeSet,
parent_scope: &ParentScope<'a>,
orig_ident: Ident,
filter_fn: &impl Fn(Res) -> bool,
) -> Option<TypoSuggestion> {
let ident = orig_ident.modern();
let rust_2015 = orig_ident.span.rust_2015();
let is_absolute_path = match scope_set {
ScopeSet::AbsolutePath(..) => true,
_ => false,
};
let mut suggestions = Vec::new();
let mut use_prelude = !parent_scope.module.no_implicit_prelude;
self.visit_scopes(scope_set, parent_scope, ident, |this, scope, _| {
match scope {
Scope::DeriveHelpers => {
let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
if filter_fn(res) {
for derive in &parent_scope.derives {
let parent_scope = ParentScope { derives: Vec::new(), ..*parent_scope };
if let Ok((Some(ext), _)) = this.resolve_macro_path(
derive, MacroKind::Derive, &parent_scope, true, true
) {
suggestions.extend(ext.helper_attrs.iter().map(|name| {
TypoSuggestion::from_res(*name, res)
}));
}
}
}
}
Scope::MacroRules(legacy_scope) => {
if let LegacyScope::Binding(legacy_binding) = legacy_scope {
let res = legacy_binding.binding.res();
if filter_fn(res) {
suggestions.push(
TypoSuggestion::from_res(legacy_binding.ident.name, res)
)
}
}
}
Scope::CrateRoot => {
let root_ident = Ident::new(kw::PathRoot, orig_ident.span);
let root_module = this.resolve_crate_root(root_ident);
add_module_candidates(root_module, &mut suggestions, filter_fn);
}
Scope::Module(module) => {
use_prelude = !module.no_implicit_prelude;
add_module_candidates(module, &mut suggestions, filter_fn);
}
Scope::MacroUsePrelude => {
if use_prelude || rust_2015 {
let macro_use_prelude = &this.macro_use_prelude;
suggestions.extend(macro_use_prelude.iter().filter_map(|(name, binding)| {
let res = binding.res();
if filter_fn(res) {
Some(TypoSuggestion::from_res(*name, res))
} else {
None
}
}));
}
}
Scope::BuiltinMacros => {
suggestions.extend(this.builtin_macros.iter().filter_map(|(name, binding)| {
let res = binding.res();
if filter_fn(res) {
Some(TypoSuggestion::from_res(*name, res))
} else {
None
}
}));
}
Scope::BuiltinAttrs => {
let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin);
if filter_fn(res) {
suggestions.extend(BUILTIN_ATTRIBUTES.iter().map(|(name, ..)| {
TypoSuggestion::from_res(*name, res)
}));
}
}
Scope::LegacyPluginHelpers => {
if use_prelude || rust_2015 {
let res = Res::NonMacroAttr(NonMacroAttrKind::LegacyPluginHelper);
if filter_fn(res) {
let plugin_attributes = this.session.plugin_attributes.borrow();
suggestions.extend(plugin_attributes.iter().map(|(name, _)| {
TypoSuggestion::from_res(*name, res)
}));
}
}
}
Scope::ExternPrelude => {
if use_prelude || is_absolute_path {
suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| {
let res = Res::Def(DefKind::Mod, DefId::local(CRATE_DEF_INDEX));
if filter_fn(res) {
Some(TypoSuggestion::from_res(ident.name, res))
} else {
None
}
}));
}
}
Scope::ToolPrelude => {
if use_prelude {
let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);
suggestions.extend(KNOWN_TOOLS.iter().map(|name| {
TypoSuggestion::from_res(*name, res)
}));
}
}
Scope::StdLibPrelude => {
if use_prelude {
if let Some(prelude) = this.prelude {
add_module_candidates(prelude, &mut suggestions, filter_fn);
}
}
}
Scope::BuiltinTypes => {
let primitive_types = &this.primitive_type_table.primitive_types;
suggestions.extend(
primitive_types.iter().flat_map(|(name, prim_ty)| {
let res = Res::PrimTy(*prim_ty);
if filter_fn(res) {
Some(TypoSuggestion::from_res(*name, res))
} else {
None
}
})
)
}
}
};
None::<()>
});
// Make sure error reporting is deterministic.
suggestions.sort_by_cached_key(|suggestion| suggestion.candidate.as_str());
match find_best_match_for_name(
suggestions.iter().map(|suggestion| &suggestion.candidate),
&ident.as_str(),
None,
) {
Some(found) if found != ident.name => suggestions
.into_iter()
.find(|suggestion| suggestion.candidate == found),
_ => None,
}
}
fn lookup_typo_candidate(
&mut self,
path: &[Segment],
ns: Namespace,
filter_fn: &impl Fn(Res) -> bool,
span: Span,
) -> Option<TypoSuggestion> {
let mut names = Vec::new();
if path.len() == 1 {
// Search in lexical scope.
......@@ -570,17 +728,13 @@ fn lookup_typo_candidate<FilterFn>(
// Locals and type parameters
for (ident, &res) in &rib.bindings {
if filter_fn(res) {
names.push(TypoSuggestion {
candidate: ident.name,
article: res.article(),
kind: res.descr(),
});
names.push(TypoSuggestion::from_res(ident.name, res));
}
}
// Items in scope
if let RibKind::ModuleRibKind(module) = rib.kind {
// Items from this module
add_module_candidates(module, &mut names);
add_module_candidates(module, &mut names, &filter_fn);
if let ModuleKind::Block(..) = module.kind {
// We can see through blocks
......@@ -612,7 +766,7 @@ fn lookup_typo_candidate<FilterFn>(
}));
if let Some(prelude) = self.prelude {
add_module_candidates(prelude, &mut names);
add_module_candidates(prelude, &mut names, &filter_fn);
}
}
break;
......@@ -622,12 +776,8 @@ fn lookup_typo_candidate<FilterFn>(
// Add primitive types to the mix
if filter_fn(Res::PrimTy(PrimTy::Bool)) {
names.extend(
self.primitive_type_table.primitive_types.iter().map(|(name, _)| {
TypoSuggestion {
candidate: *name,
article: "a",
kind: "primitive type",
}
self.primitive_type_table.primitive_types.iter().map(|(name, prim_ty)| {
TypoSuggestion::from_res(*name, Res::PrimTy(*prim_ty))
})
)
}
......@@ -638,7 +788,7 @@ fn lookup_typo_candidate<FilterFn>(
mod_path, Some(TypeNS), false, span, CrateLint::No
) {
if let ModuleOrUniformRoot::Module(module) = module {
add_module_candidates(module, &mut names);
add_module_candidates(module, &mut names, &filter_fn);
}
}
}
......@@ -844,62 +994,26 @@ fn collect_enum_variants(&mut self, def_id: DefId) -> Option<Vec<Path>> {
})
}
crate fn suggest_macro_name(
&mut self, name: Symbol, kind: MacroKind, err: &mut DiagnosticBuilder<'a>, span: Span
crate fn unresolved_macro_suggestions(
&mut self,
err: &mut DiagnosticBuilder<'a>,
macro_kind: MacroKind,
parent_scope: &ParentScope<'a>,
ident: Ident,
) {
if kind == MacroKind::Derive && (name.as_str() == "Send" || name.as_str() == "Sync") {
let msg = format!("unsafe traits like `{}` should be implemented explicitly", name);
err.span_note(span, &msg);
return;
}
// First check if this is a locally-defined bang macro.
let suggestion = if let MacroKind::Bang = kind {
find_best_match_for_name(
self.macro_names.iter().map(|ident| &ident.name), &name.as_str(), None)
} else {
None
// Then check global macros.
}.or_else(|| {
let names = self.builtin_macros.iter().chain(self.macro_use_prelude.iter())
.filter_map(|(name, binding)| {
if binding.macro_kind() == Some(kind) { Some(name) } else { None }
});
find_best_match_for_name(names, &name.as_str(), None)
// Then check modules.
}).or_else(|| {
let is_macro = |res| {
if let Res::Def(DefKind::Macro(def_kind), _) = res {
def_kind == kind
} else {
false
}
};
let ident = Ident::new(name, span);
self.lookup_typo_candidate(&[Segment::from_ident(ident)], MacroNS, is_macro, span)
.map(|suggestion| suggestion.candidate)
});
let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind);
let suggestion = self.early_lookup_typo_candidate(
ScopeSet::Macro(macro_kind), &parent_scope, ident, is_expected
);
add_typo_suggestion(err, suggestion, ident.span);
if let Some(suggestion) = suggestion {
if suggestion != name {
if let MacroKind::Bang = kind {
err.span_suggestion(
span,
"you could try the macro",
suggestion.to_string(),
Applicability::MaybeIncorrect
);
} else {
err.span_suggestion(
span,
"try",
suggestion.to_string(),
Applicability::MaybeIncorrect
);
}
} else {
err.help("have you added the `#[macro_use]` on the module/import?");
}
if macro_kind == MacroKind::Derive &&
(ident.as_str() == "Send" || ident.as_str() == "Sync") {
let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident);
err.span_note(ident.span, &msg);
}
if self.macro_names.contains(&ident.modern()) {
err.help("have you added the `#[macro_use]` on the module/import?");
}
}
}
......
......@@ -85,9 +85,7 @@
mod build_reduced_graph;
mod resolve_imports;
fn is_known_tool(name: Name) -> bool {
["clippy", "rustfmt"].contains(&&*name.as_str())
}
const KNOWN_TOOLS: &[Name] = &[sym::clippy, sym::rustfmt];
enum Weak {
Yes,
......@@ -1498,11 +1496,7 @@ fn is_macro_def(&self) -> bool {
}
fn macro_kind(&self) -> Option<MacroKind> {
match self.res() {
Res::Def(DefKind::Macro(kind), _) => Some(kind),
Res::NonMacroAttr(..) => Some(MacroKind::Attr),
_ => None,
}
self.res().macro_kind()
}
fn descr(&self) -> &'static str {
......@@ -2390,7 +2384,7 @@ fn resolve_ident_in_lexical_scope(&mut self,
return Some(LexicalScopeBinding::Item(binding));
}
}
if ns == TypeNS && is_known_tool(ident.name) {
if ns == TypeNS && KNOWN_TOOLS.contains(&ident.name) {
let binding = (Res::ToolMod, ty::Visibility::Public,
DUMMY_SP, Mark::root()).to_name_binding(self.arenas);
return Some(LexicalScopeBinding::Item(binding));
......
use crate::{AmbiguityError, AmbiguityKind, AmbiguityErrorMisc, Determinacy};
use crate::{CrateLint, Resolver, ResolutionError, Scope, ScopeSet, ParentScope, Weak};
use crate::{Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
use crate::{is_known_tool, resolve_error};
use crate::{resolve_error, KNOWN_TOOLS};
use crate::ModuleOrUniformRoot;
use crate::Namespace::*;
use crate::build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport};
......@@ -57,10 +57,10 @@ pub fn root(graph_root: Module<'a>) -> Self {
/// Not modularized, can shadow previous legacy bindings, etc.
#[derive(Debug)]
pub struct LegacyBinding<'a> {
binding: &'a NameBinding<'a>,
crate binding: &'a NameBinding<'a>,
/// Legacy scope into which the `macro_rules` item was planted.
crate parent_legacy_scope: LegacyScope<'a>,
ident: Ident,
crate ident: Ident,
}
/// The scope introduced by a `macro_rules!` macro.
......@@ -582,7 +582,7 @@ struct Flags: u8 {
}
}
Scope::ToolPrelude => {
if use_prelude && is_known_tool(ident.name) {
if use_prelude && KNOWN_TOOLS.contains(&ident.name) {
let binding = (Res::ToolMod, ty::Visibility::Public,
DUMMY_SP, Mark::root()).to_name_binding(this.arenas);
Ok((binding, Flags::PRELUDE))
......@@ -805,7 +805,7 @@ pub fn finalize_current_module_macro_resolutions(&mut self) {
let msg =
format!("cannot find {} `{}{}` in this scope", kind.descr(), ident, bang);
let mut err = self.session.struct_span_err(ident.span, &msg);
self.suggest_macro_name(ident.name, kind, &mut err, ident.span);
self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident);
err.emit();
}
}
......
......@@ -603,6 +603,7 @@
rustc_then_this_would_need,
rustc_variance,
rustdoc,
rustfmt,
rust_eh_personality,
rust_eh_unwind_resume,
rust_oom,
......
......@@ -2,7 +2,7 @@ error: cannot find derive macro `Eqr` in this scope
--> $DIR/deriving-meta-unknown-trait.rs:1:10
|
LL | #[derive(Eqr)]
| ^^^ help: try: `Eq`
| ^^^ help: a derive macro with a similar name exists: `Eq`
error: aborting due to previous error
......@@ -3,8 +3,6 @@ error: cannot find macro `print!` in this scope
|
LL | print!();
| ^^^^^
|
= help: have you added the `#[macro_use]` on the module/import?
error: aborting due to previous error
......@@ -13,7 +13,6 @@ error: cannot find macro `panic!` in this scope
LL | assert_eq!(0, 0);
| ^^^^^^^^^^^^^^^^^
|
= help: have you added the `#[macro_use]` on the module/import?
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0599]: no method named `clone` found for type `()` in the current scope
......
......@@ -2,7 +2,7 @@ error: cannot find attribute macro `marco_use` in this scope
--> $DIR/issue-49074.rs:3:3
|
LL | #[marco_use] // typo
| ^^^^^^^^^
| ^^^^^^^^^ help: a built-in attribute with a similar name exists: `macro_use`
error: cannot find macro `bar!` in this scope
--> $DIR/issue-49074.rs:12:4
......
......@@ -2,7 +2,7 @@ error: cannot find macro `printlx!` in this scope
--> $DIR/macro-name-typo.rs:2:5
|
LL | printlx!("oh noes!");
| ^^^^^^^ help: you could try the macro: `println`
| ^^^^^^^ help: a macro with a similar name exists: `println`
error: aborting due to previous error
......@@ -2,7 +2,7 @@ error: cannot find macro `inline!` in this scope
--> $DIR/macro-path-prelude-fail-3.rs:2:5
|
LL | inline!();
| ^^^^^^ help: you could try the macro: `line`
| ^^^^^^ help: a macro with a similar name exists: `line`
error: aborting due to previous error
......@@ -14,7 +14,7 @@ error: cannot find attribute macro `macro_reexport` in this scope
--> $DIR/macro-reexport-removed.rs:5:3
|
LL | #[macro_reexport(macro_one)]
| ^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^ help: a built-in attribute with a similar name exists: `macro_export`
error: aborting due to 2 previous errors
......
......@@ -2,7 +2,7 @@ error: cannot find macro `macro_two!` in this scope
--> $DIR/macro-use-wrong-name.rs:7:5
|
LL | macro_two!();
| ^^^^^^^^^ help: you could try the macro: `macro_one`
| ^^^^^^^^^ help: a macro with a similar name exists: `macro_one`
error: aborting due to previous error
......@@ -2,7 +2,7 @@ error: cannot find macro `k!` in this scope
--> $DIR/macro_undefined.rs:11:5
|
LL | k!();
| ^ help: you could try the macro: `kl`
| ^ help: a macro with a similar name exists: `kl`
error: aborting due to previous error
......@@ -2,7 +2,7 @@ error: cannot find attribute macro `C` in this scope
--> $DIR/proc-macro-attributes.rs:7:3
|
LL | #[C]
| ^
| ^ help: a derive helper attribute with a similar name exists: `B`
error[E0659]: `B` is ambiguous (derive helper attribute vs any other name)
--> $DIR/proc-macro-attributes.rs:6:3
......
......@@ -2,13 +2,13 @@ error: cannot find derive macro `FooWithLongNan` in this scope
--> $DIR/resolve-error.rs:22:10
|
LL | #[derive(FooWithLongNan)]
| ^^^^^^^^^^^^^^ help: try: `FooWithLongName`
| ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName`
error: cannot find attribute macro `attr_proc_macra` in this scope
--> $DIR/resolve-error.rs:27:3
|
LL | #[attr_proc_macra]
| ^^^^^^^^^^^^^^^ help: try: `attr_proc_macro`
| ^^^^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `attr_proc_macro`
error: cannot find attribute macro `FooWithLongNan` in this scope
--> $DIR/resolve-error.rs:31:3
......@@ -20,13 +20,13 @@ error: cannot find derive macro `Dlone` in this scope
--> $DIR/resolve-error.rs:34:10
|
LL | #[derive(Dlone)]
| ^^^^^ help: try: `Clone`
| ^^^^^ help: a derive macro with a similar name exists: `Clone`
error: cannot find derive macro `Dlona` in this scope
--> $DIR/resolve-error.rs:38:10
|
LL | #[derive(Dlona)]
| ^^^^^ help: try: `Clona`
| ^^^^^ help: a derive macro with a similar name exists: `Clona`
error: cannot find derive macro `attr_proc_macra` in this scope
--> $DIR/resolve-error.rs:42:10
......@@ -38,13 +38,13 @@ error: cannot find macro `FooWithLongNama!` in this scope
--> $DIR/resolve-error.rs:47:5
|
LL | FooWithLongNama!();
| ^^^^^^^^^^^^^^^ help: you could try the macro: `FooWithLongNam`
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `FooWithLongNam`
error: cannot find macro `attr_proc_macra!` in this scope
--> $DIR/resolve-error.rs:50:5
|
LL | attr_proc_macra!();
| ^^^^^^^^^^^^^^^ help: you could try the macro: `attr_proc_mac`
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `attr_proc_mac`
error: cannot find macro `Dlona!` in this scope
--> $DIR/resolve-error.rs:53:5
......@@ -56,7 +56,7 @@ error: cannot find macro `bang_proc_macrp!` in this scope
--> $DIR/resolve-error.rs:56:5
|
LL | bang_proc_macrp!();
| ^^^^^^^^^^^^^^^ help: you could try the macro: `bang_proc_macro`
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `bang_proc_macro`
error: aborting due to 10 previous errors
......@@ -2,7 +2,7 @@ error[E0412]: cannot find type `esize` in this scope
--> $DIR/levenshtein.rs:5:11
|
LL | fn foo(c: esize) {} // Misspelled primitive type name.
| ^^^^^ help: a primitive type with a similar name exists: `isize`
| ^^^^^ help: a builtin type with a similar name exists: `isize`
error[E0412]: cannot find type `Baz` in this scope
--> $DIR/levenshtein.rs:10:10
......
......@@ -11,19 +11,19 @@ error: cannot find attribute macro `rustc_err` in this scope
--> $DIR/attribute-typos.rs:7:3
|
LL | #[rustc_err]
| ^^^^^^^^^
| ^^^^^^^^^ help: a built-in attribute with a similar name exists: `rustc_error`
error: cannot find attribute macro `tests` in this scope
--> $DIR/attribute-typos.rs:4:3
|
LL | #[tests]
| ^^^^^ help: try: `test`
| ^^^^^ help: an attribute macro with a similar name exists: `test`
error: cannot find attribute macro `deprcated` in this scope
--> $DIR/attribute-typos.rs:1:3
|
LL | #[deprcated]
| ^^^^^^^^^
| ^^^^^^^^^ help: a built-in attribute with a similar name exists: `deprecated`
error: aborting due to 4 previous errors
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册