// This pass is supposed to perform only simple checks not requiring name resolution
// or type checking or some other kind of complex analysis.
+use itertools::{Either, Itertools};
use rustc_ast::ast::*;
use rustc_ast::attr;
use rustc_ast::expand::is_proc_macro_attr;
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use std::mem;
+use std::ops::DerefMut;
const MORE_EXTERN: &str =
"for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
}
}
- /// We currently do not permit const generics in `const fn`,
- /// as this is tantamount to allowing compile-time dependent typing.
- ///
- /// FIXME(const_generics): Is this really true / necessary? Discuss with @varkor.
- /// At any rate, the restriction feels too syntactic. Consider moving it to e.g. typeck.
- fn check_const_fn_const_generic(&self, span: Span, sig: &FnSig, generics: &Generics) {
- if let Const::Yes(const_span) = sig.header.constness {
- // Look for const generics and error if we find any.
- for param in &generics.params {
- if let GenericParamKind::Const { .. } = param.kind {
- self.err_handler()
- .struct_span_err(
- span,
- "const parameters are not permitted in const functions",
- )
- .span_label(const_span, "`const` because of this")
- .emit();
- }
- }
- }
- }
-
fn check_item_named(&self, ident: Ident, kind: &str) {
if ident.name != kw::Underscore {
return;
}
}
- fn suggest_correct_generic_order(&self, data: &AngleBracketedArgs) -> String {
+ fn correct_generic_order_suggestion(&self, data: &AngleBracketedArgs) -> String {
// Lifetimes always come first.
let lt_sugg = data.args.iter().filter_map(|arg| match arg {
AngleBracketedArg::Arg(lt @ GenericArg::Lifetime(_)) => {
_ => None,
});
let args_sugg = data.args.iter().filter_map(|a| match a {
- AngleBracketedArg::Arg(GenericArg::Lifetime(_)) => None,
+ AngleBracketedArg::Arg(GenericArg::Lifetime(_)) | AngleBracketedArg::Constraint(_) => {
+ None
+ }
AngleBracketedArg::Arg(arg) => Some(pprust::to_string(|s| s.print_generic_arg(arg))),
- AngleBracketedArg::Constraint(_) => None,
});
- // Cosntraints always come last.
+ // Constraints always come last.
let constraint_sugg = data.args.iter().filter_map(|a| match a {
AngleBracketedArg::Arg(_) => None,
AngleBracketedArg::Constraint(c) => {
return;
}
// Find all generic argument coming after the first constraint...
- let constraint_spans = data
- .args
- .iter()
- .filter_map(|arg| match arg {
- AngleBracketedArg::Constraint(c) => Some(c.span),
- _ => None,
- })
- .collect::<Vec<_>>();
- let arg_spans = data
- .args
- .iter()
- .filter_map(|arg| match arg {
- AngleBracketedArg::Arg(a) => Some(a.span()),
- _ => None,
- })
- .collect::<Vec<_>>();
+ let (constraint_spans, arg_spans): (Vec<Span>, Vec<Span>) =
+ data.args.iter().partition_map(|arg| match arg {
+ AngleBracketedArg::Constraint(c) => Either::Left(c.span),
+ AngleBracketedArg::Arg(a) => Either::Right(a.span()),
+ });
let args_len = arg_spans.len();
let constraint_len = constraint_spans.len();
// ...and then error:
arg_spans.clone(),
"generic arguments must come before the first constraint",
)
- .span_labels(constraint_spans, "constraint")
- .span_labels(arg_spans, "generic argument")
+ .span_label(constraint_spans[0], &format!("constraint{}", pluralize!(constraint_len)))
+ .span_label(
+ *arg_spans.iter().last().unwrap(),
+ &format!("generic argument{}", pluralize!(args_len)),
+ )
+ .span_labels(constraint_spans, "")
+ .span_labels(arg_spans, "")
.span_suggestion_verbose(
data.span,
&format!(
pluralize!(constraint_len),
pluralize!(args_len)
),
- self.suggest_correct_generic_order(&data),
+ self.correct_generic_order_suggestion(&data),
Applicability::MachineApplicable,
)
.emit();
.emit();
}
}
- ItemKind::Fn(def, ref sig, ref generics, ref body) => {
+ ItemKind::Fn(def, _, _, ref body) => {
self.check_defaultness(item.span, def);
- self.check_const_fn_const_generic(item.span, sig, generics);
if body.is_none() {
let msg = "free function without a body";
for predicate in &generics.where_clause.predicates {
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
- self.err_handler()
- .struct_span_err(
- predicate.span,
- "equality constraints are not yet supported in `where` clauses",
- )
- .span_label(predicate.span, "not supported")
- .note(
- "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> \
- for more information",
- )
- .emit();
+ deny_equality_constraints(self, predicate, generics);
}
}
}
}
+fn deny_equality_constraints(
+ this: &mut AstValidator<'_>,
+ predicate: &WhereEqPredicate,
+ generics: &Generics,
+) {
+ let mut err = this.err_handler().struct_span_err(
+ predicate.span,
+ "equality constraints are not yet supported in `where` clauses",
+ );
+ err.span_label(predicate.span, "not supported");
+
+ // Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
+ if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind {
+ if let TyKind::Path(None, path) = &qself.ty.kind {
+ match &path.segments[..] {
+ [PathSegment { ident, args: None, .. }] => {
+ for param in &generics.params {
+ if param.ident == *ident {
+ let param = ident;
+ match &full_path.segments[qself.position..] {
+ [PathSegment { ident, .. }] => {
+ // Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
+ let mut assoc_path = full_path.clone();
+ // Remove `Bar` from `Foo::Bar`.
+ assoc_path.segments.pop();
+ let len = assoc_path.segments.len() - 1;
+ // Build `<Bar = RhsTy>`.
+ let arg = AngleBracketedArg::Constraint(AssocTyConstraint {
+ id: rustc_ast::node_id::DUMMY_NODE_ID,
+ ident: *ident,
+ kind: AssocTyConstraintKind::Equality {
+ ty: predicate.rhs_ty.clone(),
+ },
+ span: ident.span,
+ });
+ // Add `<Bar = RhsTy>` to `Foo`.
+ match &mut assoc_path.segments[len].args {
+ Some(args) => match args.deref_mut() {
+ GenericArgs::Parenthesized(_) => continue,
+ GenericArgs::AngleBracketed(args) => {
+ args.args.push(arg);
+ }
+ },
+ empty_args => {
+ *empty_args = AngleBracketedArgs {
+ span: ident.span,
+ args: vec![arg],
+ }
+ .into();
+ }
+ }
+ err.span_suggestion_verbose(
+ predicate.span,
+ &format!(
+ "if `{}` is an associated type you're trying to set, \
+ use the associated type binding syntax",
+ ident
+ ),
+ format!(
+ "{}: {}",
+ param,
+ pprust::path_to_string(&assoc_path)
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ };
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ err.note(
+ "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
+ );
+ err.emit();
+}
+
pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
let mut validator = AstValidator {
session,