Skip to content

Commit 68b0e7d

Browse files
committed
Refactor generic argument count check in method/confirm.rs
1 parent 49c4573 commit 68b0e7d

File tree

4 files changed

+195
-124
lines changed

4 files changed

+195
-124
lines changed

src/librustc/hir/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,13 @@ impl GenericArg {
401401
GenericArg::Type(t) => t.span,
402402
}
403403
}
404+
405+
pub fn id(&self) -> NodeId {
406+
match self {
407+
GenericArg::Lifetime(l) => l.id,
408+
GenericArg::Type(t) => t.id,
409+
}
410+
}
404411
}
405412

406413
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]

src/librustc_typeck/astconv.rs

Lines changed: 169 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ use require_c_abi_if_variadic;
3030
use util::common::ErrorReported;
3131
use util::nodemap::{FxHashSet, FxHashMap};
3232
use errors::{FatalError, DiagnosticId};
33+
use lint;
3334

3435
use std::iter;
3536
use syntax::ast;
3637
use syntax::feature_gate::{GateIssue, emit_feature_err};
37-
use syntax_pos::Span;
38+
use syntax_pos::{Span, MultiSpan};
3839

3940
pub trait AstConv<'gcx, 'tcx> {
4041
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
@@ -172,21 +173,164 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
172173
-> &'tcx Substs<'tcx>
173174
{
174175

175-
let (substs, assoc_bindings) =
176-
item_segment.with_generic_args(|generic_args| {
177-
self.create_substs_for_ast_path(
178-
span,
179-
def_id,
180-
generic_args,
181-
item_segment.infer_types,
182-
None)
183-
});
176+
let (substs, assoc_bindings) = item_segment.with_generic_args(|generic_args| {
177+
self.create_substs_for_ast_path(
178+
span,
179+
def_id,
180+
generic_args,
181+
item_segment.infer_types,
182+
None,
183+
)
184+
});
184185

185-
assoc_bindings.first().map(|b| self.prohibit_projection(b.span));
186+
assoc_bindings.first().map(|b| Self::prohibit_assoc_ty_binding(self.tcx(), b.span));
186187

187188
substs
188189
}
189190

191+
/// Check that the correct number of generic arguments have been provided.
192+
/// This is used both for type declarations and function calls.
193+
pub fn check_generic_arg_count(
194+
tcx: TyCtxt,
195+
span: Span,
196+
def: &ty::Generics,
197+
args: &hir::GenericArgs,
198+
is_declaration: bool,
199+
is_method_call: bool,
200+
has_self: bool,
201+
infer_types: bool,
202+
) -> bool {
203+
// At this stage we are guaranteed that the generic arguments are in the correct order, e.g.
204+
// that lifetimes will proceed types. So it suffices to check the number of each generic
205+
// arguments in order to validate them with respect to the generic parameters.
206+
let param_counts = def.own_counts();
207+
let arg_counts = args.own_counts();
208+
let infer_lifetimes = !is_declaration && arg_counts.lifetimes == 0;
209+
210+
let mut defaults: ty::GenericParamCount = Default::default();
211+
for param in &def.params {
212+
match param.kind {
213+
GenericParamDefKind::Lifetime => {}
214+
GenericParamDefKind::Type { has_default, .. } => {
215+
defaults.types += has_default as usize
216+
}
217+
};
218+
}
219+
220+
if !is_declaration && !args.bindings.is_empty() {
221+
AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
222+
}
223+
224+
// Prohibit explicit lifetime arguments if late-bound lifetime parameters are present.
225+
if !infer_lifetimes {
226+
if let Some(span_late) = def.has_late_bound_regions {
227+
let msg = "cannot specify lifetime arguments explicitly \
228+
if late bound lifetime parameters are present";
229+
let note = "the late bound lifetime parameter is introduced here";
230+
let span = args.args[0].span();
231+
if !is_method_call && arg_counts.lifetimes != param_counts.lifetimes {
232+
let mut err = tcx.sess.struct_span_err(span, msg);
233+
err.span_note(span_late, note);
234+
err.emit();
235+
return true;
236+
} else {
237+
let mut multispan = MultiSpan::from_span(span);
238+
multispan.push_span_label(span_late, note.to_string());
239+
tcx.lint_node(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
240+
args.args[0].id(), multispan, msg);
241+
return false;
242+
}
243+
}
244+
}
245+
246+
let check_kind_count = |error_code_less: &str,
247+
error_code_more: &str,
248+
kind,
249+
required,
250+
permitted,
251+
provided| {
252+
// We enforce the following: `required` <= `provided` <= `permitted`.
253+
// For kinds without defaults (i.e. lifetimes), `required == permitted`.
254+
// For other kinds (i.e. types), `permitted` may be greater than `required`.
255+
if required <= provided && provided <= permitted {
256+
return false;
257+
}
258+
259+
// Unfortunately lifetime and type parameter mismatches are typically styled
260+
// differently in diagnostics, which means we have a few cases to consider here.
261+
let (bound, quantifier, suppress_error) = if required != permitted {
262+
if provided < required {
263+
(required, "at least ", false)
264+
} else { // provided > permitted
265+
(permitted, "at most ", true)
266+
}
267+
} else {
268+
(required, "", false)
269+
};
270+
let label = if required == permitted && provided > permitted {
271+
let diff = provided - permitted;
272+
format!(
273+
"{}unexpected {} argument{}",
274+
if diff != 1 { format!("{} ", diff) } else { String::new() },
275+
kind,
276+
if diff != 1 { "s" } else { "" },
277+
)
278+
} else {
279+
format!(
280+
"expected {}{} {} argument{}",
281+
quantifier,
282+
bound,
283+
kind,
284+
if required != 1 { "s" } else { "" },
285+
)
286+
};
287+
288+
tcx.sess.struct_span_err_with_code(
289+
span,
290+
&format!(
291+
"wrong number of {} arguments: expected {}{}, found {}",
292+
kind,
293+
quantifier,
294+
bound,
295+
provided,
296+
),
297+
DiagnosticId::Error({
298+
if provided <= permitted {
299+
error_code_less
300+
} else {
301+
error_code_more
302+
}
303+
}.into())
304+
).span_label(span, label).emit();
305+
306+
suppress_error
307+
};
308+
309+
if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
310+
check_kind_count(
311+
"E0107",
312+
"E0107",
313+
"lifetime",
314+
param_counts.lifetimes,
315+
param_counts.lifetimes,
316+
arg_counts.lifetimes,
317+
);
318+
}
319+
if !infer_types
320+
|| arg_counts.types > param_counts.types - defaults.types - has_self as usize {
321+
check_kind_count(
322+
"E0243",
323+
"E0244", // FIXME: E0243 and E0244 should be unified.
324+
"type",
325+
param_counts.types - defaults.types - has_self as usize,
326+
param_counts.types - has_self as usize,
327+
arg_counts.types,
328+
)
329+
} else {
330+
false
331+
}
332+
}
333+
190334
/// Creates the relevant generic argument substitutions
191335
/// corresponding to a set of generic parameters.
192336
pub fn create_substs_for_generic_args<'a, 'b, A, P, I>(
@@ -355,7 +499,16 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
355499
assert_eq!(generic_params.has_self, self_ty.is_some());
356500

357501
let has_self = generic_params.has_self;
358-
check_generic_arg_count(tcx, span, &generic_params, &generic_args, has_self, infer_types);
502+
Self::check_generic_arg_count(
503+
self.tcx(),
504+
span,
505+
&generic_params,
506+
&generic_args,
507+
true, // `is_declaration`
508+
false, // `is_method_call` (irrelevant here)
509+
has_self,
510+
infer_types,
511+
);
359512

360513
let is_object = self_ty.map_or(false, |ty| ty.sty == TRAIT_OBJECT_DUMMY_SELF);
361514
let default_needs_object_self = |param: &ty::GenericParamDef| {
@@ -548,7 +701,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
548701
trait_def_id,
549702
self_ty,
550703
trait_segment);
551-
assoc_bindings.first().map(|b| self.prohibit_projection(b.span));
704+
assoc_bindings.first().map(|b| AstConv::prohibit_assoc_ty_binding(self.tcx(), b.span));
552705
ty::TraitRef::new(trait_def_id, substs)
553706
}
554707

@@ -1113,15 +1266,15 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
11131266
}
11141267
}
11151268
for binding in &generic_args.bindings {
1116-
self.prohibit_projection(binding.span);
1269+
Self::prohibit_assoc_ty_binding(self.tcx(), binding.span);
11171270
break;
11181271
}
11191272
})
11201273
}
11211274
}
11221275

1123-
pub fn prohibit_projection(&self, span: Span) {
1124-
let mut err = struct_span_err!(self.tcx().sess, span, E0229,
1276+
pub fn prohibit_assoc_ty_binding(tcx: TyCtxt, span: Span) {
1277+
let mut err = struct_span_err!(tcx.sess, span, E0229,
11251278
"associated type bindings are not allowed here");
11261279
err.span_label(span, "associated type not allowed here").emit();
11271280
}
@@ -1497,109 +1650,6 @@ fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
14971650
(auto_traits, trait_bounds)
14981651
}
14991652

1500-
pub fn check_generic_arg_count(
1501-
tcx: TyCtxt,
1502-
span: Span,
1503-
def: &ty::Generics,
1504-
args: &hir::GenericArgs,
1505-
has_self: bool,
1506-
infer_types: bool,
1507-
) {
1508-
// At this stage we are guaranteed that the generic arguments are in the correct order, e.g.
1509-
// that lifetimes will proceed types. So it suffices to check the number of each generic
1510-
// arguments in order to validate them with respect to the generic parameters.
1511-
let param_counts = def.own_counts();
1512-
let arg_counts = args.own_counts();
1513-
1514-
let mut defaults: ty::GenericParamCount = Default::default();
1515-
for param in &def.params {
1516-
match param.kind {
1517-
GenericParamDefKind::Lifetime => {}
1518-
GenericParamDefKind::Type { has_default, .. } => defaults.types += has_default as usize,
1519-
};
1520-
}
1521-
1522-
let check_kind_count = |error_code_less: &str,
1523-
error_code_more: &str,
1524-
kind,
1525-
required,
1526-
permitted,
1527-
provided| {
1528-
// We enforce the following: `required` <= `provided` <= `permitted`.
1529-
// For kinds without defaults (i.e. lifetimes), `required == permitted`.
1530-
// For other kinds (i.e. types), `permitted` may be greater than `required`.
1531-
if required <= provided && provided <= permitted {
1532-
return;
1533-
}
1534-
1535-
// Unfortunately lifetime and type parameter mismatches are typically styled
1536-
// differently in diagnostics, which means we have a few cases to consider here.
1537-
let (bound, quantifier) = if required != permitted {
1538-
if provided < required {
1539-
(required, "at least ")
1540-
} else { // provided > permitted
1541-
(permitted, "at most ")
1542-
}
1543-
} else {
1544-
(required, "")
1545-
};
1546-
let label = if required == permitted && provided > permitted {
1547-
let diff = provided - permitted;
1548-
format!(
1549-
"{}unexpected {} argument{}",
1550-
if diff != 1 { format!("{} ", diff) } else { String::new() },
1551-
kind,
1552-
if diff != 1 { "s" } else { "" },
1553-
)
1554-
} else {
1555-
format!(
1556-
"expected {}{} {} argument{}",
1557-
quantifier,
1558-
bound,
1559-
kind,
1560-
if required != 1 { "s" } else { "" },
1561-
)
1562-
};
1563-
1564-
tcx.sess.struct_span_err_with_code(
1565-
span,
1566-
&format!(
1567-
"wrong number of {} arguments: expected {}{}, found {}",
1568-
kind,
1569-
quantifier,
1570-
bound,
1571-
provided,
1572-
),
1573-
DiagnosticId::Error({
1574-
if provided <= permitted {
1575-
error_code_less
1576-
} else {
1577-
error_code_more
1578-
}
1579-
}.into())
1580-
).span_label(span, label).emit();
1581-
};
1582-
1583-
check_kind_count(
1584-
"E0107",
1585-
"E0107",
1586-
"lifetime",
1587-
param_counts.lifetimes,
1588-
param_counts.lifetimes,
1589-
arg_counts.lifetimes,
1590-
);
1591-
if !infer_types || arg_counts.types > param_counts.types - defaults.types - has_self as usize {
1592-
check_kind_count(
1593-
"E0243",
1594-
"E0244", // FIXME: E0243 and E0244 should be unified.
1595-
"type",
1596-
param_counts.types - defaults.types - has_self as usize,
1597-
param_counts.types - has_self as usize,
1598-
arg_counts.types,
1599-
);
1600-
}
1601-
}
1602-
16031653
// A helper struct for conveniently grouping a set of bounds which we pass to
16041654
// and return from functions in multiple places.
16051655
#[derive(PartialEq, Eq, Clone, Debug)]

0 commit comments

Comments
 (0)