Emit a better diagnostic when function actually has a 'self' parameter

Fixes #66898

When we are unable to resolve a reference to `self`, we current assume
that the containing function doesn't have a `self` parameter, and
emit an error message accordingly.

However, if the reference to `self` was created by a macro invocation,
then resolution will correctly fail, due to hygiene. In this case, we
don't want to tell the user that the containing fuction doesn't have a
'self' paramter if it actually has one.

This PR checks for the precense of a 'self' parameter, and adjusts the
error message we emit accordingly.

TODO: The exact error message we emit could probably be improved. Should
we explicitly mention hygiene?
This commit is contained in:
Aaron Hill 2020-05-17 18:28:12 -04:00
parent d79f1bd31a
commit b6844489f9
No known key found for this signature in database
GPG key ID: B4087E510E98B164
4 changed files with 52 additions and 4 deletions

View file

@ -347,7 +347,7 @@ struct DiagnosticMetadata<'ast> {
currently_processing_generics: bool,
/// The current enclosing function (used for better errors).
current_function: Option<Span>,
current_function: Option<(FnKind<'ast>, Span)>,
/// A list of labels as of yet unused. Labels will be removed from this map when
/// they are used (in a `break` or `continue` statement)
@ -466,7 +466,8 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
FnKind::Fn(FnCtxt::Free | FnCtxt::Foreign, ..) => FnItemRibKind,
FnKind::Fn(FnCtxt::Assoc(_), ..) | FnKind::Closure(..) => NormalRibKind,
};
let previous_value = replace(&mut self.diagnostic_metadata.current_function, Some(sp));
let previous_value =
replace(&mut self.diagnostic_metadata.current_function, Some((fn_kind, sp)));
debug!("(resolving function) entering function");
let declaration = fn_kind.decl();

View file

@ -195,8 +195,15 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
_ => "`self` value is a keyword only available in methods with a `self` parameter"
.to_string(),
});
if let Some(span) = &self.diagnostic_metadata.current_function {
err.span_label(*span, "this function doesn't have a `self` parameter");
if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function {
// The current function has a `self' parameter, but we were unable to resolve
// a reference to `self`. This can only happen if the `self` identifier we
// are resolving came from a different hygiene context.
if fn_kind.decl().inputs.get(0).map(|p| p.is_self()).unwrap_or(false) {
err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
} else {
err.span_label(*span, "this function doesn't have a `self` parameter");
}
}
return (err, Vec::new());
}

View file

@ -0,0 +1,23 @@
// Regression test for issue #66898
// Tests that we don't emit a nonsensical error message
// when a macro invocation tries to access `self` from a function
// that has a 'self' parameter
pub struct Foo;
macro_rules! call_bar {
() => {
self.bar(); //~ ERROR expected value
}
}
impl Foo {
pub fn foo(&self) {
call_bar!();
}
pub fn bar(&self) {
}
}
fn main() {}

View file

@ -0,0 +1,17 @@
error[E0424]: expected value, found module `self`
--> $DIR/missing-self-diag.rs:10:9
|
LL | self.bar();
| ^^^^ `self` value is a keyword only available in methods with a `self` parameter
...
LL | / pub fn foo(&self) {
LL | | call_bar!();
| | ------------ in this macro invocation
LL | | }
| |_____- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
For more information about this error, try `rustc --explain E0424`.