Factored out context-dependent help for error reporting.

This commit is contained in:
Alexander Regueiro 2019-02-02 02:18:32 +00:00
parent 0e2d96e88c
commit 14b674ab9d

View File

@ -3124,6 +3124,193 @@ impl<'a> Resolver<'a> {
)
}
/// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
/// function.
/// Returns `true` if able to provide context-dependent help.
fn smart_resolve_context_dep_help(
&mut self,
err: &mut DiagnosticBuilder<'a>,
span: Span,
source: PathSource,
def: Def,
path_str: &str,
fallback_label: &str,
) -> bool {
let ns = source.namespace();
let is_expected = &|def| source.is_expected(def);
match (def, source) {
(Def::Macro(..), _) => {
err.span_suggestion(
span,
"use `!` to invoke the macro",
format!("{}!", path_str),
Applicability::MaybeIncorrect,
);
}
(Def::TyAlias(..), PathSource::Trait(_)) => {
err.span_label(span, "type aliases cannot be used as traits");
if nightly_options::is_nightly_build() {
err.note("did you mean to use a trait alias?");
}
}
(Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
ExprKind::Field(_, ident) => {
err.span_suggestion(
parent.span,
"use the path separator to refer to an item",
format!("{}::{}", path_str, ident),
Applicability::MaybeIncorrect,
);
}
ExprKind::MethodCall(ref segment, ..) => {
let span = parent.span.with_hi(segment.ident.span.hi());
err.span_suggestion(
span,
"use the path separator to refer to an item",
format!("{}::{}", path_str, segment.ident),
Applicability::MaybeIncorrect,
);
}
_ => return false,
},
(Def::Enum(..), PathSource::TupleStruct)
| (Def::Enum(..), PathSource::Expr(..)) => {
if let Some(variants) = self.collect_enum_variants(def) {
err.note(&format!("did you mean to use one \
of the following variants?\n{}",
variants.iter()
.map(|suggestion| path_names_to_string(suggestion))
.map(|suggestion| format!("- `{}`", suggestion))
.collect::<Vec<_>>()
.join("\n")));
} else {
err.note("did you mean to use one of the enum's variants?");
}
},
(Def::Struct(def_id), _) if ns == ValueNS => {
if let Some((ctor_def, ctor_vis))
= self.struct_constructors.get(&def_id).cloned() {
let accessible_ctor = self.is_accessible(ctor_vis);
if is_expected(ctor_def) && !accessible_ctor {
err.span_label(span, format!("constructor is not visible \
here due to private fields"));
}
} else {
// HACK(estebank): find a better way to figure out that this was a
// parser issue where a struct literal is being used on an expression
// where a brace being opened means a block is being started. Look
// ahead for the next text to see if `span` is followed by a `{`.
let sm = self.session.source_map();
let mut sp = span;
loop {
sp = sm.next_point(sp);
match sm.span_to_snippet(sp) {
Ok(ref snippet) => {
if snippet.chars().any(|c| { !c.is_whitespace() }) {
break;
}
}
_ => break,
}
}
let followed_by_brace = match sm.span_to_snippet(sp) {
Ok(ref snippet) if snippet == "{" => true,
_ => false,
};
// In case this could be a struct literal that needs to be surrounded
// by parenthesis, find the appropriate span.
let mut i = 0;
let mut closing_brace = None;
loop {
sp = sm.next_point(sp);
match sm.span_to_snippet(sp) {
Ok(ref snippet) => {
if snippet == "}" {
let sp = span.to(sp);
if let Ok(snippet) = sm.span_to_snippet(sp) {
closing_brace = Some((sp, snippet));
}
break;
}
}
_ => break,
}
i += 1;
// The bigger the span, the more likely we're incorrect --
// bound it to 100 chars long.
if i > 100 {
break;
}
}
match source {
PathSource::Expr(Some(parent)) => {
match parent.node {
ExprKind::MethodCall(ref path_assignment, _) => {
err.span_suggestion(
sm.start_point(parent.span)
.to(path_assignment.ident.span),
"use `::` to access an associated function",
format!("{}::{}",
path_str,
path_assignment.ident),
Applicability::MaybeIncorrect
);
},
_ => {
err.span_label(
span,
format!("did you mean `{} {{ /* fields */ }}`?",
path_str),
);
},
}
},
PathSource::Expr(None) if followed_by_brace == true => {
if let Some((sp, snippet)) = closing_brace {
err.span_suggestion(
sp,
"surround the struct literal with parenthesis",
format!("({})", snippet),
Applicability::MaybeIncorrect,
);
} else {
err.span_label(
span,
format!("did you mean `({} {{ /* fields */ }})`?",
path_str),
);
}
},
_ => {
err.span_label(
span,
format!("did you mean `{} {{ /* fields */ }}`?",
path_str),
);
},
}
}
}
(Def::Union(..), _) |
(Def::Variant(..), _) |
(Def::VariantCtor(_, CtorKind::Fictive), _) if ns == ValueNS => {
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
path_str));
}
(Def::SelfTy(..), _) if ns == ValueNS => {
err.span_label(span, fallback_label);
err.note("can't use `Self` as a constructor, you must use the \
implemented struct");
}
(Def::TyAlias(_), _) | (Def::AssociatedTy(..), _) if ns == ValueNS => {
err.note("can't use a type alias as a constructor");
}
_ => return false,
}
true
}
/// Handles error reporting for `smart_resolve_path_fragment` function.
/// Creates base error and amends it with one short label and possibly some longer helps/notes.
fn smart_resolve_report_errors(
@ -3137,7 +3324,7 @@ impl<'a> Resolver<'a> {
let ns = source.namespace();
let is_expected = &|def| source.is_expected(def);
let is_enum_variant = &|def| if let Def::Variant(..) = def { true } else { false };
// Make the base error.
let expected = source.descr_expected();
let path_str = Segment::names_to_string(path);
@ -3317,188 +3504,13 @@ impl<'a> Resolver<'a> {
// Try context-dependent help if relaxed lookup didn't work.
if let Some(def) = def {
match (def, source) {
(Def::Macro(..), _) => {
err.span_suggestion(
span,
"use `!` to invoke the macro",
format!("{}!", path_str),
Applicability::MaybeIncorrect,
);
return (err, candidates);
}
(Def::TyAlias(..), PathSource::Trait(_)) => {
err.span_label(span, "type aliases cannot be used as traits");
if nightly_options::is_nightly_build() {
err.note("did you mean to use a trait alias?");
}
return (err, candidates);
}
(Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
ExprKind::Field(_, ident) => {
err.span_suggestion(
parent.span,
"use the path separator to refer to an item",
format!("{}::{}", path_str, ident),
Applicability::MaybeIncorrect,
);
return (err, candidates);
}
ExprKind::MethodCall(ref segment, ..) => {
let span = parent.span.with_hi(segment.ident.span.hi());
err.span_suggestion(
span,
"use the path separator to refer to an item",
format!("{}::{}", path_str, segment.ident),
Applicability::MaybeIncorrect,
);
return (err, candidates);
}
_ => {}
},
(Def::Enum(..), PathSource::TupleStruct)
| (Def::Enum(..), PathSource::Expr(..)) => {
if let Some(variants) = self.collect_enum_variants(def) {
err.note(&format!("did you mean to use one \
of the following variants?\n{}",
variants.iter()
.map(|suggestion| path_names_to_string(suggestion))
.map(|suggestion| format!("- `{}`", suggestion))
.collect::<Vec<_>>()
.join("\n")));
} else {
err.note("did you mean to use one of the enum's variants?");
}
return (err, candidates);
},
(Def::Struct(def_id), _) if ns == ValueNS => {
if let Some((ctor_def, ctor_vis))
= self.struct_constructors.get(&def_id).cloned() {
let accessible_ctor = self.is_accessible(ctor_vis);
if is_expected(ctor_def) && !accessible_ctor {
err.span_label(span, format!("constructor is not visible \
here due to private fields"));
}
} else {
// HACK(estebank): find a better way to figure out that this was a
// parser issue where a struct literal is being used on an expression
// where a brace being opened means a block is being started. Look
// ahead for the next text to see if `span` is followed by a `{`.
let sm = self.session.source_map();
let mut sp = span;
loop {
sp = sm.next_point(sp);
match sm.span_to_snippet(sp) {
Ok(ref snippet) => {
if snippet.chars().any(|c| { !c.is_whitespace() }) {
break;
}
}
_ => break,
}
}
let followed_by_brace = match sm.span_to_snippet(sp) {
Ok(ref snippet) if snippet == "{" => true,
_ => false,
};
// In case this could be a struct literal that needs to be surrounded
// by parenthesis, find the appropriate span.
let mut i = 0;
let mut closing_brace = None;
loop {
sp = sm.next_point(sp);
match sm.span_to_snippet(sp) {
Ok(ref snippet) => {
if snippet == "}" {
let sp = span.to(sp);
if let Ok(snippet) = sm.span_to_snippet(sp) {
closing_brace = Some((sp, snippet));
}
break;
}
}
_ => break,
}
i += 1;
// The bigger the span, the more likely we're
// incorrect. Bound it to 100 chars long.
if i > 100 {
break;
}
}
match source {
PathSource::Expr(Some(parent)) => {
match parent.node {
ExprKind::MethodCall(ref path_assignment, _) => {
err.span_suggestion(
sm.start_point(parent.span)
.to(path_assignment.ident.span),
"use `::` to access an associated function",
format!("{}::{}",
path_str,
path_assignment.ident),
Applicability::MaybeIncorrect
);
return (err, candidates);
},
_ => {
err.span_label(
span,
format!("did you mean `{} {{ /* fields */ }}`?",
path_str),
);
return (err, candidates);
},
}
},
PathSource::Expr(None) if followed_by_brace == true => {
if let Some((sp, snippet)) = closing_brace {
err.span_suggestion(
sp,
"surround the struct literal with parenthesis",
format!("({})", snippet),
Applicability::MaybeIncorrect,
);
} else {
err.span_label(
span,
format!("did you mean `({} {{ /* fields */ }})`?",
path_str),
);
}
return (err, candidates);
},
_ => {
err.span_label(
span,
format!("did you mean `{} {{ /* fields */ }}`?",
path_str),
);
return (err, candidates);
},
}
}
return (err, candidates);
}
(Def::Union(..), _) |
(Def::Variant(..), _) |
(Def::VariantCtor(_, CtorKind::Fictive), _) if ns == ValueNS => {
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
path_str));
return (err, candidates);
}
(Def::SelfTy(..), _) if ns == ValueNS => {
err.span_label(span, fallback_label);
err.note("can't use `Self` as a constructor, you must use the \
implemented struct");
return (err, candidates);
}
(Def::TyAlias(_), _) | (Def::AssociatedTy(..), _) if ns == ValueNS => {
err.note("can't use a type alias as a constructor");
return (err, candidates);
}
_ => {}
if self.smart_resolve_context_dep_help(&mut err,
span,
source,
def,
&path_str,
&fallback_label) {
return (err, candidates);
}
}