From 99925fb562086ff789df95220104f9d8d5fc8b3c Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 21 Nov 2015 15:07:50 +0530 Subject: [PATCH] Look up macro names as well when suggesting replacements for function resolve errors fixes #5780 --- src/librustc/session/mod.rs | 8 ++++- src/librustc_driver/driver.rs | 13 ++++---- src/librustc_resolve/lib.rs | 26 +++++++++++++--- src/libsyntax/ext/base.rs | 12 ++++++-- src/libsyntax/ext/expand.rs | 34 ++++++++++++--------- src/test/compile-fail/resolve-hint-macro.rs | 13 ++++++++ 6 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 src/test/compile-fail/resolve-hint-macro.rs diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 29ac2d3abb5..cab61b96b07 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -15,7 +15,7 @@ use middle::dependency_format; use session::search_paths::PathKind; use util::nodemap::{NodeMap, FnvHashMap}; -use syntax::ast::{NodeId, NodeIdAssigner}; +use syntax::ast::{NodeId, NodeIdAssigner, Name}; use syntax::codemap::Span; use syntax::diagnostic::{self, Emitter}; use syntax::diagnostics; @@ -30,6 +30,7 @@ use rustc_back::target::Target; use std::path::{Path, PathBuf}; use std::cell::{Cell, RefCell}; +use std::collections::HashSet; use std::env; pub mod config; @@ -74,6 +75,10 @@ pub struct Session { /// didn't already find one, and this tracks what was injected. pub injected_allocator: Cell>, + /// Names of all bang-style macros and syntax extensions + /// available in this crate + pub available_macros: RefCell>, + next_node_id: Cell, } @@ -468,6 +473,7 @@ pub fn build_session_(sopts: config::Options, can_print_warnings: can_print_warnings, next_node_id: Cell::new(1), injected_allocator: Cell::new(None), + available_macros: RefCell::new(HashSet::new()), }; sess diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index cb345ac517e..a1bec7e78a3 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -553,15 +553,16 @@ pub fn phase_2_configure_and_expand(sess: &Session, recursion_limit: sess.recursion_limit.get(), trace_mac: sess.opts.debugging_opts.trace_macros, }; - let ret = syntax::ext::expand::expand_crate(&sess.parse_sess, - cfg, - macros, - syntax_exts, - &mut feature_gated_cfgs, - krate); + let (ret, macro_names) = syntax::ext::expand::expand_crate(&sess.parse_sess, + cfg, + macros, + syntax_exts, + &mut feature_gated_cfgs, + krate); if cfg!(windows) { env::set_var("PATH", &_old_path); } + *sess.available_macros.borrow_mut() = macro_names; ret }); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 8776ee2d831..5b68142746c 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -124,6 +124,12 @@ macro_rules! execute_callback { ) } +enum SuggestionType { + Macro(String), + Function(String), + NotFound, +} + pub enum ResolutionError<'a> { /// error E0401: can't use type parameters from outer function TypeParametersFromOuterFunction, @@ -3616,10 +3622,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { NoSuggestion } - fn find_best_match_for_name(&mut self, name: &str) -> Option { + fn find_best_match_for_name(&mut self, name: &str) -> SuggestionType { let mut maybes: Vec = Vec::new(); let mut values: Vec = Vec::new(); + if let Some(macro_name) = self.session.available_macros + .borrow().iter().find(|n| n.as_str() == name) { + return SuggestionType::Macro(format!("{}!", macro_name)); + } + for rib in self.value_ribs.iter().rev() { for (&k, _) in &rib.bindings { maybes.push(k.as_str()); @@ -3643,10 +3654,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if !values.is_empty() && values[smallest] <= max_distance && name != &maybes[smallest][..] { - Some(maybes[smallest].to_string()) + SuggestionType::Function(maybes[smallest].to_string()) } else { - None + SuggestionType::NotFound } } @@ -3758,8 +3769,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { NoSuggestion => { // limit search to 5 to reduce the number // of stupid suggestions - self.find_best_match_for_name(&path_name) - .map_or("".to_string(), |x| format!("`{}`", x)) + match self.find_best_match_for_name(&path_name) { + SuggestionType::Macro(s) => { + format!("the macro `{}`", s) + } + SuggestionType::Function(s) => format!("`{}`", s), + SuggestionType::NotFound => "".to_string(), + } } Field => format!("`self.{}`", path_name), Method | diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 18db028b60b..0dba15760cd 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -27,7 +27,7 @@ use util::small_vector::SmallVector; use ext::mtwt; use fold::Folder; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; use std::default::Default; @@ -856,7 +856,10 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt, /// /// This environment maps Names to SyntaxExtensions. pub struct SyntaxEnv { - chain: Vec , + chain: Vec, + /// All bang-style macro/extension names + /// encountered so far; to be used for diagnostics in resolve + pub names: HashSet, } // impl question: how to implement it? Initially, the @@ -876,7 +879,7 @@ struct MapChainFrame { impl SyntaxEnv { fn new() -> SyntaxEnv { - let mut map = SyntaxEnv { chain: Vec::new() }; + let mut map = SyntaxEnv { chain: Vec::new() , names: HashSet::new()}; map.push_frame(); map } @@ -913,6 +916,9 @@ impl SyntaxEnv { } pub fn insert(&mut self, k: Name, v: SyntaxExtension) { + if let NormalTT(..) = v { + self.names.insert(k); + } self.find_escape_frame().map.insert(k, Rc::new(v)); } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index a0e4fd301a2..9b1a7a50201 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -9,7 +9,7 @@ // except according to those terms. use ast::{Block, Crate, DeclLocal, ExprMac, PatMac}; -use ast::{Local, Ident, Mac_}; +use ast::{Local, Ident, Mac_, Name}; use ast::{ItemMac, MacStmtWithSemicolon, Mrk, Stmt, StmtDecl, StmtMac}; use ast::{StmtExpr, StmtSemi}; use ast::TokenTree; @@ -32,6 +32,8 @@ use visit; use visit::Visitor; use std_inject; +use std::collections::HashSet; + pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let expr_span = e.span; @@ -1261,7 +1263,7 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess, imported_macros: Vec, user_exts: Vec, feature_gated_cfgs: &mut Vec, - c: Crate) -> Crate { + c: Crate) -> (Crate, HashSet) { let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg, feature_gated_cfgs); if std_inject::no_core(&c) { @@ -1271,21 +1273,23 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess, } else { cx.crate_root = Some("std"); } + let ret = { + let mut expander = MacroExpander::new(&mut cx); - let mut expander = MacroExpander::new(&mut cx); + for def in imported_macros { + expander.cx.insert_macro(def); + } - for def in imported_macros { - expander.cx.insert_macro(def); - } + for (name, extension) in user_exts { + expander.cx.syntax_env.insert(name, extension); + } - for (name, extension) in user_exts { - expander.cx.syntax_env.insert(name, extension); - } - - let mut ret = expander.fold_crate(c); - ret.exported_macros = expander.cx.exported_macros.clone(); - parse_sess.span_diagnostic.handler().abort_if_errors(); - return ret; + let mut ret = expander.fold_crate(c); + ret.exported_macros = expander.cx.exported_macros.clone(); + parse_sess.span_diagnostic.handler().abort_if_errors(); + ret + }; + return (ret, cx.syntax_env.names); } // HYGIENIC CONTEXT EXTENSION: @@ -1480,7 +1484,7 @@ mod tests { let ps = parse::ParseSess::new(); let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod()); // the cfg argument actually does matter, here... - expand_crate(&ps,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast) + expand_crate(&ps,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast).0 } // find the pat_ident paths in a crate diff --git a/src/test/compile-fail/resolve-hint-macro.rs b/src/test/compile-fail/resolve-hint-macro.rs new file mode 100644 index 00000000000..f05f1cd544a --- /dev/null +++ b/src/test/compile-fail/resolve-hint-macro.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + assert(true); //~ERROR unresolved name `assert`. Did you mean the macro `assert!`? +}