Skip to content

Commit e2834a2

Browse files
committed
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. :)
2 parents da4a21e + 04c05c7 commit e2834a2

File tree

2 files changed

+133
-5
lines changed

2 files changed

+133
-5
lines changed

src/librustc_resolve/lib.rs

+73-5
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ pub enum ResolutionError<'a> {
179179
/// error E0424: `self` is not available in a static method
180180
SelfNotAvailableInStaticMethod,
181181
/// error E0425: unresolved name
182-
UnresolvedName(&'a str, &'a str),
182+
UnresolvedName(&'a str, &'a str, UnresolvedNameContext),
183183
/// error E0426: use of undeclared label
184184
UndeclaredLabel(&'a str),
185185
/// error E0427: cannot use `ref` binding mode with ...
@@ -202,6 +202,21 @@ pub enum ResolutionError<'a> {
202202
AttemptToUseNonConstantValueInConstant,
203203
}
204204

205+
/// Context of where `ResolutionError::UnresolvedName` arose.
206+
#[derive(Clone, PartialEq, Eq, Debug)]
207+
pub enum UnresolvedNameContext {
208+
/// `PathIsMod(id)` indicates that a given path, used in
209+
/// expression context, actually resolved to a module rather than
210+
/// a value. The `id` attached to the variant is the node id of
211+
/// the erroneous path expression.
212+
PathIsMod(ast::NodeId),
213+
214+
/// `Other` means we have no extra information about the context
215+
/// of the unresolved name error. (Maybe we could eliminate all
216+
/// such cases; but for now, this is an information-free default.)
217+
Other,
218+
}
219+
205220
fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
206221
span: syntax::codemap::Span,
207222
resolution_error: ResolutionError<'b>) {
@@ -402,13 +417,46 @@ fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
402417
"`self` is not available in a static method. Maybe a `self` argument is \
403418
missing?");
404419
}
405-
ResolutionError::UnresolvedName(path, name) => {
420+
ResolutionError::UnresolvedName(path, msg, context) => {
406421
span_err!(resolver.session,
407422
span,
408423
E0425,
409424
"unresolved name `{}`{}",
410425
path,
411-
name);
426+
msg);
427+
428+
match context {
429+
UnresolvedNameContext::Other => {} // no help available
430+
UnresolvedNameContext::PathIsMod(id) => {
431+
let mut help_msg = String::new();
432+
let parent_id = resolver.ast_map.get_parent_node(id);
433+
if let Some(hir_map::Node::NodeExpr(e)) = resolver.ast_map.find(parent_id) {
434+
match e.node {
435+
ExprField(_, ident) => {
436+
help_msg = format!("To reference an item from the \
437+
`{module}` module, use \
438+
`{module}::{ident}`",
439+
module = &*path,
440+
ident = ident.node);
441+
}
442+
443+
ExprMethodCall(ident, _, _) => {
444+
help_msg = format!("To call a function from the \
445+
`{module}` module, use \
446+
`{module}::{ident}(..)`",
447+
module = &*path,
448+
ident = ident.node);
449+
}
450+
451+
_ => {} // no help available
452+
}
453+
}
454+
455+
if !help_msg.is_empty() {
456+
resolver.session.fileline_help(span, &help_msg);
457+
}
458+
}
459+
}
412460
}
413461
ResolutionError::UndeclaredLabel(name) => {
414462
span_err!(resolver.session,
@@ -3539,13 +3587,33 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
35393587
format!("to call `{}::{}`", path_str, path_name),
35403588
};
35413589

3590+
let mut context = UnresolvedNameContext::Other;
35423591
if !msg.is_empty() {
3543-
msg = format!(". Did you mean {}?", msg)
3592+
msg = format!(". Did you mean {}?", msg);
3593+
} else {
3594+
// we check if this a module and if so, we display a help
3595+
// message
3596+
let name_path = path.segments.iter()
3597+
.map(|seg| seg.identifier.name)
3598+
.collect::<Vec<_>>();
3599+
let current_module = self.current_module.clone();
3600+
3601+
match self.resolve_module_path(current_module,
3602+
&name_path[..],
3603+
UseLexicalScope,
3604+
expr.span,
3605+
PathSearch) {
3606+
Success(_) => {
3607+
context = UnresolvedNameContext::PathIsMod(expr.id);
3608+
},
3609+
_ => {},
3610+
};
35443611
}
35453612

35463613
resolve_error(self,
35473614
expr.span,
3548-
ResolutionError::UnresolvedName(&*path_name, &*msg));
3615+
ResolutionError::UnresolvedName(
3616+
&*path_name, &*msg, context));
35493617
}
35503618
}
35513619
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Beginners write `mod.item` when they should write `mod::item`.
12+
// This tests that we suggest the latter when we encounter the former.
13+
14+
pub mod a {
15+
pub const I: i32 = 1;
16+
17+
pub fn f() -> i32 { 2 }
18+
19+
pub mod b {
20+
pub const J: i32 = 3;
21+
22+
pub fn g() -> i32 { 4 }
23+
}
24+
}
25+
26+
fn h1() -> i32 {
27+
a.I
28+
//~^ ERROR E0425
29+
//~| HELP To reference an item from the `a` module, use `a::I`
30+
}
31+
32+
fn h2() -> i32 {
33+
a.g()
34+
//~^ ERROR E0425
35+
//~| HELP To call a function from the `a` module, use `a::g(..)`
36+
}
37+
38+
fn h3() -> i32 {
39+
a.b.J
40+
//~^ ERROR E0425
41+
//~| HELP To reference an item from the `a` module, use `a::b`
42+
}
43+
44+
fn h4() -> i32 {
45+
a::b.J
46+
//~^ ERROR E0425
47+
//~| HELP To reference an item from the `a::b` module, use `a::b::J`
48+
}
49+
50+
fn h5() -> i32 {
51+
a.b.f()
52+
//~^ ERROR E0425
53+
//~| HELP To reference an item from the `a` module, use `a::b`
54+
}
55+
56+
fn h6() -> i32 {
57+
a::b.f()
58+
//~^ ERROR E0425
59+
//~| HELP To call a function from the `a::b` module, use `a::b::f(..)`
60+
}

0 commit comments

Comments
 (0)