@@ -7,14 +7,16 @@ use std::iter;
77use std:: ops:: ControlFlow ;
88
99use either:: Either ;
10- use hir:: { ClosureKind , Path } ;
10+ use hir:: { AttrArgs , Attribute , ClosureKind , Path } ;
11+ use rustc_ast:: { LitKind , MetaItem , MetaItemInner , MetaItemKind , MetaItemLit } ;
1112use rustc_data_structures:: fx:: FxIndexSet ;
1213use rustc_errors:: codes:: * ;
1314use rustc_errors:: { Applicability , Diag , MultiSpan , struct_span_code_err} ;
1415use rustc_hir as hir;
1516use rustc_hir:: def:: { DefKind , Res } ;
1617use rustc_hir:: intravisit:: { Visitor , walk_block, walk_expr} ;
1718use rustc_hir:: { CoroutineDesugaring , CoroutineKind , CoroutineSource , LangItem , PatField } ;
19+ use rustc_macros:: LintDiagnostic ;
1820use rustc_middle:: bug;
1921use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
2022use rustc_middle:: mir:: {
@@ -29,9 +31,13 @@ use rustc_middle::ty::{
2931 suggest_constraining_type_params,
3032} ;
3133use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
34+ use rustc_parse_format:: { ParseMode , Parser , Piece , Position } ;
35+ use rustc_session:: lint:: builtin:: {
36+ MALFORMED_DIAGNOSTIC_ATTRIBUTES , MALFORMED_DIAGNOSTIC_FORMAT_LITERALS ,
37+ } ;
3238use rustc_span:: def_id:: { DefId , LocalDefId } ;
3339use rustc_span:: hygiene:: DesugaringKind ;
34- use rustc_span:: { BytePos , Ident , Span , Symbol , kw, sym} ;
40+ use rustc_span:: { BytePos , Ident , InnerSpan , Span , Symbol , kw, sym} ;
3541use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
3642use rustc_trait_selection:: error_reporting:: traits:: FindExprBySpan ;
3743use rustc_trait_selection:: error_reporting:: traits:: call_kind:: CallKind ;
@@ -69,6 +75,148 @@ enum StorageDeadOrDrop<'tcx> {
6975 Destructor ( Ty < ' tcx > ) ,
7076}
7177
78+ #[ derive( Debug ) ]
79+ struct OnMoveDirective {
80+ message : Option < ( Span , String ) > ,
81+ label : Option < ( Span , String ) > ,
82+ }
83+
84+ #[ derive( LintDiagnostic ) ]
85+ #[ diag( borrowck_unknown_format_parameter_for_on_move_attr) ]
86+ #[ help]
87+ struct UnknownFormatParameterForOnMoveAttr {
88+ argument_name : String ,
89+ }
90+
91+ #[ derive( LintDiagnostic ) ]
92+ #[ diag( borrowck_malformed_on_move_attr) ]
93+ #[ help]
94+ struct MalformedOnMoveAttr {
95+ #[ label]
96+ span : Span ,
97+ }
98+
99+ #[ derive( LintDiagnostic ) ]
100+ #[ diag( borrowck_missing_options_for_on_move_attr) ]
101+ #[ help]
102+ struct MissingOptionsForOnMoveAttr ;
103+
104+ #[ derive( LintDiagnostic ) ]
105+ #[ diag( borrowck_ignored_diagnostic_option) ]
106+ struct IgnoredDiagnosticOption {
107+ option_name : & ' static str ,
108+ #[ label]
109+ span : Span ,
110+ #[ label( borrowck_other_label) ]
111+ prev_span : Span ,
112+ }
113+ impl IgnoredDiagnosticOption {
114+ fn maybe_emit_warning < ' tcx > (
115+ tcx : TyCtxt < ' tcx > ,
116+ item_def_id : DefId ,
117+ new : Option < Span > ,
118+ old : Option < Span > ,
119+ option_name : & ' static str ,
120+ ) {
121+ if let ( Some ( new_item) , Some ( old_item) ) = ( new, old)
122+ && let Some ( item_def_id) = item_def_id. as_local ( )
123+ {
124+ tcx. emit_node_span_lint (
125+ MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
126+ tcx. local_def_id_to_hir_id ( item_def_id) ,
127+ new_item,
128+ IgnoredDiagnosticOption { span : new_item, prev_span : old_item, option_name } ,
129+ ) ;
130+ }
131+ }
132+ }
133+
134+ impl < ' tcx > OnMoveDirective {
135+ fn new ( ) -> Self {
136+ Self { message : None , label : None }
137+ }
138+ fn parse ( tcx : TyCtxt < ' tcx > , item_def_id : DefId , items : & [ MetaItemInner ] ) -> Self {
139+ let get_value_and_span = |item : & _ , key| {
140+ if let MetaItemInner :: MetaItem ( MetaItem {
141+ path,
142+ kind : MetaItemKind :: NameValue ( MetaItemLit { span, kind : LitKind :: Str ( s, _) , .. } ) ,
143+ ..
144+ } ) = item
145+ && * path == key
146+ {
147+ Some ( ( * s, * span) )
148+ } else {
149+ None
150+ }
151+ } ;
152+ let parse_value = |value : Symbol , span : Span | {
153+ let mut parser = Parser :: new ( value. as_str ( ) , None , None , false , ParseMode :: Diagnostic ) ;
154+ let mut value = String :: new ( ) ;
155+ let pieces: Vec < _ > = parser. by_ref ( ) . collect ( ) ;
156+
157+ for piece in pieces {
158+ match piece {
159+ Piece :: Lit ( lit) => value. push_str ( lit) ,
160+ Piece :: NextArgument ( arg) => match arg. position {
161+ Position :: ArgumentNamed ( name) if name == kw:: SelfUpper . as_str ( ) => {
162+ let slf = match tcx. opt_item_name ( item_def_id) {
163+ Some ( name) => name. to_string ( ) ,
164+ None => "Self" . to_string ( ) ,
165+ } ;
166+ value. push_str ( & slf) ;
167+ }
168+ Position :: ArgumentNamed ( name) => {
169+ if let Some ( item_def_id) = item_def_id. as_local ( ) {
170+ let span = span. from_inner ( InnerSpan {
171+ start : arg. position_span . start ,
172+ end : arg. position_span . end ,
173+ } ) ;
174+ tcx. emit_node_span_lint (
175+ MALFORMED_DIAGNOSTIC_FORMAT_LITERALS ,
176+ tcx. local_def_id_to_hir_id ( item_def_id) ,
177+ span,
178+ UnknownFormatParameterForOnMoveAttr {
179+ argument_name : name. to_string ( ) ,
180+ } ,
181+ ) ;
182+ }
183+ value. push_str ( format ! ( "{{{name}}}" ) . as_str ( ) ) ;
184+ }
185+ _ => { }
186+ } ,
187+ }
188+ }
189+ value
190+ } ;
191+ let mut message = None ;
192+ let mut label = None ;
193+ for item in items {
194+ if let Some ( ( message_, span) ) = get_value_and_span ( item, sym:: message)
195+ && message. is_none ( )
196+ {
197+ let value = parse_value ( message_, span) ;
198+ message = Some ( ( item. span ( ) , value) ) ;
199+ continue ;
200+ } else if let Some ( ( label_, span) ) = get_value_and_span ( item, sym:: label)
201+ && label. is_none ( )
202+ {
203+ let value = parse_value ( label_, span) ;
204+ label = Some ( ( item. span ( ) , value) ) ;
205+ continue ;
206+ }
207+ if let Some ( def_id) = item_def_id. as_local ( ) {
208+ tcx. emit_node_span_lint (
209+ MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
210+ tcx. local_def_id_to_hir_id ( def_id) ,
211+ vec ! [ item. span( ) ] ,
212+ MalformedOnMoveAttr { span : item. span ( ) } ,
213+ ) ;
214+ }
215+ }
216+ Self { message, label }
217+ }
218+ }
219+
72220impl < ' infcx , ' tcx > MirBorrowckCtxt < ' _ , ' infcx , ' tcx > {
73221 pub ( crate ) fn report_use_of_moved_or_uninitialized (
74222 & mut self ,
@@ -141,6 +289,75 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
141289 let partial_str = if is_partial_move { "partial " } else { "" } ;
142290 let partially_str = if is_partial_move { "partially " } else { "" } ;
143291
292+ let mut on_move_directive = if let ty:: Adt ( item_def, _) =
293+ self . body . local_decls [ moved_place. local ] . ty . kind ( )
294+ {
295+ self . infcx
296+ . tcx
297+ . get_attrs_by_path ( item_def. did ( ) , & [ sym:: diagnostic, sym:: on_move] )
298+ . fold ( None , |acc : Option < OnMoveDirective > , attr| {
299+ if let Some ( items) = attr. meta_item_list ( ) {
300+ let directive =
301+ OnMoveDirective :: parse ( self . infcx . tcx , item_def. did ( ) , & items) ;
302+ if let Some ( acc) = acc {
303+ IgnoredDiagnosticOption :: maybe_emit_warning (
304+ self . infcx . tcx ,
305+ item_def. did ( ) ,
306+ directive. message . as_ref ( ) . map ( |attr| attr. 0 ) ,
307+ acc. message . as_ref ( ) . map ( |attr| attr. 0 ) ,
308+ "message" ,
309+ ) ;
310+
311+ IgnoredDiagnosticOption :: maybe_emit_warning (
312+ self . infcx . tcx ,
313+ item_def. did ( ) ,
314+ directive. label . as_ref ( ) . map ( |attr| attr. 0 ) ,
315+ acc. label . as_ref ( ) . map ( |attr| attr. 0 ) ,
316+ "label" ,
317+ ) ;
318+
319+ Some ( acc)
320+ } else {
321+ Some ( directive)
322+ }
323+ } else {
324+ match attr {
325+ Attribute :: Unparsed ( p) if !matches ! ( p. args, AttrArgs :: Empty ) => {
326+ if let Some ( item_def_id) = item_def. did ( ) . as_local ( ) {
327+ self . infcx . tcx . emit_node_span_lint (
328+ MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
329+ self . infcx . tcx . local_def_id_to_hir_id ( item_def_id) ,
330+ attr. span ( ) ,
331+ MalformedOnMoveAttr { span : attr. span ( ) } ,
332+ ) ;
333+ }
334+ }
335+ _ => {
336+ if let Some ( item_def_id) = item_def. did ( ) . as_local ( ) {
337+ self . infcx . tcx . emit_node_span_lint (
338+ MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
339+ self . infcx . tcx . local_def_id_to_hir_id ( item_def_id) ,
340+ attr. span ( ) ,
341+ MissingOptionsForOnMoveAttr ,
342+ )
343+ }
344+ }
345+ }
346+ Some ( OnMoveDirective :: new ( ) )
347+ }
348+ } )
349+ } else {
350+ None
351+ } ;
352+
353+ let message = if let Some ( on_move_directive) = & mut on_move_directive
354+ && let Some ( message) = on_move_directive. message . take ( )
355+ {
356+ Some ( message. 1 )
357+ } else {
358+ None
359+ } ;
360+
144361 let mut err = self . cannot_act_on_moved_value (
145362 span,
146363 desired_action. as_noun ( ) ,
@@ -149,6 +366,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
149366 moved_place,
150367 DescribePlaceOpt { including_downcast : true , including_tuple_field : true } ,
151368 ) ,
369+ message,
152370 ) ;
153371
154372 let reinit_spans = maybe_reinitialized_locations
@@ -278,12 +496,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
278496 if needs_note {
279497 if let Some ( local) = place. as_local ( ) {
280498 let span = self . body . local_decls [ local] . source_info . span ;
281- err. subdiagnostic ( crate :: session_diagnostics:: TypeNoCopy :: Label {
282- is_partial_move,
283- ty,
284- place : & note_msg,
285- span,
286- } ) ;
499+ if let Some ( on_move_directive) = on_move_directive
500+ && let Some ( label) = on_move_directive. label
501+ {
502+ err. span_label ( span, label. 1 ) ;
503+ } else {
504+ err. subdiagnostic ( crate :: session_diagnostics:: TypeNoCopy :: Label {
505+ is_partial_move,
506+ ty,
507+ place : & note_msg,
508+ span,
509+ } ) ;
510+ }
287511 } else {
288512 err. subdiagnostic ( crate :: session_diagnostics:: TypeNoCopy :: Note {
289513 is_partial_move,
0 commit comments