Auto merge of #30413 - pnkfelix:fsk-span_note, r=Manishearth
Add note when item accessed from module via `m.i` rather than `m::i`. (I tried to make this somewhat future-proofed, in that the `UnresolvedNameContext` could be expanded in the future with other cases besides paths that are known to be modules.) This supersedes PR #30356 ; since I'm responsible for a bunch of new code here, someone else should review it. :)
This commit is contained in:
@@ -179,7 +179,7 @@ pub enum ResolutionError<'a> {
|
|||||||
/// error E0424: `self` is not available in a static method
|
/// error E0424: `self` is not available in a static method
|
||||||
SelfNotAvailableInStaticMethod,
|
SelfNotAvailableInStaticMethod,
|
||||||
/// error E0425: unresolved name
|
/// error E0425: unresolved name
|
||||||
UnresolvedName(&'a str, &'a str),
|
UnresolvedName(&'a str, &'a str, UnresolvedNameContext),
|
||||||
/// error E0426: use of undeclared label
|
/// error E0426: use of undeclared label
|
||||||
UndeclaredLabel(&'a str),
|
UndeclaredLabel(&'a str),
|
||||||
/// error E0427: cannot use `ref` binding mode with ...
|
/// error E0427: cannot use `ref` binding mode with ...
|
||||||
@@ -202,6 +202,21 @@ pub enum ResolutionError<'a> {
|
|||||||
AttemptToUseNonConstantValueInConstant,
|
AttemptToUseNonConstantValueInConstant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Context of where `ResolutionError::UnresolvedName` arose.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum UnresolvedNameContext {
|
||||||
|
/// `PathIsMod(id)` indicates that a given path, used in
|
||||||
|
/// expression context, actually resolved to a module rather than
|
||||||
|
/// a value. The `id` attached to the variant is the node id of
|
||||||
|
/// the erroneous path expression.
|
||||||
|
PathIsMod(ast::NodeId),
|
||||||
|
|
||||||
|
/// `Other` means we have no extra information about the context
|
||||||
|
/// of the unresolved name error. (Maybe we could eliminate all
|
||||||
|
/// such cases; but for now, this is an information-free default.)
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
|
fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
|
||||||
span: syntax::codemap::Span,
|
span: syntax::codemap::Span,
|
||||||
resolution_error: ResolutionError<'b>) {
|
resolution_error: ResolutionError<'b>) {
|
||||||
@@ -402,13 +417,46 @@ fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
|
|||||||
"`self` is not available in a static method. Maybe a `self` argument is \
|
"`self` is not available in a static method. Maybe a `self` argument is \
|
||||||
missing?");
|
missing?");
|
||||||
}
|
}
|
||||||
ResolutionError::UnresolvedName(path, name) => {
|
ResolutionError::UnresolvedName(path, msg, context) => {
|
||||||
span_err!(resolver.session,
|
span_err!(resolver.session,
|
||||||
span,
|
span,
|
||||||
E0425,
|
E0425,
|
||||||
"unresolved name `{}`{}",
|
"unresolved name `{}`{}",
|
||||||
path,
|
path,
|
||||||
name);
|
msg);
|
||||||
|
|
||||||
|
match context {
|
||||||
|
UnresolvedNameContext::Other => {} // no help available
|
||||||
|
UnresolvedNameContext::PathIsMod(id) => {
|
||||||
|
let mut help_msg = String::new();
|
||||||
|
let parent_id = resolver.ast_map.get_parent_node(id);
|
||||||
|
if let Some(hir_map::Node::NodeExpr(e)) = resolver.ast_map.find(parent_id) {
|
||||||
|
match e.node {
|
||||||
|
ExprField(_, ident) => {
|
||||||
|
help_msg = format!("To reference an item from the \
|
||||||
|
`{module}` module, use \
|
||||||
|
`{module}::{ident}`",
|
||||||
|
module = &*path,
|
||||||
|
ident = ident.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprMethodCall(ident, _, _) => {
|
||||||
|
help_msg = format!("To call a function from the \
|
||||||
|
`{module}` module, use \
|
||||||
|
`{module}::{ident}(..)`",
|
||||||
|
module = &*path,
|
||||||
|
ident = ident.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {} // no help available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !help_msg.is_empty() {
|
||||||
|
resolver.session.fileline_help(span, &help_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ResolutionError::UndeclaredLabel(name) => {
|
ResolutionError::UndeclaredLabel(name) => {
|
||||||
span_err!(resolver.session,
|
span_err!(resolver.session,
|
||||||
@@ -3539,13 +3587,33 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||||||
format!("to call `{}::{}`", path_str, path_name),
|
format!("to call `{}::{}`", path_str, path_name),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut context = UnresolvedNameContext::Other;
|
||||||
if !msg.is_empty() {
|
if !msg.is_empty() {
|
||||||
msg = format!(". Did you mean {}?", msg)
|
msg = format!(". Did you mean {}?", msg);
|
||||||
|
} else {
|
||||||
|
// we check if this a module and if so, we display a help
|
||||||
|
// message
|
||||||
|
let name_path = path.segments.iter()
|
||||||
|
.map(|seg| seg.identifier.name)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let current_module = self.current_module.clone();
|
||||||
|
|
||||||
|
match self.resolve_module_path(current_module,
|
||||||
|
&name_path[..],
|
||||||
|
UseLexicalScope,
|
||||||
|
expr.span,
|
||||||
|
PathSearch) {
|
||||||
|
Success(_) => {
|
||||||
|
context = UnresolvedNameContext::PathIsMod(expr.id);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_error(self,
|
resolve_error(self,
|
||||||
expr.span,
|
expr.span,
|
||||||
ResolutionError::UnresolvedName(&*path_name, &*msg));
|
ResolutionError::UnresolvedName(
|
||||||
|
&*path_name, &*msg, context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// Beginners write `mod.item` when they should write `mod::item`.
|
||||||
|
// This tests that we suggest the latter when we encounter the former.
|
||||||
|
|
||||||
|
pub mod a {
|
||||||
|
pub const I: i32 = 1;
|
||||||
|
|
||||||
|
pub fn f() -> i32 { 2 }
|
||||||
|
|
||||||
|
pub mod b {
|
||||||
|
pub const J: i32 = 3;
|
||||||
|
|
||||||
|
pub fn g() -> i32 { 4 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h1() -> i32 {
|
||||||
|
a.I
|
||||||
|
//~^ ERROR E0425
|
||||||
|
//~| HELP To reference an item from the `a` module, use `a::I`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h2() -> i32 {
|
||||||
|
a.g()
|
||||||
|
//~^ ERROR E0425
|
||||||
|
//~| HELP To call a function from the `a` module, use `a::g(..)`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h3() -> i32 {
|
||||||
|
a.b.J
|
||||||
|
//~^ ERROR E0425
|
||||||
|
//~| HELP To reference an item from the `a` module, use `a::b`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h4() -> i32 {
|
||||||
|
a::b.J
|
||||||
|
//~^ ERROR E0425
|
||||||
|
//~| HELP To reference an item from the `a::b` module, use `a::b::J`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h5() -> i32 {
|
||||||
|
a.b.f()
|
||||||
|
//~^ ERROR E0425
|
||||||
|
//~| HELP To reference an item from the `a` module, use `a::b`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h6() -> i32 {
|
||||||
|
a::b.f()
|
||||||
|
//~^ ERROR E0425
|
||||||
|
//~| HELP To call a function from the `a::b` module, use `a::b::f(..)`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user