11#![ allow( warnings) ]
2-
32use std:: ops:: Range ;
43
5- use rustc_hir:: attrs:: diagnostic:: { FormatArg , FormatString , Piece } ;
4+ use rustc_errors:: E0232 ;
5+ use rustc_hir:: AttrPath ;
6+ use rustc_hir:: attrs:: diagnostic:: {
7+ FilterFormatString , Flag , FormatArg , FormatString , LitOrArg , Name , NameValue ,
8+ OnUnimplementedCondition , Piece , Predicate ,
9+ } ;
610use rustc_hir:: lints:: FormatWarning ;
11+ use rustc_macros:: Diagnostic ;
712use rustc_parse_format:: {
813 Argument , FormatSpec , ParseError , ParseMode , Parser , Piece as RpfPiece , Position ,
914} ;
10- use rustc_span:: { InnerSpan , Span , Symbol , kw, sym} ;
15+ use rustc_span:: { Ident , InnerSpan , Span , Symbol , kw, sym} ;
16+ use thin_vec:: ThinVec ;
17+
18+ use crate :: parser:: { ArgParser , MetaItemListParser , MetaItemOrLitParser , MetaItemParser } ;
1119
1220pub mod on_unimplemented;
1321
1422#[ derive( Copy , Clone ) ]
15- pub ( crate ) enum Ctx {
23+ pub ( crate ) enum Mode {
1624 // `#[rustc_on_unimplemented]`
1725 RustcOnUnimplemented ,
1826 // `#[diagnostic::...]`
@@ -23,7 +31,7 @@ pub(crate) fn parse_format_string(
2331 input : Symbol ,
2432 snippet : Option < String > ,
2533 span : Span ,
26- ctx : Ctx ,
34+ mode : Mode ,
2735) -> Result < ( FormatString , Vec < FormatWarning > ) , ParseError > {
2836 let s = input. as_str ( ) ;
2937 let mut parser = Parser :: new ( s, None , snippet, false , ParseMode :: Diagnostic ) ;
@@ -40,7 +48,7 @@ pub(crate) fn parse_format_string(
4048 RpfPiece :: Lit ( lit) => Piece :: Lit ( Symbol :: intern ( lit) ) ,
4149 RpfPiece :: NextArgument ( arg) => {
4250 warn_on_format_spec ( & arg. format , & mut warnings, span, parser. is_source_literal ) ;
43- let arg = parse_arg ( & arg, ctx , & mut warnings, span, parser. is_source_literal ) ;
51+ let arg = parse_arg ( & arg, mode , & mut warnings, span, parser. is_source_literal ) ;
4452 Piece :: Arg ( arg)
4553 }
4654 } )
@@ -51,7 +59,7 @@ pub(crate) fn parse_format_string(
5159
5260fn parse_arg (
5361 arg : & Argument < ' _ > ,
54- ctx : Ctx ,
62+ mode : Mode ,
5563 warnings : & mut Vec < FormatWarning > ,
5664 input_span : Span ,
5765 is_source_literal : bool ,
@@ -60,18 +68,18 @@ fn parse_arg(
6068
6169 match arg. position {
6270 // Something like "hello {name}"
63- Position :: ArgumentNamed ( name) => match ( ctx , Symbol :: intern ( name) ) {
71+ Position :: ArgumentNamed ( name) => match ( mode , Symbol :: intern ( name) ) {
6472 // Only `#[rustc_on_unimplemented]` can use these
65- ( Ctx :: RustcOnUnimplemented { .. } , sym:: ItemContext ) => FormatArg :: ItemContext ,
66- ( Ctx :: RustcOnUnimplemented { .. } , sym:: This ) => FormatArg :: This ,
67- ( Ctx :: RustcOnUnimplemented { .. } , sym:: Trait ) => FormatArg :: Trait ,
73+ ( Mode :: RustcOnUnimplemented { .. } , sym:: ItemContext ) => FormatArg :: ItemContext ,
74+ ( Mode :: RustcOnUnimplemented { .. } , sym:: This ) => FormatArg :: This ,
75+ ( Mode :: RustcOnUnimplemented { .. } , sym:: Trait ) => FormatArg :: Trait ,
6876 // Any attribute can use these
6977 (
70- Ctx :: RustcOnUnimplemented { .. } | Ctx :: DiagnosticOnUnimplemented { .. } ,
78+ Mode :: RustcOnUnimplemented { .. } | Mode :: DiagnosticOnUnimplemented { .. } ,
7179 kw:: SelfUpper ,
7280 ) => FormatArg :: SelfUpper ,
7381 (
74- Ctx :: RustcOnUnimplemented { .. } | Ctx :: DiagnosticOnUnimplemented { .. } ,
82+ Mode :: RustcOnUnimplemented { .. } | Mode :: DiagnosticOnUnimplemented { .. } ,
7583 generic_param,
7684 ) => FormatArg :: GenericParam { generic_param, span } ,
7785 } ,
@@ -115,3 +123,175 @@ fn warn_on_format_spec(
115123fn slice_span ( input : Span , Range { start, end } : Range < usize > , is_source_literal : bool ) -> Span {
116124 if is_source_literal { input. from_inner ( InnerSpan { start, end } ) } else { input }
117125}
126+
127+ pub ( crate ) fn parse_condition (
128+ input : & MetaItemOrLitParser ,
129+ ) -> Result < OnUnimplementedCondition , InvalidOnClause > {
130+ let span = input. span ( ) ;
131+ let pred = parse_predicate ( input) ?;
132+ Ok ( OnUnimplementedCondition { span, pred } )
133+ }
134+
135+ fn parse_predicate ( input : & MetaItemOrLitParser ) -> Result < Predicate , InvalidOnClause > {
136+ let Some ( meta_item) = input. meta_item ( ) else {
137+ return Err ( InvalidOnClause :: UnsupportedLiteral { span : input. span ( ) } ) ;
138+ } ;
139+
140+ let Some ( predicate) = meta_item. ident ( ) else {
141+ return Err ( InvalidOnClause :: ExpectedIdentifier {
142+ span : meta_item. path ( ) . span ( ) ,
143+ path : meta_item. path ( ) . get_attribute_path ( ) ,
144+ } ) ;
145+ } ;
146+
147+ match meta_item. args ( ) {
148+ ArgParser :: List ( mis) => match predicate. name {
149+ sym:: any => Ok ( Predicate :: Any ( parse_predicate_sequence ( mis) ?) ) ,
150+ sym:: all => Ok ( Predicate :: All ( parse_predicate_sequence ( mis) ?) ) ,
151+ sym:: not => {
152+ if let Some ( single) = mis. single ( ) {
153+ Ok ( Predicate :: Not ( Box :: new ( parse_predicate ( single) ?) ) )
154+ } else {
155+ Err ( InvalidOnClause :: ExpectedOnePredInNot { span : mis. span } )
156+ }
157+ }
158+ invalid_pred => {
159+ Err ( InvalidOnClause :: InvalidPredicate { span : predicate. span , invalid_pred } )
160+ }
161+ } ,
162+ ArgParser :: NameValue ( p) => {
163+ let Some ( value) = p. value_as_ident ( ) else {
164+ return Err ( InvalidOnClause :: UnsupportedLiteral { span : p. args_span ( ) } ) ;
165+ } ;
166+ let name = parse_name ( predicate) ;
167+ let value = parse_filter ( value. name ) ;
168+ let kv = NameValue { name, value } ;
169+ Ok ( Predicate :: Match ( kv) )
170+ }
171+ ArgParser :: NoArgs => {
172+ let flag = parse_flag ( predicate) ?;
173+ Ok ( Predicate :: Flag ( flag) )
174+ }
175+ }
176+ }
177+
178+ fn parse_predicate_sequence (
179+ sequence : & MetaItemListParser ,
180+ ) -> Result < ThinVec < Predicate > , InvalidOnClause > {
181+ sequence. mixed ( ) . map ( parse_predicate) . collect ( )
182+ }
183+
184+ fn parse_flag ( Ident { name, span } : Ident ) -> Result < Flag , InvalidOnClause > {
185+ match name {
186+ sym:: crate_local => Ok ( Flag :: CrateLocal ) ,
187+ sym:: direct => Ok ( Flag :: Direct ) ,
188+ sym:: from_desugaring => Ok ( Flag :: FromDesugaring ) ,
189+ invalid_flag => Err ( InvalidOnClause :: InvalidFlag { invalid_flag, span } ) ,
190+ }
191+ }
192+
193+ fn parse_name ( Ident { name, span } : Ident ) -> Name {
194+ match name {
195+ kw:: SelfUpper => Name :: SelfUpper ,
196+ sym:: from_desugaring => Name :: FromDesugaring ,
197+ sym:: cause => Name :: Cause ,
198+ generic => Name :: GenericArg ( generic) ,
199+ }
200+ }
201+
202+ fn parse_filter ( input : Symbol ) -> FilterFormatString {
203+ let pieces = Parser :: new ( input. as_str ( ) , None , None , false , ParseMode :: Diagnostic )
204+ . map ( |p| match p {
205+ RpfPiece :: Lit ( s) => LitOrArg :: Lit ( Symbol :: intern ( s) ) ,
206+ // We just ignore formatspecs here
207+ RpfPiece :: NextArgument ( a) => match a. position {
208+ // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even
209+ // if the integer type has been resolved, to allow targeting all integers.
210+ // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet,
211+ // from the `Display` impl of `InferTy` to be precise.
212+ //
213+ // Don't try to format these later!
214+ Position :: ArgumentNamed ( arg @ "integer" | arg @ "integral" | arg @ "float" ) => {
215+ LitOrArg :: Lit ( Symbol :: intern ( & format ! ( "{{{arg}}}" ) ) )
216+ }
217+
218+ // FIXME(mejrs) We should check if these correspond to a generic of the trait.
219+ Position :: ArgumentNamed ( arg) => LitOrArg :: Arg ( Symbol :: intern ( arg) ) ,
220+
221+ // FIXME(mejrs) These should really be warnings/errors
222+ Position :: ArgumentImplicitlyIs ( _) => LitOrArg :: Lit ( sym:: empty_braces) ,
223+ Position :: ArgumentIs ( idx) => LitOrArg :: Lit ( Symbol :: intern ( & format ! ( "{{{idx}}}" ) ) ) ,
224+ } ,
225+ } )
226+ . collect ( ) ;
227+ FilterFormatString { pieces }
228+ }
229+
230+ #[ derive( Diagnostic ) ]
231+ pub ( crate ) enum InvalidOnClause {
232+ #[ diag( "empty `on`-clause in `#[rustc_on_unimplemented]`" , code = E0232 ) ]
233+ Empty {
234+ #[ primary_span]
235+ #[ label( "empty `on`-clause here" ) ]
236+ span : Span ,
237+ } ,
238+ #[ diag( "expected a single predicate in `not(..)`" , code = E0232 ) ]
239+ ExpectedOnePredInNot {
240+ #[ primary_span]
241+ #[ label( "unexpected quantity of predicates here" ) ]
242+ span : Span ,
243+ } ,
244+ #[ diag( "literals inside `on`-clauses are not supported" , code = E0232 ) ]
245+ UnsupportedLiteral {
246+ #[ primary_span]
247+ #[ label( "unexpected literal here" ) ]
248+ span : Span ,
249+ } ,
250+ #[ diag( "expected an identifier inside this `on`-clause" , code = E0232 ) ]
251+ ExpectedIdentifier {
252+ #[ primary_span]
253+ #[ label( "expected an identifier here, not `{$path}`" ) ]
254+ span : Span ,
255+ path : AttrPath ,
256+ } ,
257+ #[ diag( "this predicate is invalid" , code = E0232 ) ]
258+ InvalidPredicate {
259+ #[ primary_span]
260+ #[ label( "expected one of `any`, `all` or `not` here, not `{$invalid_pred}`" ) ]
261+ span : Span ,
262+ invalid_pred : Symbol ,
263+ } ,
264+ #[ diag( "invalid flag in `on`-clause" , code = E0232 ) ]
265+ InvalidFlag {
266+ #[ primary_span]
267+ #[ label(
268+ "expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`"
269+ ) ]
270+ span : Span ,
271+ invalid_flag : Symbol ,
272+ } ,
273+ #[ diag( "invalid name in `on`-clause" , code = E0232 ) ]
274+ InvalidName {
275+ #[ primary_span]
276+ #[ label(
277+ "expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}`"
278+ ) ]
279+ span : Span ,
280+ invalid_name : Symbol ,
281+ } ,
282+ }
283+
284+ #[ derive( Diagnostic ) ]
285+ #[ diag( "this attribute must have a value" , code = E0232 ) ]
286+ #[ note( "e.g. `#[rustc_on_unimplemented(message=\" foo\" )]`" ) ]
287+ pub struct NoValueInOnUnimplemented {
288+ #[ primary_span]
289+ #[ label( "expected value here" ) ]
290+ pub span : Span ,
291+ }
292+
293+ #[ derive( Diagnostic ) ]
294+ #[ diag(
295+ "using multiple `rustc_on_unimplemented` (or mixing it with `diagnostic::on_unimplemented`) is not supported"
296+ ) ]
297+ pub struct DupesNotAllowed ;
0 commit comments