@@ -458,6 +458,54 @@ pub struct ClippyResponse {
458
458
pub exit_detail : String ,
459
459
}
460
460
461
+ #[ derive( Debug , Clone ) ]
462
+ pub struct MiriRequest {
463
+ pub channel : Channel ,
464
+ pub crate_type : CrateType ,
465
+ pub edition : Edition ,
466
+ pub code : String ,
467
+ }
468
+
469
+ impl MiriRequest {
470
+ pub ( crate ) fn delete_previous_main_request ( & self ) -> DeleteFileRequest {
471
+ delete_previous_primary_file_request ( self . crate_type )
472
+ }
473
+
474
+ pub ( crate ) fn write_main_request ( & self ) -> WriteFileRequest {
475
+ write_primary_file_request ( self . crate_type , & self . code )
476
+ }
477
+
478
+ pub ( crate ) fn execute_cargo_request ( & self ) -> ExecuteCommandRequest {
479
+ ExecuteCommandRequest {
480
+ cmd : "cargo" . to_owned ( ) ,
481
+ args : vec ! [ "miri-playground" . to_owned( ) ] ,
482
+ envs : Default :: default ( ) ,
483
+ cwd : None ,
484
+ }
485
+ }
486
+ }
487
+
488
+ impl CargoTomlModifier for MiriRequest {
489
+ fn modify_cargo_toml ( & self , mut cargo_toml : toml:: Value ) -> toml:: Value {
490
+ if self . edition == Edition :: Rust2024 {
491
+ cargo_toml = modify_cargo_toml:: set_feature_edition2024 ( cargo_toml) ;
492
+ }
493
+
494
+ cargo_toml = modify_cargo_toml:: set_edition ( cargo_toml, self . edition . to_cargo_toml_key ( ) ) ;
495
+
496
+ if let Some ( crate_type) = self . crate_type . to_library_cargo_toml_key ( ) {
497
+ cargo_toml = modify_cargo_toml:: set_crate_type ( cargo_toml, crate_type) ;
498
+ }
499
+ cargo_toml
500
+ }
501
+ }
502
+
503
+ #[ derive( Debug , Clone ) ]
504
+ pub struct MiriResponse {
505
+ pub success : bool ,
506
+ pub exit_detail : String ,
507
+ }
508
+
461
509
#[ derive( Debug , Clone ) ]
462
510
pub struct WithOutput < T > {
463
511
pub response : T ,
@@ -649,6 +697,30 @@ where
649
697
. await
650
698
}
651
699
700
+ pub async fn miri ( & self , request : MiriRequest ) -> Result < WithOutput < MiriResponse > , MiriError > {
701
+ use miri_error:: * ;
702
+
703
+ self . select_channel ( request. channel )
704
+ . await
705
+ . context ( CouldNotStartContainerSnafu ) ?
706
+ . miri ( request)
707
+ . await
708
+ }
709
+
710
+ pub async fn begin_miri (
711
+ & self ,
712
+ token : CancellationToken ,
713
+ request : MiriRequest ,
714
+ ) -> Result < ActiveMiri , MiriError > {
715
+ use miri_error:: * ;
716
+
717
+ self . select_channel ( request. channel )
718
+ . await
719
+ . context ( CouldNotStartContainerSnafu ) ?
720
+ . begin_miri ( token, request)
721
+ . await
722
+ }
723
+
652
724
pub async fn idle ( & mut self ) -> Result < ( ) > {
653
725
let Self {
654
726
stable,
@@ -1076,6 +1148,75 @@ impl Container {
1076
1148
} )
1077
1149
}
1078
1150
1151
+ async fn miri ( & self , request : MiriRequest ) -> Result < WithOutput < MiriResponse > , MiriError > {
1152
+ let token = Default :: default ( ) ;
1153
+
1154
+ let ActiveMiri {
1155
+ task,
1156
+ stdout_rx,
1157
+ stderr_rx,
1158
+ } = self . begin_miri ( token, request) . await ?;
1159
+
1160
+ WithOutput :: try_absorb ( task, stdout_rx, stderr_rx) . await
1161
+ }
1162
+
1163
+ async fn begin_miri (
1164
+ & self ,
1165
+ token : CancellationToken ,
1166
+ request : MiriRequest ,
1167
+ ) -> Result < ActiveMiri , MiriError > {
1168
+ use miri_error:: * ;
1169
+
1170
+ let delete_previous_main = request. delete_previous_main_request ( ) ;
1171
+ let write_main = request. write_main_request ( ) ;
1172
+ let execute_cargo = request. execute_cargo_request ( ) ;
1173
+
1174
+ let delete_previous_main = self . commander . one ( delete_previous_main) ;
1175
+ let write_main = self . commander . one ( write_main) ;
1176
+ let modify_cargo_toml = self . modify_cargo_toml . modify_for ( & request) ;
1177
+
1178
+ let ( delete_previous_main, write_main, modify_cargo_toml) =
1179
+ join ! ( delete_previous_main, write_main, modify_cargo_toml) ;
1180
+
1181
+ delete_previous_main. context ( CouldNotDeletePreviousCodeSnafu ) ?;
1182
+ write_main. context ( CouldNotWriteCodeSnafu ) ?;
1183
+ modify_cargo_toml. context ( CouldNotModifyCargoTomlSnafu ) ?;
1184
+
1185
+ let SpawnCargo {
1186
+ task,
1187
+ stdin_tx,
1188
+ stdout_rx,
1189
+ stderr_rx,
1190
+ } = self
1191
+ . spawn_cargo_task ( token, execute_cargo)
1192
+ . await
1193
+ . context ( CouldNotStartCargoSnafu ) ?;
1194
+
1195
+ drop ( stdin_tx) ;
1196
+
1197
+ let task = async move {
1198
+ let ExecuteCommandResponse {
1199
+ success,
1200
+ exit_detail,
1201
+ } = task
1202
+ . await
1203
+ . context ( CargoTaskPanickedSnafu ) ?
1204
+ . context ( CargoFailedSnafu ) ?;
1205
+
1206
+ Ok ( MiriResponse {
1207
+ success,
1208
+ exit_detail,
1209
+ } )
1210
+ }
1211
+ . boxed ( ) ;
1212
+
1213
+ Ok ( ActiveMiri {
1214
+ task,
1215
+ stdout_rx,
1216
+ stderr_rx,
1217
+ } )
1218
+ }
1219
+
1079
1220
async fn spawn_cargo_task (
1080
1221
& self ,
1081
1222
token : CancellationToken ,
@@ -1346,6 +1487,47 @@ pub enum ClippyError {
1346
1487
CargoFailed { source : SpawnCargoError } ,
1347
1488
}
1348
1489
1490
+ pub struct ActiveMiri {
1491
+ pub task : BoxFuture < ' static , Result < MiriResponse , MiriError > > ,
1492
+ pub stdout_rx : mpsc:: Receiver < String > ,
1493
+ pub stderr_rx : mpsc:: Receiver < String > ,
1494
+ }
1495
+
1496
+ impl fmt:: Debug for ActiveMiri {
1497
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1498
+ f. debug_struct ( "ActiveMiri" )
1499
+ . field ( "task" , & "<future>" )
1500
+ . field ( "stdout_rx" , & self . stdout_rx )
1501
+ . field ( "stderr_rx" , & self . stderr_rx )
1502
+ . finish ( )
1503
+ }
1504
+ }
1505
+
1506
+ #[ derive( Debug , Snafu ) ]
1507
+ #[ snafu( module) ]
1508
+ pub enum MiriError {
1509
+ #[ snafu( display( "Could not start the container" ) ) ]
1510
+ CouldNotStartContainer { source : Error } ,
1511
+
1512
+ #[ snafu( display( "Could not modify Cargo.toml" ) ) ]
1513
+ CouldNotModifyCargoToml { source : ModifyCargoTomlError } ,
1514
+
1515
+ #[ snafu( display( "Could not delete previous source code" ) ) ]
1516
+ CouldNotDeletePreviousCode { source : CommanderError } ,
1517
+
1518
+ #[ snafu( display( "Could not write source code" ) ) ]
1519
+ CouldNotWriteCode { source : CommanderError } ,
1520
+
1521
+ #[ snafu( display( "Could not start Cargo task" ) ) ]
1522
+ CouldNotStartCargo { source : SpawnCargoError } ,
1523
+
1524
+ #[ snafu( display( "The Cargo task panicked" ) ) ]
1525
+ CargoTaskPanicked { source : tokio:: task:: JoinError } ,
1526
+
1527
+ #[ snafu( display( "Cargo task failed" ) ) ]
1528
+ CargoFailed { source : SpawnCargoError } ,
1529
+ }
1530
+
1349
1531
struct SpawnCargo {
1350
1532
task : JoinHandle < Result < ExecuteCommandResponse , SpawnCargoError > > ,
1351
1533
stdin_tx : mpsc:: Sender < String > ,
@@ -2910,6 +3092,43 @@ mod tests {
2910
3092
Ok ( ( ) )
2911
3093
}
2912
3094
3095
+ const ARBITRARY_MIRI_REQUEST : MiriRequest = MiriRequest {
3096
+ channel : Channel :: Nightly ,
3097
+ crate_type : CrateType :: Binary ,
3098
+ edition : Edition :: Rust2021 ,
3099
+ code : String :: new ( ) ,
3100
+ } ;
3101
+
3102
+ #[ tokio:: test]
3103
+ #[ snafu:: report]
3104
+ async fn miri ( ) -> Result < ( ) > {
3105
+ // cargo-miri-playground only exists inside the container
3106
+ let coordinator = new_coordinator_docker ( ) . await ;
3107
+
3108
+ let req = MiriRequest {
3109
+ code : r#"
3110
+ fn main() {
3111
+ let mut a: [u8; 0] = [];
3112
+ unsafe { *a.get_unchecked_mut(1) = 1; }
3113
+ }
3114
+ "#
3115
+ . into ( ) ,
3116
+ ..ARBITRARY_MIRI_REQUEST
3117
+ } ;
3118
+
3119
+ let response = coordinator. miri ( req) . with_timeout ( ) . await . unwrap ( ) ;
3120
+
3121
+ assert ! ( !response. success, "stderr: {}" , response. stderr) ;
3122
+
3123
+ assert_contains ! ( response. stderr, "Undefined Behavior" ) ;
3124
+ assert_contains ! ( response. stderr, "pointer to 1 byte" ) ;
3125
+ assert_contains ! ( response. stderr, "starting at offset 0" ) ;
3126
+ assert_contains ! ( response. stderr, "is out-of-bounds" ) ;
3127
+ assert_contains ! ( response. stderr, "has size 0" ) ;
3128
+
3129
+ Ok ( ( ) )
3130
+ }
3131
+
2913
3132
// The next set of tests are broader than the functionality of a
2914
3133
// single operation.
2915
3134
0 commit comments