Repository: rust-lang/rust
File: compiler/rustc_ast_passes/src/feature_gate.rs (Rust)

Code before:
                    } else {
                        gate_feature_post!(
                            &self.vis,
                            type_alias_impl_trait,
                            ty.span,
                            "`impl Trait` in type aliases is unstable"
                        );
                    }
                }
                visit::walk_ty(self, ty);
            }
        }
        ImplTraitVisitor { vis: self, in_associated_ty }.visit_ty(ty);
    }

    fn check_late_bound_lifetime_defs(&self, params: &[ast::GenericParam]) {
        // Check only lifetime parameters are present and that the lifetime
        // parameters that are present have no bounds.
        let non_lt_param_spans: Vec<_> = params
            .iter()
            .filter_map(|param| match param.kind {
                ast::GenericParamKind::Lifetime { .. } => None,
                _ => Some(param.ident.span),
            })
            .collect();
        if !non_lt_param_spans.is_empty() {
            for span in non_lt_param_spans {
                let msg = crate::fluent_generated::ast_passes_forbidden_non_lifetime_param;
                gate_feature_post!(self, non_lifetime_binders, span, msg);
            }
        }
        for param in params {
            if !param.bounds.is_empty() {
                let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
                self.sess.emit_err(errors::ForbiddenLifetimeBound { spans });
            }
        }
    }
}

impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
        let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
        // Check feature gates for built-in attributes.
        if let Some(BuiltinAttribute {
            gate: AttributeGate::Gated(_, name, descr, has_feature),
            ..
        }) = attr_info
        {
            gate_feature_fn!(self, has_feature, attr.span, *name, *descr);
        }

Review comment (suggestion):
What this will do is emit a feature error for every single span. What I want is for it to be a single feature error with multiple primary spans.

I prefer if this is not changed, or it is fixed in the right way.

Code after:

    /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
    fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) {
        struct ImplTraitVisitor<'a> {
            vis: &'a PostExpansionVisitor<'a>,
            in_associated_ty: bool,
        }
        impl Visitor<'_> for ImplTraitVisitor<'_> {
            fn visit_ty(&mut self, ty: &ast::Ty) {
                if let ast::TyKind::ImplTrait(..) = ty.kind {
                    if self.in_associated_ty {
                        gate!(
                            &self.vis,
                            impl_trait_in_assoc_type,
                            ty.span,
                            "`impl Trait` in associated types is unstable"
                        );
                    } else {
                        gate!(
                            &self.vis,
                            type_alias_impl_trait,
                            ty.span,
                            "`impl Trait` in type aliases is unstable"
                        );
                    }
                }
                visit::walk_ty(self, ty);
            }
        }
        ImplTraitVisitor { vis: self, in_associated_ty }.visit_ty(ty);
    }

    fn check_late_bound_lifetime_defs(&self, params: &[ast::GenericParam]) {
        // Check only lifetime parameters are present and that the lifetime
        // parameters that are present have no bounds.
        let non_lt_param_spans = params.iter().filter_map(|param| match param.kind {
            ast::GenericParamKind::Lifetime { .. } => None,
            _ => Some(param.ident.span),
        });
        gate_multi!(
            &self,
            non_lifetime_binders,
            non_lt_param_spans,
            crate::fluent_generated::ast_passes_forbidden_non_lifetime_param
        );
        for param in params {
            if !param.bounds.is_empty() {
                let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
                self.sess.emit_err(errors::ForbiddenLifetimeBound { spans });
            }
        }