Skip to content

Commit

Permalink
Report all errors in check_generic_arg_count
Browse files Browse the repository at this point in the history
  • Loading branch information
varkor committed Feb 22, 2020
1 parent 2a50487 commit d232acd
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 85 deletions.
147 changes: 77 additions & 70 deletions src/librustc_typeck/astconv.rs
Expand Up @@ -287,7 +287,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
position: GenericArgPosition,
has_self: bool,
infer_args: bool,
) -> (bool, Option<Vec<Span>>) {
) -> (bool, Vec<Span>) {
// At this stage we are guaranteed that the generic arguments are in the correct order, e.g.
// that lifetimes will proceed types. So it suffices to check the number of each generic
// arguments in order to validate them with respect to the generic parameters.
Expand Down Expand Up @@ -341,104 +341,110 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}

let check_kind_count = |kind, required, permitted, provided, offset| {
debug!(
"check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}",
kind, required, permitted, provided, offset
);
// We enforce the following: `required` <= `provided` <= `permitted`.
// For kinds without defaults (e.g.., lifetimes), `required == permitted`.
// For other kinds (i.e., types), `permitted` may be greater than `required`.
if required <= provided && provided <= permitted {
return (reported_late_bound_region_err.unwrap_or(false), None);
}

// Unfortunately lifetime and type parameter mismatches are typically styled
// differently in diagnostics, which means we have a few cases to consider here.
let (bound, quantifier) = if required != permitted {
if provided < required {
(required, "at least ")
} else {
// provided > permitted
(permitted, "at most ")
let check_kind_count =
|kind, required, permitted, provided, offset, unexpected_spans: &mut Vec<Span>| {
debug!(
"check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}",
kind, required, permitted, provided, offset
);
// We enforce the following: `required` <= `provided` <= `permitted`.
// For kinds without defaults (e.g.., lifetimes), `required == permitted`.
// For other kinds (i.e., types), `permitted` may be greater than `required`.
if required <= provided && provided <= permitted {
return false;
}
} else {
(required, "")
};

let mut potential_assoc_types: Option<Vec<Span>> = None;
let (spans, label) = if required == permitted && provided > permitted {
// In the case when the user has provided too many arguments,
// we want to point to the unexpected arguments.
let spans: Vec<Span> = args.args[offset + permitted..offset + provided]
.iter()
.map(|arg| arg.span())
.collect();
potential_assoc_types = Some(spans.clone());
(spans, format!("unexpected {} argument", kind))
} else {
(
vec![span],
format!(
"expected {}{} {} argument{}",
quantifier,
bound,
kind,
pluralize!(bound),
// Unfortunately lifetime and type parameter mismatches are typically styled
// differently in diagnostics, which means we have a few cases to consider here.
let (bound, quantifier) = if required != permitted {
if provided < required {
(required, "at least ")
} else {
// provided > permitted
(permitted, "at most ")
}
} else {
(required, "")
};

let (spans, label) = if required == permitted && provided > permitted {
// In the case when the user has provided too many arguments,
// we want to point to the unexpected arguments.
let spans: Vec<Span> = args.args[offset + permitted..offset + provided]
.iter()
.map(|arg| arg.span())
.collect();
unexpected_spans.extend(spans.clone());
(spans, format!("unexpected {} argument", kind))
} else {
(
vec![span],
format!(
"expected {}{} {} argument{}",
quantifier,
bound,
kind,
pluralize!(bound),
),
)
};

let mut err = tcx.sess.struct_span_err_with_code(
spans.clone(),
&format!(
"wrong number of {} arguments: expected {}{}, found {}",
kind, quantifier, bound, provided,
),
)
};
DiagnosticId::Error("E0107".into()),
);
for span in spans {
err.span_label(span, label.as_str());
}
err.emit();

let mut err = tcx.sess.struct_span_err_with_code(
spans.clone(),
&format!(
"wrong number of {} arguments: expected {}{}, found {}",
kind, quantifier, bound, provided,
),
DiagnosticId::Error("E0107".into()),
);
for span in spans {
err.span_label(span, label.as_str());
}
err.emit();
true
};

(true, potential_assoc_types)
};
let mut arg_count_mismatch = reported_late_bound_region_err.unwrap_or(false);
let mut unexpected_spans = vec![];

if reported_late_bound_region_err.is_none()
&& (!infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes)
{
check_kind_count(
arg_count_mismatch |= check_kind_count(
"lifetime",
param_counts.lifetimes,
param_counts.lifetimes,
arg_counts.lifetimes,
0,
&mut unexpected_spans,
);
}
// FIXME(const_generics:defaults)
if !infer_args || arg_counts.consts > param_counts.consts {
check_kind_count(
arg_count_mismatch |= check_kind_count(
"const",
param_counts.consts,
param_counts.consts,
arg_counts.consts,
arg_counts.lifetimes + arg_counts.types,
&mut unexpected_spans,
);
}
// Note that type errors are currently be emitted *after* const errors.
if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize
{
check_kind_count(
arg_count_mismatch |= check_kind_count(
"type",
param_counts.types - defaults.types - has_self as usize,
param_counts.types - has_self as usize,
arg_counts.types,
arg_counts.lifetimes,
)
} else {
(reported_late_bound_region_err.unwrap_or(false), None)
&mut unexpected_spans,
);
}

(arg_count_mismatch, unexpected_spans)
}

/// Creates the relevant generic argument substitutions
Expand Down Expand Up @@ -627,7 +633,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
generic_args: &'a hir::GenericArgs<'_>,
infer_args: bool,
self_ty: Option<Ty<'tcx>>,
) -> (SubstsRef<'tcx>, Vec<ConvertedBinding<'a, 'tcx>>, Option<Vec<Span>>) {
) -> (SubstsRef<'tcx>, Vec<ConvertedBinding<'a, 'tcx>>, Vec<Span>) {
// If the type is parameterized by this region, then replace this
// region with the current anon region binding (in other words,
// whatever & would get replaced with).
Expand Down Expand Up @@ -922,7 +928,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self_ty: Ty<'tcx>,
bounds: &mut Bounds<'tcx>,
speculative: bool,
) -> Option<Vec<Span>> {
) -> Vec<Span> {
let trait_def_id = trait_ref.trait_def_id();

debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id);
Expand Down Expand Up @@ -968,6 +974,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
"instantiate_poly_trait_ref({:?}, bounds={:?}) -> {:?}",
trait_ref, bounds, poly_trait_ref
);

potential_assoc_types
}

Expand Down Expand Up @@ -996,7 +1003,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
constness: Constness,
self_ty: Ty<'tcx>,
bounds: &mut Bounds<'tcx>,
) -> Option<Vec<Span>> {
) -> Vec<Span> {
self.instantiate_poly_trait_ref_inner(
&poly_trait_ref.trait_ref,
poly_trait_ref.span,
Expand Down Expand Up @@ -1085,7 +1092,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_def_id: DefId,
self_ty: Ty<'tcx>,
trait_segment: &'a hir::PathSegment<'a>,
) -> (SubstsRef<'tcx>, Vec<ConvertedBinding<'a, 'tcx>>, Option<Vec<Span>>) {
) -> (SubstsRef<'tcx>, Vec<ConvertedBinding<'a, 'tcx>>, Vec<Span>) {
debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment);

self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment);
Expand Down Expand Up @@ -1436,7 +1443,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
dummy_self,
&mut bounds,
);
potential_assoc_types.extend(cur_potential_assoc_types.into_iter().flatten());
potential_assoc_types.extend(cur_potential_assoc_types.into_iter());
}

// Expand trait aliases recursively and check that only one regular (non-auto) trait
Expand Down
1 change: 0 additions & 1 deletion src/test/ui/generic/generic-arg-mismatch-recover.rs
Expand Up @@ -4,7 +4,6 @@ struct Bar<'a>(&'a ());

fn main() {
Foo::<'static, 'static, ()>(&0); //~ ERROR wrong number of lifetime arguments
//~^ ERROR mismatched types

Bar::<'static, 'static, ()>(&()); //~ ERROR wrong number of lifetime arguments
//~^ ERROR wrong number of type arguments
Expand Down
18 changes: 4 additions & 14 deletions src/test/ui/generic/generic-arg-mismatch-recover.stderr
Expand Up @@ -4,28 +4,18 @@ error[E0107]: wrong number of lifetime arguments: expected 1, found 2
LL | Foo::<'static, 'static, ()>(&0);
| ^^^^^^^ unexpected lifetime argument

error[E0308]: mismatched types
--> $DIR/generic-arg-mismatch-recover.rs:6:33
|
LL | Foo::<'static, 'static, ()>(&0);
| ^^ expected `()`, found integer
|
= note: expected reference `&'static ()`
found reference `&{integer}`

error[E0107]: wrong number of lifetime arguments: expected 1, found 2
--> $DIR/generic-arg-mismatch-recover.rs:9:20
--> $DIR/generic-arg-mismatch-recover.rs:8:20
|
LL | Bar::<'static, 'static, ()>(&());
| ^^^^^^^ unexpected lifetime argument

error[E0107]: wrong number of type arguments: expected 0, found 1
--> $DIR/generic-arg-mismatch-recover.rs:9:29
--> $DIR/generic-arg-mismatch-recover.rs:8:29
|
LL | Bar::<'static, 'static, ()>(&());
| ^^ unexpected type argument

error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

Some errors have detailed explanations: E0107, E0308.
For more information about an error, try `rustc --explain E0107`.
For more information about this error, try `rustc --explain E0107`.

0 comments on commit d232acd

Please sign in to comment.