Skip to content

Commit

Permalink
Rollup merge of #83669 - kwj2104:issue-81508-fix, r=varkor
Browse files Browse the repository at this point in the history
Issue 81508 fix

Fix #81508

**Problem**: When variable name is used incorrectly as path, error and warning point to undeclared/unused name, when in fact the name is used, just incorrectly (should be used as a variable, not part of a path).

**Summary for fix**: When path resolution errs, diagnostics checks for variables in ```ValueNS``` that have the same name (e.g., variable rather than path named Foo), and adds additional suggestion that user may actually intend to use the variable name rather than a path.

The fix does not suppress or otherwise change the *warning* that results. I did not find a straightforward way in the code to modify this, but would love to make changes here as well with any guidance.
  • Loading branch information
Dylan-DPC authored Apr 11, 2021
2 parents a866124 + f51f25a commit b6780b3
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 3 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
}

ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => {
debug!("resolve_item ItemKind::Const");
self.with_item_rib(HasGenericParams::No, |this| {
this.visit_ty(ty);
if let Some(expr) = expr {
Expand Down Expand Up @@ -1597,6 +1596,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
.try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub)
.unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
self.r.record_partial_res(pat.id, PartialRes::new(res));
self.r.record_pat_span(pat.id, pat.span);
}
PatKind::TupleStruct(ref path, ref sub_patterns) => {
self.smart_resolve_path(
Expand Down
65 changes: 63 additions & 2 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,10 @@ pub struct Resolver<'a> {
/// "self-confirming" import resolutions during import validation.
unusable_binding: Option<&'a NameBinding<'a>>,

// Spans for local variables found during pattern resolution.
// Used for suggestions during error reporting.
pat_span_map: NodeMap<Span>,

/// Resolutions for nodes that have a single resolution.
partial_res_map: NodeMap<PartialRes>,
/// Resolutions for import nodes, which have multiple resolutions in different namespaces.
Expand Down Expand Up @@ -1270,6 +1274,7 @@ impl<'a> Resolver<'a> {
last_import_segment: false,
unusable_binding: None,

pat_span_map: Default::default(),
partial_res_map: Default::default(),
import_res_map: Default::default(),
label_res_map: Default::default(),
Expand Down Expand Up @@ -1917,7 +1922,6 @@ impl<'a> Resolver<'a> {
return Some(LexicalScopeBinding::Item(binding));
}
}

self.early_resolve_ident_in_lexical_scope(
orig_ident,
ScopeSet::Late(ns, module, record_used_id),
Expand Down Expand Up @@ -2394,7 +2398,59 @@ impl<'a> Resolver<'a> {
.next()
.map_or(false, |c| c.is_ascii_uppercase())
{
(format!("use of undeclared type `{}`", ident), None)
// Check whether the name refers to an item in the value namespace.
let suggestion = if ribs.is_some() {
let match_span = match self.resolve_ident_in_lexical_scope(
ident,
ValueNS,
parent_scope,
None,
path_span,
&ribs.unwrap()[ValueNS],
) {
// Name matches a local variable. For example:
// ```
// fn f() {
// let Foo: &str = "";
// println!("{}", Foo::Bar); // Name refers to local
// // variable `Foo`.
// }
// ```
Some(LexicalScopeBinding::Res(Res::Local(id))) => {
Some(*self.pat_span_map.get(&id).unwrap())
}

// Name matches item from a local name binding
// created by `use` declaration. For example:
// ```
// pub Foo: &str = "";
//
// mod submod {
// use super::Foo;
// println!("{}", Foo::Bar); // Name refers to local
// // binding `Foo`.
// }
// ```
Some(LexicalScopeBinding::Item(name_binding)) => {
Some(name_binding.span)
}
_ => None,
};

if let Some(span) = match_span {
Some((
vec![(span, String::from(""))],
format!("`{}` is defined here, but is not a type", ident),
Applicability::MaybeIncorrect,
))
} else {
None
}
} else {
None
};

(format!("use of undeclared type `{}`", ident), suggestion)
} else {
(format!("use of undeclared crate or module `{}`", ident), None)
}
Expand Down Expand Up @@ -2805,6 +2861,11 @@ impl<'a> Resolver<'a> {
}
}

fn record_pat_span(&mut self, node: NodeId, span: Span) {
debug!("(recording pat) recording {:?} for {:?}", node, span);
self.pat_span_map.insert(node, span);
}

fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool {
vis.is_accessible_from(module.nearest_parent_mod, self)
}
Expand Down
22 changes: 22 additions & 0 deletions src/test/ui/resolve/issue-81508.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Confusing diagnostic when using variable as a type:
//
// Previous warnings indicate Foo is not used, when in fact it is
// used improperly as a variable or constant. New warning points
// out user may be trying to use variable as a type. Test demonstrates
// cases for both local variable and const.

fn main() {
let Baz: &str = "";

println!("{}", Baz::Bar); //~ ERROR: failed to resolve: use of undeclared type `Baz`
}

#[allow(non_upper_case_globals)]
pub const Foo: &str = "";

mod submod {
use super::Foo;
fn function() {
println!("{}", Foo::Bar); //~ ERROR: failed to resolve: use of undeclared type `Foo`
}
}
21 changes: 21 additions & 0 deletions src/test/ui/resolve/issue-81508.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0433]: failed to resolve: use of undeclared type `Baz`
--> $DIR/issue-81508.rs:11:20
|
LL | let Baz: &str = "";
| --- help: `Baz` is defined here, but is not a type
LL |
LL | println!("{}", Baz::Bar);
| ^^^ use of undeclared type `Baz`

error[E0433]: failed to resolve: use of undeclared type `Foo`
--> $DIR/issue-81508.rs:20:24
|
LL | use super::Foo;
| ---------- help: `Foo` is defined here, but is not a type
LL | fn function() {
LL | println!("{}", Foo::Bar);
| ^^^ use of undeclared type `Foo`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0433`.

0 comments on commit b6780b3

Please sign in to comment.