]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_ast_passes/src/feature_gate.rs
Merge 1.70 into proxmox/bookworm
[rustc.git] / compiler / rustc_ast_passes / src / feature_gate.rs
CommitLineData
3dfed10e 1use rustc_ast as ast;
74b04a01 2use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
353b0b11 3use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId};
2b03887a 4use rustc_ast::{PatKind, RangeEnd};
9ffffee4 5use rustc_errors::{Applicability, StashKey};
2b03887a
FG
6use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP};
7use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
3dfed10e 8use rustc_session::Session;
dfeec247 9use rustc_span::source_map::Spanned;
cdc7bbd5 10use rustc_span::symbol::sym;
dfeec247 11use rustc_span::Span;
2b03887a 12use rustc_target::spec::abi;
9ffffee4
FG
13use thin_vec::ThinVec;
14use tracing::debug;
15
16use crate::errors;
dfeec247 17
dfeec247 18macro_rules! gate_feature_fn {
5869c6ff
XL
19 ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
20 let (visitor, has_feature, span, name, explain, help) =
21 (&*$visitor, $has_feature, $span, $name, $explain, $help);
22 let has_feature: bool = has_feature(visitor.features);
23 debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
24 if !has_feature && !span.allows_unstable($name) {
f2b60f7d 25 feature_err(&visitor.sess.parse_sess, name, span, explain).help(help).emit();
5869c6ff
XL
26 }
27 }};
3dfed10e
XL
28 ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
29 let (visitor, has_feature, span, name, explain) =
30 (&*$visitor, $has_feature, $span, $name, $explain);
31 let has_feature: bool = has_feature(visitor.features);
dfeec247
XL
32 debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
33 if !has_feature && !span.allows_unstable($name) {
f2b60f7d
FG
34 feature_err(&visitor.sess.parse_sess, name, span, explain).emit();
35 }
36 }};
37 (future_incompatible; $visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
38 let (visitor, has_feature, span, name, explain) =
39 (&*$visitor, $has_feature, $span, $name, $explain);
40 let has_feature: bool = has_feature(visitor.features);
41 debug!(
42 "gate_feature(feature = {:?}, span = {:?}); has? {} (future_incompatible)",
43 name, span, has_feature
44 );
45 if !has_feature && !span.allows_unstable($name) {
46 feature_warn(&visitor.sess.parse_sess, name, span, explain);
dfeec247
XL
47 }
48 }};
49}
50
51macro_rules! gate_feature_post {
5869c6ff
XL
52 ($visitor: expr, $feature: ident, $span: expr, $explain: expr, $help: expr) => {
53 gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain, $help)
54 };
3dfed10e
XL
55 ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
56 gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
dfeec247 57 };
f2b60f7d
FG
58 (future_incompatible; $visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
59 gate_feature_fn!(future_incompatible; $visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
60 };
dfeec247
XL
61}
62
3dfed10e
XL
63pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
64 PostExpansionVisitor { sess, features }.visit_attribute(attr)
dfeec247
XL
65}
66
67struct PostExpansionVisitor<'a> {
3dfed10e
XL
68 sess: &'a Session,
69
70 // `sess` contains a `Features`, but this might not be that one.
dfeec247
XL
71 features: &'a Features,
72}
73
74impl<'a> PostExpansionVisitor<'a> {
04454e1e 75 fn check_abi(&self, abi: ast::StrLit, constness: ast::Const) {
dfeec247
XL
76 let ast::StrLit { symbol_unescaped, span, .. } = abi;
77
04454e1e 78 if let ast::Const::Yes(_) = constness {
064997fb 79 match symbol_unescaped {
04454e1e 80 // Stable
064997fb 81 sym::Rust | sym::C => {}
04454e1e
FG
82 abi => gate_feature_post!(
83 &self,
84 const_extern_fn,
85 span,
86 &format!("`{}` as a `const fn` ABI is unstable", abi)
87 ),
88 }
89 }
90
2b03887a
FG
91 match abi::is_enabled(&self.features, span, symbol_unescaped.as_str()) {
92 Ok(()) => (),
93 Err(abi::AbiDisabled::Unstable { feature, explain }) => {
94 feature_err_issue(
95 &self.sess.parse_sess,
96 feature,
5099ac24 97 span,
2b03887a
FG
98 GateIssue::Language,
99 explain,
100 )
101 .emit();
cdc7bbd5 102 }
2b03887a 103 Err(abi::AbiDisabled::Unrecognized) => {
064997fb
FG
104 if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {
105 self.sess.parse_sess.span_diagnostic.delay_span_bug(
106 span,
2b03887a
FG
107 &format!(
108 "unrecognized ABI not caught in lowering: {}",
109 symbol_unescaped.as_str()
110 ),
064997fb
FG
111 );
112 }
5e7ed085 113 }
dfeec247
XL
114 }
115 }
116
04454e1e 117 fn check_extern(&self, ext: ast::Extern, constness: ast::Const) {
064997fb 118 if let ast::Extern::Explicit(abi, _) = ext {
04454e1e 119 self.check_abi(abi, constness);
dfeec247
XL
120 }
121 }
122
dfeec247 123 /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
353b0b11 124 fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) {
dfeec247
XL
125 struct ImplTraitVisitor<'a> {
126 vis: &'a PostExpansionVisitor<'a>,
353b0b11 127 in_associated_ty: bool,
dfeec247
XL
128 }
129 impl Visitor<'_> for ImplTraitVisitor<'_> {
130 fn visit_ty(&mut self, ty: &ast::Ty) {
131 if let ast::TyKind::ImplTrait(..) = ty.kind {
353b0b11
FG
132 if self.in_associated_ty {
133 gate_feature_post!(
134 &self.vis,
135 impl_trait_in_assoc_type,
136 ty.span,
137 "`impl Trait` in associated types is unstable"
138 );
139 } else {
140 gate_feature_post!(
141 &self.vis,
142 type_alias_impl_trait,
143 ty.span,
144 "`impl Trait` in type aliases is unstable"
145 );
146 }
dfeec247
XL
147 }
148 visit::walk_ty(self, ty);
149 }
150 }
353b0b11 151 ImplTraitVisitor { vis: self, in_associated_ty }.visit_ty(ty);
dfeec247 152 }
9ffffee4
FG
153
154 fn check_late_bound_lifetime_defs(&self, params: &[ast::GenericParam]) {
155 // Check only lifetime parameters are present and that the lifetime
156 // parameters that are present have no bounds.
157 let non_lt_param_spans: Vec<_> = params
158 .iter()
159 .filter_map(|param| match param.kind {
160 ast::GenericParamKind::Lifetime { .. } => None,
161 _ => Some(param.ident.span),
162 })
163 .collect();
164 // FIXME: gate_feature_post doesn't really handle multispans...
165 if !non_lt_param_spans.is_empty() && !self.features.non_lifetime_binders {
166 feature_err(
167 &self.sess.parse_sess,
168 sym::non_lifetime_binders,
169 non_lt_param_spans,
170 crate::fluent_generated::ast_passes_forbidden_non_lifetime_param,
171 )
172 .emit();
173 }
174 for param in params {
175 if !param.bounds.is_empty() {
176 let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
177 self.sess.emit_err(errors::ForbiddenLifetimeBound { spans });
178 }
179 }
180 }
dfeec247
XL
181}
182
183impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
184 fn visit_attribute(&mut self, attr: &ast::Attribute) {
3c0e092e 185 let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
dfeec247 186 // Check feature gates for built-in attributes.
3c0e092e
XL
187 if let Some(BuiltinAttribute {
188 gate: AttributeGate::Gated(_, name, descr, has_feature),
189 ..
190 }) = attr_info
191 {
9ffffee4 192 gate_feature_fn!(self, has_feature, attr.span, *name, *descr);
dfeec247
XL
193 }
194 // Check unstable flavors of the `#[doc]` attribute.
94222f64 195 if attr.has_name(sym::doc) {
dfeec247
XL
196 for nested_meta in attr.meta_item_list().unwrap_or_default() {
197 macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
3dfed10e 198 $(if nested_meta.has_name(sym::$name) {
dfeec247
XL
199 let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental");
200 gate_feature_post!(self, $feature, attr.span, msg);
201 })*
202 }}
203
204 gate_doc!(
dfeec247 205 cfg => doc_cfg
c295e0f8 206 cfg_hide => doc_cfg_hide
dfeec247 207 masked => doc_masked
cdc7bbd5 208 notable_trait => doc_notable_trait
dfeec247 209 );
3c0e092e
XL
210
211 if nested_meta.has_name(sym::keyword) {
212 let msg = "`#[doc(keyword)]` is meant for internal use only";
213 gate_feature_post!(self, rustdoc_internals, attr.span, msg);
214 }
17df50a5 215
064997fb
FG
216 if nested_meta.has_name(sym::fake_variadic) {
217 let msg = "`#[doc(fake_variadic)]` is meant for internal use only";
923072b8 218 gate_feature_post!(self, rustdoc_internals, attr.span, msg);
17df50a5
XL
219 }
220 }
221 }
5e7ed085
FG
222
223 // Emit errors for non-staged-api crates.
224 if !self.features.staged_api {
923072b8 225 if attr.has_name(sym::unstable)
5e7ed085
FG
226 || attr.has_name(sym::stable)
227 || attr.has_name(sym::rustc_const_unstable)
228 || attr.has_name(sym::rustc_const_stable)
f2b60f7d 229 || attr.has_name(sym::rustc_default_body_unstable)
5e7ed085 230 {
9ffffee4 231 self.sess.emit_err(errors::StabilityOutsideStd { span: attr.span });
5e7ed085
FG
232 }
233 }
dfeec247
XL
234 }
235
dfeec247 236 fn visit_item(&mut self, i: &'a ast::Item) {
487cf647
FG
237 match &i.kind {
238 ast::ItemKind::ForeignMod(foreign_module) => {
dfeec247 239 if let Some(abi) = foreign_module.abi {
04454e1e 240 self.check_abi(abi, ast::Const::No);
dfeec247
XL
241 }
242 }
243
244 ast::ItemKind::Fn(..) => {
353b0b11 245 if attr::contains_name(&i.attrs, sym::start) {
dfeec247
XL
246 gate_feature_post!(
247 &self,
248 start,
249 i.span,
250 "`#[start]` functions are experimental \
ba9703b0
XL
251 and their signature may change \
252 over time"
dfeec247
XL
253 );
254 }
dfeec247
XL
255 }
256
257 ast::ItemKind::Struct(..) => {
353b0b11 258 for attr in attr::filter_by_name(&i.attrs, sym::repr) {
9ffffee4 259 for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
3dfed10e 260 if item.has_name(sym::simd) {
dfeec247
XL
261 gate_feature_post!(
262 &self,
263 repr_simd,
264 attr.span,
265 "SIMD types are experimental and possibly buggy"
266 );
267 }
268 }
269 }
270 }
271
487cf647
FG
272 ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, of_trait, .. }) => {
273 if let &ast::ImplPolarity::Negative(span) = polarity {
dfeec247
XL
274 gate_feature_post!(
275 &self,
ba9703b0 276 negative_impls,
5869c6ff 277 span.to(of_trait.as_ref().map_or(span, |t| t.path.span)),
dfeec247 278 "negative trait bounds are not yet fully implemented; \
ba9703b0 279 use marker types for now"
dfeec247
XL
280 );
281 }
282
74b04a01 283 if let ast::Defaultness::Default(_) = defaultness {
dfeec247
XL
284 gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
285 }
286 }
287
3c0e092e 288 ast::ItemKind::Trait(box ast::Trait { is_auto: ast::IsAuto::Yes, .. }) => {
dfeec247
XL
289 gate_feature_post!(
290 &self,
fc512014 291 auto_traits,
dfeec247
XL
292 i.span,
293 "auto traits are experimental and possibly buggy"
294 );
295 }
296
297 ast::ItemKind::TraitAlias(..) => {
298 gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental");
299 }
300
ba9703b0 301 ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => {
dfeec247
XL
302 let msg = "`macro` is experimental";
303 gate_feature_post!(&self, decl_macro, i.span, msg);
304 }
305
487cf647 306 ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
353b0b11 307 self.check_impl_trait(&ty, false)
5869c6ff 308 }
dfeec247
XL
309
310 _ => {}
311 }
312
313 visit::walk_item(self, i);
314 }
315
316 fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
317 match i.kind {
318 ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
353b0b11 319 let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
5869c6ff
XL
320 let links_to_llvm =
321 link_name.map_or(false, |val| val.as_str().starts_with("llvm."));
dfeec247
XL
322 if links_to_llvm {
323 gate_feature_post!(
324 &self,
325 link_llvm_intrinsics,
326 i.span,
327 "linking to LLVM intrinsics is experimental"
328 );
329 }
330 }
74b04a01 331 ast::ForeignItemKind::TyAlias(..) => {
dfeec247
XL
332 gate_feature_post!(&self, extern_types, i.span, "extern types are experimental");
333 }
ba9703b0 334 ast::ForeignItemKind::MacCall(..) => {}
dfeec247
XL
335 }
336
337 visit::walk_foreign_item(self, i)
338 }
339
340 fn visit_ty(&mut self, ty: &'a ast::Ty) {
487cf647
FG
341 match &ty.kind {
342 ast::TyKind::BareFn(bare_fn_ty) => {
04454e1e
FG
343 // Function pointers cannot be `const`
344 self.check_extern(bare_fn_ty.ext, ast::Const::No);
9ffffee4 345 self.check_late_bound_lifetime_defs(&bare_fn_ty.generic_params);
dfeec247
XL
346 }
347 ast::TyKind::Never => {
348 gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
349 }
350 _ => {}
351 }
352 visit::walk_ty(self, ty)
353 }
354
9ffffee4
FG
355 fn visit_generics(&mut self, g: &'a ast::Generics) {
356 for predicate in &g.where_clause.predicates {
357 match predicate {
358 ast::WherePredicate::BoundPredicate(bound_pred) => {
359 // A type binding, eg `for<'c> Foo: Send+Clone+'c`
360 self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
361 }
362 _ => {}
363 }
364 }
365 visit::walk_generics(self, g);
366 }
367
74b04a01 368 fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
487cf647 369 if let ast::FnRetTy::Ty(output_ty) = ret_ty {
dfeec247
XL
370 if let ast::TyKind::Never = output_ty.kind {
371 // Do nothing.
372 } else {
373 self.visit_ty(output_ty)
374 }
375 }
376 }
377
064997fb
FG
378 fn visit_stmt(&mut self, stmt: &'a ast::Stmt) {
379 if let ast::StmtKind::Semi(expr) = &stmt.kind
380 && let ast::ExprKind::Assign(lhs, _, _) = &expr.kind
381 && let ast::ExprKind::Type(..) = lhs.kind
382 && self.sess.parse_sess.span_diagnostic.err_count() == 0
383 && !self.features.type_ascription
384 && !lhs.span.allows_unstable(sym::type_ascription)
385 {
386 // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type
387 // ascription error, but the likely intention was to write a `let` statement. (#78907).
f2b60f7d 388 feature_err(
064997fb
FG
389 &self.sess.parse_sess,
390 sym::type_ascription,
391 lhs.span,
064997fb
FG
392 "type ascription is experimental",
393 ).span_suggestion_verbose(
394 lhs.span.shrink_to_lo(),
395 "you might have meant to introduce a new binding",
9ffffee4 396 "let ",
064997fb
FG
397 Applicability::MachineApplicable,
398 ).emit();
399 }
400 visit::walk_stmt(self, stmt);
401 }
402
dfeec247
XL
403 fn visit_expr(&mut self, e: &'a ast::Expr) {
404 match e.kind {
dfeec247 405 ast::ExprKind::Type(..) => {
3dfed10e 406 if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
f2b60f7d
FG
407 // To avoid noise about type ascription in common syntax errors,
408 // only emit if it is the *only* error.
dfeec247
XL
409 gate_feature_post!(
410 &self,
411 type_ascription,
412 e.span,
413 "type ascription is experimental"
414 );
f2b60f7d
FG
415 } else {
416 // And if it isn't, cancel the early-pass warning.
353b0b11
FG
417 if let Some(err) = self
418 .sess
f2b60f7d
FG
419 .parse_sess
420 .span_diagnostic
421 .steal_diagnostic(e.span, StashKey::EarlySyntaxWarning)
353b0b11
FG
422 {
423 err.cancel()
424 }
dfeec247
XL
425 }
426 }
427 ast::ExprKind::TryBlock(_) => {
428 gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
429 }
dfeec247
XL
430 _ => {}
431 }
432 visit::walk_expr(self, e)
433 }
434
435 fn visit_pat(&mut self, pattern: &'a ast::Pat) {
436 match &pattern.kind {
136023e0
XL
437 PatKind::Slice(pats) => {
438 for pat in pats {
439 let inner_pat = match &pat.kind {
440 PatKind::Ident(.., Some(pat)) => pat,
441 _ => pat,
442 };
443 if let PatKind::Range(Some(_), None, Spanned { .. }) = inner_pat.kind {
444 gate_feature_post!(
445 &self,
2b03887a 446 half_open_range_patterns_in_slices,
136023e0
XL
447 pat.span,
448 "`X..` patterns in slices are experimental"
449 );
450 }
451 }
452 }
dfeec247
XL
453 PatKind::Box(..) => {
454 gate_feature_post!(
455 &self,
456 box_patterns,
457 pattern.span,
458 "box pattern syntax is experimental"
459 );
460 }
136023e0 461 PatKind::Range(_, Some(_), Spanned { node: RangeEnd::Excluded, .. }) => {
dfeec247
XL
462 gate_feature_post!(
463 &self,
464 exclusive_range_pattern,
465 pattern.span,
466 "exclusive range pattern syntax is experimental"
467 );
468 }
469 _ => {}
470 }
471 visit::walk_pat(self, pattern)
472 }
473
9ffffee4
FG
474 fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
475 self.check_late_bound_lifetime_defs(&t.bound_generic_params);
476 visit::walk_poly_trait_ref(self, t);
477 }
478
74b04a01 479 fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
dfeec247 480 if let Some(header) = fn_kind.header() {
74b04a01 481 // Stability of const fn methods are covered in `visit_assoc_item` below.
04454e1e 482 self.check_extern(header.ext, header.constness);
dfeec247
XL
483 }
484
9ffffee4
FG
485 if let FnKind::Closure(ast::ClosureBinder::For { generic_params, .. }, ..) = fn_kind {
486 self.check_late_bound_lifetime_defs(generic_params);
487 }
488
74b04a01 489 if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
dfeec247
XL
490 gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
491 }
492
f2b60f7d 493 visit::walk_fn(self, fn_kind)
dfeec247
XL
494 }
495
5099ac24
FG
496 fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) {
497 if let AssocConstraintKind::Bound { .. } = constraint.kind {
353b0b11
FG
498 if let Some(ast::GenericArgs::Parenthesized(args)) = constraint.gen_args.as_ref()
499 && args.inputs.is_empty()
500 && matches!(args.output, ast::FnRetTy::Default(..))
501 {
502 gate_feature_post!(
503 &self,
504 return_type_notation,
505 constraint.span,
506 "return type notation is experimental"
507 );
508 } else {
509 gate_feature_post!(
510 &self,
511 associated_type_bounds,
512 constraint.span,
513 "associated type bounds are unstable"
514 );
515 }
dfeec247 516 }
5099ac24 517 visit::walk_assoc_constraint(self, constraint)
dfeec247
XL
518 }
519
74b04a01 520 fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
487cf647 521 let is_fn = match &i.kind {
cdc7bbd5 522 ast::AssocItemKind::Fn(_) => true,
487cf647 523 ast::AssocItemKind::Type(box ast::TyAlias { ty, .. }) => {
74b04a01 524 if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
dfeec247
XL
525 gate_feature_post!(
526 &self,
527 associated_type_defaults,
74b04a01 528 i.span,
dfeec247
XL
529 "associated type defaults are unstable"
530 );
531 }
dfeec247 532 if let Some(ty) = ty {
353b0b11 533 self.check_impl_trait(ty, true);
dfeec247 534 }
ba9703b0 535 false
dfeec247 536 }
ba9703b0
XL
537 _ => false,
538 };
539 if let ast::Defaultness::Default(_) = i.kind.defaultness() {
540 // Limit `min_specialization` to only specializing functions.
541 gate_feature_fn!(
542 &self,
543 |x: &Features| x.specialization || (is_fn && x.min_specialization),
544 i.span,
545 sym::specialization,
546 "specialization is unstable"
547 );
dfeec247 548 }
74b04a01 549 visit::walk_assoc_item(self, i, ctxt)
dfeec247 550 }
dfeec247
XL
551}
552
3dfed10e
XL
553pub fn check_crate(krate: &ast::Crate, sess: &Session) {
554 maybe_stage_features(sess, krate);
1b1a35ee 555 check_incompatible_features(sess);
3dfed10e 556 let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() };
dfeec247 557
3dfed10e 558 let spans = sess.parse_sess.gated_spans.spans.borrow();
dfeec247 559 macro_rules! gate_all {
5869c6ff
XL
560 ($gate:ident, $msg:literal, $help:literal) => {
561 if let Some(spans) = spans.get(&sym::$gate) {
562 for span in spans {
563 gate_feature_post!(&visitor, $gate, *span, $msg, $help);
564 }
565 }
566 };
dfeec247 567 ($gate:ident, $msg:literal) => {
3dfed10e
XL
568 if let Some(spans) = spans.get(&sym::$gate) {
569 for span in spans {
570 gate_feature_post!(&visitor, $gate, *span, $msg);
571 }
dfeec247
XL
572 }
573 };
574 }
6a06907d
XL
575 gate_all!(
576 if_let_guard,
577 "`if let` guards are experimental",
578 "you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
579 );
5099ac24 580 gate_all!(let_chains, "`let` expressions in this position are unstable");
5869c6ff
XL
581 gate_all!(
582 async_closure,
583 "async closures are unstable",
584 "to use an async block, remove the `||`: `async {`"
585 );
064997fb
FG
586 gate_all!(
587 closure_lifetime_binder,
588 "`for<...>` binders for closures are experimental",
589 "consider removing `for<...>`"
590 );
17df50a5 591 gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
dfeec247 592 gate_all!(generators, "yield syntax is experimental");
dfeec247 593 gate_all!(raw_ref_op, "raw address of syntax is experimental");
dfeec247 594 gate_all!(const_trait_impl, "const trait impls are experimental");
2b03887a
FG
595 gate_all!(
596 half_open_range_patterns_in_slices,
597 "half-open range patterns in slices are unstable"
598 );
29967ef6 599 gate_all!(inline_const, "inline-const is experimental");
3c0e092e 600 gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
5099ac24 601 gate_all!(associated_const_equality, "associated const equality is incomplete");
04454e1e 602 gate_all!(yeet_expr, "`do yeet` expression is experimental");
353b0b11
FG
603 gate_all!(dyn_star, "`dyn*` trait objects are experimental");
604 gate_all!(const_closures, "const closures are experimental");
dfeec247
XL
605
606 // All uses of `gate_all!` below this point were added in #65742,
607 // and subsequently disabled (with the non-early gating readded).
f2b60f7d
FG
608 // We emit an early future-incompatible warning for these.
609 // New syntax gates should go above here to get a hard error gate.
dfeec247
XL
610 macro_rules! gate_all {
611 ($gate:ident, $msg:literal) => {
f2b60f7d
FG
612 for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
613 gate_feature_post!(future_incompatible; &visitor, $gate, *span, $msg);
dfeec247
XL
614 }
615 };
616 }
617
618 gate_all!(trait_alias, "trait aliases are experimental");
619 gate_all!(associated_type_bounds, "associated type bounds are unstable");
353b0b11 620 gate_all!(return_type_notation, "return type notation is experimental");
dfeec247
XL
621 gate_all!(decl_macro, "`macro` is experimental");
622 gate_all!(box_patterns, "box pattern syntax is experimental");
623 gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
624 gate_all!(try_blocks, "`try` blocks are unstable");
f2b60f7d 625 gate_all!(type_ascription, "type ascription is experimental");
dfeec247
XL
626
627 visit::walk_crate(&mut visitor, krate);
628}
629
3dfed10e 630fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
c295e0f8
XL
631 // checks if `#![feature]` has been used to enable any lang feature
632 // does not check the same for lib features unless there's at least one
633 // declared lang feature
3dfed10e 634 if !sess.opts.unstable_features.is_nightly_build() {
cdc7bbd5 635 let lang_features = &sess.features_untracked().declared_lang_features;
c295e0f8
XL
636 if lang_features.len() == 0 {
637 return;
638 }
94222f64 639 for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
9ffffee4
FG
640 let mut err = errors::FeatureOnNonNightly {
641 span: attr.span,
642 channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
643 stable_features: vec![],
644 sugg: None,
645 };
646
cdc7bbd5
XL
647 let mut all_stable = true;
648 for ident in
5099ac24 649 attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident())
cdc7bbd5
XL
650 {
651 let name = ident.name;
652 let stable_since = lang_features
653 .iter()
654 .flat_map(|&(feature, _, since)| if feature == name { since } else { None })
655 .next();
656 if let Some(since) = stable_since {
9ffffee4 657 err.stable_features.push(errors::StableFeature { name, since });
cdc7bbd5
XL
658 } else {
659 all_stable = false;
660 }
661 }
662 if all_stable {
9ffffee4 663 err.sugg = Some(attr.span);
cdc7bbd5 664 }
9ffffee4 665 sess.parse_sess.span_diagnostic.emit_err(err);
dfeec247
XL
666 }
667 }
668}
1b1a35ee
XL
669
670fn check_incompatible_features(sess: &Session) {
671 let features = sess.features_untracked();
672
673 let declared_features = features
674 .declared_lang_features
675 .iter()
676 .copied()
677 .map(|(name, span, _)| (name, span))
678 .chain(features.declared_lib_features.iter().copied());
679
680 for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
681 .iter()
682 .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
683 {
684 if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
685 if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
686 {
687 let spans = vec![f1_span, f2_span];
9ffffee4 688 sess.emit_err(errors::IncompatibleFeatures { spans, f1: f1_name, f2: f2_name });
1b1a35ee
XL
689 }
690 }
691 }
692}