Factored out context-dependent help for error reporting.
This commit is contained in:
parent
0e2d96e88c
commit
14b674ab9d
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user