@@ -50,12 +50,16 @@ use log::{debug, trace, warn};
5050use rustfix:: diagnostics:: Diagnostic ;
5151use rustfix:: { self , CodeFix } ;
5252
53- use crate :: core:: { Edition , Workspace } ;
53+ use crate :: core:: compiler:: RustcTargetData ;
54+ use crate :: core:: resolver:: features:: { FeatureOpts , FeatureResolver , RequestedFeatures } ;
55+ use crate :: core:: resolver:: { HasDevUnits , ResolveBehavior , ResolveOpts } ;
56+ use crate :: core:: { Edition , MaybePackage , Workspace } ;
5457use crate :: ops:: { self , CompileOptions } ;
5558use crate :: util:: diagnostic_server:: { Message , RustfixDiagnosticServer } ;
5659use crate :: util:: errors:: CargoResult ;
5760use crate :: util:: { self , paths, Config , ProcessBuilder } ;
5861use crate :: util:: { existing_vcs_repo, LockServer , LockServerClient } ;
62+ use crate :: { drop_eprint, drop_eprintln} ;
5963
6064const FIX_ENV : & str = "__CARGO_FIX_PLZ" ;
6165const BROKEN_CODE_ENV : & str = "__CARGO_FIX_BROKEN_CODE" ;
@@ -74,6 +78,9 @@ pub struct FixOptions {
7478
7579pub fn fix ( ws : & Workspace < ' _ > , opts : & mut FixOptions ) -> CargoResult < ( ) > {
7680 check_version_control ( ws. config ( ) , opts) ?;
81+ if opts. edition {
82+ check_resolver_change ( ws, opts) ?;
83+ }
7784
7885 // Spin up our lock server, which our subprocesses will use to synchronize fixes.
7986 let lock_server = LockServer :: new ( ) ?;
@@ -193,6 +200,108 @@ fn check_version_control(config: &Config, opts: &FixOptions) -> CargoResult<()>
193200 ) ;
194201}
195202
203+ fn check_resolver_change ( ws : & Workspace < ' _ > , opts : & FixOptions ) -> CargoResult < ( ) > {
204+ let root = ws. root_maybe ( ) ;
205+ match root {
206+ MaybePackage :: Package ( root_pkg) => {
207+ if root_pkg. manifest ( ) . resolve_behavior ( ) . is_some ( ) {
208+ // If explicitly specified by the user, no need to check.
209+ return Ok ( ( ) ) ;
210+ }
211+ // Only trigger if updating the root package from 2018.
212+ let pkgs = opts. compile_opts . spec . get_packages ( ws) ?;
213+ if !pkgs. iter ( ) . any ( |& pkg| pkg == root_pkg) {
214+ // The root is not being migrated.
215+ return Ok ( ( ) ) ;
216+ }
217+ if root_pkg. manifest ( ) . edition ( ) != Edition :: Edition2018 {
218+ // V1 to V2 only happens on 2018 to 2021.
219+ return Ok ( ( ) ) ;
220+ }
221+ }
222+ MaybePackage :: Virtual ( _vm) => {
223+ // Virtual workspaces don't have a global edition to set (yet).
224+ return Ok ( ( ) ) ;
225+ }
226+ }
227+ // 2018 without `resolver` set must be V1
228+ assert_eq ! ( ws. resolve_behavior( ) , ResolveBehavior :: V1 ) ;
229+ let specs = opts. compile_opts . spec . to_package_id_specs ( ws) ?;
230+ let resolve_opts = ResolveOpts :: new (
231+ /*dev_deps*/ true ,
232+ RequestedFeatures :: from_command_line (
233+ & opts. compile_opts . features ,
234+ opts. compile_opts . all_features ,
235+ !opts. compile_opts . no_default_features ,
236+ ) ,
237+ ) ;
238+ let target_data = RustcTargetData :: new ( ws, & opts. compile_opts . build_config . requested_kinds ) ?;
239+ // HasDevUnits::No because that may uncover more differences.
240+ // This is not the same as what `cargo fix` is doing, since it is doing
241+ // `--all-targets` which includes dev dependencies.
242+ let ws_resolve = ops:: resolve_ws_with_opts (
243+ ws,
244+ & target_data,
245+ & opts. compile_opts . build_config . requested_kinds ,
246+ & resolve_opts,
247+ & specs,
248+ HasDevUnits :: No ,
249+ crate :: core:: resolver:: features:: ForceAllTargets :: No ,
250+ ) ?;
251+
252+ let feature_opts = FeatureOpts :: new_behavior ( ResolveBehavior :: V2 , HasDevUnits :: No ) ;
253+ let v2_features = FeatureResolver :: resolve (
254+ ws,
255+ & target_data,
256+ & ws_resolve. targeted_resolve ,
257+ & ws_resolve. pkg_set ,
258+ & resolve_opts. features ,
259+ & specs,
260+ & opts. compile_opts . build_config . requested_kinds ,
261+ feature_opts,
262+ ) ?;
263+
264+ let differences = v2_features. compare_legacy ( & ws_resolve. resolved_features ) ;
265+ if differences. features . is_empty ( ) && differences. optional_deps . is_empty ( ) {
266+ // Nothing is different, nothing to report.
267+ return Ok ( ( ) ) ;
268+ }
269+ let config = ws. config ( ) ;
270+ config. shell ( ) . note (
271+ "Switching to Edition 2021 will enable the use of the version 2 feature resolver in Cargo." ,
272+ ) ?;
273+ drop_eprintln ! (
274+ config,
275+ "This may cause dependencies to resolve with a different set of features."
276+ ) ;
277+ drop_eprintln ! (
278+ config,
279+ "More information about the resolver changes may be found \
280+ at https://doc.rust-lang.org/cargo/reference/features.html#feature-resolver-version-2"
281+ ) ;
282+ drop_eprintln ! (
283+ config,
284+ "The following differences were detected with the current configuration:\n "
285+ ) ;
286+ let report = |changes : crate :: core:: resolver:: features:: DiffMap , what| {
287+ for ( ( pkg_id, for_host) , removed) in changes {
288+ drop_eprint ! ( config, " {}" , pkg_id) ;
289+ if for_host {
290+ drop_eprint ! ( config, " (as build dependency)" ) ;
291+ }
292+ if !removed. is_empty ( ) {
293+ let joined: Vec < _ > = removed. iter ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ;
294+ drop_eprint ! ( config, " removed {} `{}`" , what, joined. join( "," ) ) ;
295+ }
296+ drop_eprint ! ( config, "\n " ) ;
297+ }
298+ } ;
299+ report ( differences. features , "features" ) ;
300+ report ( differences. optional_deps , "optional dependency" ) ;
301+ drop_eprint ! ( config, "\n " ) ;
302+ Ok ( ( ) )
303+ }
304+
196305/// Entry point for `cargo` running as a proxy for `rustc`.
197306///
198307/// This is called every time `cargo` is run to check if it is in proxy mode.
0 commit comments