@@ -2,8 +2,8 @@ use std::num::NonZero;
22
33use rustc_errors:: ErrorGuaranteed ;
44use rustc_hir:: {
5- DefaultBodyStability , MethodKind , PartialConstStability , Stability , StabilityLevel ,
6- StableSince , Target , UnstableReason , VERSION_PLACEHOLDER ,
5+ DefaultBodyStability , MethodKind , PartialConstStability , RemovedFeature , Stability ,
6+ StabilityLevel , StableSince , Target , UnstableReason , VERSION_PLACEHOLDER ,
77} ;
88
99use super :: prelude:: * ;
@@ -471,3 +471,149 @@ pub(crate) fn parse_unstability<S: Stage>(
471471 ( Err ( ErrorGuaranteed { .. } ) , _) | ( _, Err ( ErrorGuaranteed { .. } ) ) => None ,
472472 }
473473}
474+
475+ #[ allow( dead_code) ]
476+ #[ derive( Default ) ]
477+ pub ( crate ) struct RemovedFeatureParser {
478+ removed : Option < ( RemovedFeature , Span ) > ,
479+ }
480+
481+ impl < S : Stage > AttributeParser < S > for RemovedFeatureParser {
482+ const ATTRIBUTES : AcceptMapping < Self , S > = & [ (
483+ & [ sym:: unstable_removed] ,
484+ template ! ( List : & [ r#"feature = "name", since = "1.2.3", reason = "...", issue = "N|none""# ] ) ,
485+ |this, cx, args| {
486+ // same staged-api restriction as other stability attrs
487+ reject_outside_std ! ( cx) ;
488+
489+ let ArgParser :: List ( list) = args else {
490+ cx. expected_list ( cx. attr_span ) ;
491+ return ;
492+ } ;
493+
494+ let mut feature_opt: Option < Symbol > = None ;
495+ let mut reason_opt: Option < Symbol > = None ;
496+ let mut since_opt: Option < StableSince > = None ;
497+
498+ for param in list. mixed ( ) {
499+ let param_span = param. span ( ) ;
500+ let Some ( param) = param. meta_item ( ) else {
501+ cx. emit_err ( session_diagnostics:: UnsupportedLiteral {
502+ span : param_span,
503+ reason : UnsupportedLiteralReason :: Generic ,
504+ is_bytestr : false ,
505+ start_point_span : cx. sess ( ) . source_map ( ) . start_point ( param_span) ,
506+ } ) ;
507+ return ;
508+ } ;
509+
510+ let word = param. path ( ) . word ( ) ;
511+ match word. map ( |i| i. name ) {
512+ Some ( sym:: feature) => {
513+ if insert_value_into_option_or_error (
514+ cx,
515+ & param,
516+ & mut feature_opt,
517+ word. unwrap ( ) ,
518+ )
519+ . is_none ( )
520+ {
521+ return ;
522+ }
523+ }
524+ Some ( sym:: reason) => {
525+ if insert_value_into_option_or_error (
526+ cx,
527+ & param,
528+ & mut reason_opt,
529+ word. unwrap ( ) ,
530+ )
531+ . is_none ( )
532+ {
533+ return ;
534+ }
535+ }
536+ Some ( sym:: since) => {
537+ if insert_value_into_option_or_error (
538+ cx,
539+ & param,
540+ & mut None :: < Symbol > ,
541+ word. unwrap ( ) ,
542+ )
543+ . is_none ( )
544+ {
545+ return ;
546+ }
547+ // parse version string
548+ if let Some ( s) = param. args ( ) . name_value ( ) . and_then ( |nv| nv. value_as_str ( ) )
549+ {
550+ if s. as_str ( ) == VERSION_PLACEHOLDER {
551+ since_opt = Some ( StableSince :: Current ) ;
552+ } else if let Some ( v) = super :: util:: parse_version ( s) {
553+ since_opt = Some ( StableSince :: Version ( v) ) ;
554+ } else {
555+ let err = cx. emit_err ( session_diagnostics:: InvalidSince {
556+ span : cx. attr_span ,
557+ } ) ;
558+ since_opt = Some ( StableSince :: Err ( err) ) ;
559+ }
560+ } else {
561+ cx. expected_name_value ( param. span ( ) , Some ( word. unwrap ( ) . name ) ) ;
562+ return ;
563+ }
564+ }
565+ _ => {
566+ cx. emit_err ( session_diagnostics:: UnknownMetaItem {
567+ span : param_span,
568+ item : param. path ( ) . to_string ( ) ,
569+ expected : & [ "feature" , "reason" , "since" ] ,
570+ } ) ;
571+ return ;
572+ }
573+ }
574+ }
575+
576+ // Validate presence and form of fields:
577+ let feature = match feature_opt {
578+ Some ( f) if rustc_lexer:: is_ident ( f. as_str ( ) ) => f,
579+ Some ( _) => {
580+ cx. emit_err ( session_diagnostics:: NonIdentFeature { span : cx. attr_span } ) ;
581+ return ;
582+ }
583+ None => {
584+ cx. emit_err ( session_diagnostics:: MissingFeature { span : cx. attr_span } ) ;
585+ return ;
586+ }
587+ } ;
588+
589+ let since = match since_opt {
590+ Some ( s) => s,
591+ None => {
592+ cx. emit_err ( session_diagnostics:: MissingSince { span : cx. attr_span } ) ;
593+ return ;
594+ }
595+ } ;
596+
597+ let since_symbol = match since {
598+ StableSince :: Version ( v) => Symbol :: intern ( & v. to_string ( ) ) , // dynamic string — ok
599+ StableSince :: Current => sym:: current,
600+ StableSince :: Err ( _) => sym:: err,
601+ } ;
602+
603+ let removed = RemovedFeature {
604+ feature,
605+ reason : reason_opt. unwrap_or_else ( || sym:: no_reason_given) ,
606+ since : since_symbol,
607+ span : cx. attr_span ,
608+ } ;
609+
610+ this. removed = Some ( ( removed, cx. attr_span ) ) ;
611+ } ,
612+ ) ] ;
613+
614+ const ALLOWED_TARGETS : AllowedTargets = AllowedTargets :: AllowList ( & [ Allow ( Target :: Crate ) ] ) ;
615+
616+ fn finalize ( self , _cx : & FinalizeContext < ' _ , ' _ , S > ) -> Option < AttributeKind > {
617+ self . removed . map ( |( removed_feature, _span) | AttributeKind :: RemovedFeature ( removed_feature) )
618+ }
619+ }
0 commit comments