fn rustfix_and_fix(
    fixes: &mut FixedCrate,
    rustc: &ProcessBuilder,
    filename: &Path,
    args: &FixArgs,
    config: &Config,
) -> Result<(), Error> {
    // If not empty, filter by these lints.
    // TODO: implement a way to specify this.
    let only = HashSet::new();

    let mut cmd = rustc.build_command();
    cmd.arg("--error-format=json");
    args.apply(&mut cmd);
    debug!(
        "calling rustc to collect suggestions and validate previous fixes: {:?}",
        cmd
    );
    let output = cmd.output().with_context(|| {
        format!(
            "failed to execute `{}`",
            rustc.get_program().to_string_lossy()
        )
    })?;

    // If rustc didn't succeed for whatever reasons then we're very likely to be
    // looking at otherwise broken code. Let's not make things accidentally
    // worse by applying fixes where a bug could cause *more* broken code.
    // Instead, punt upwards which will reexec rustc over the original code,
    // displaying pretty versions of the diagnostics we just read out.
    if !output.status.success() && env::var_os(BROKEN_CODE_ENV).is_none() {
        debug!(
            "rustfixing `{:?}` failed, rustc exited with {:?}",
            filename,
            output.status.code()
        );
        return Ok(());
    }

    let fix_mode = env::var_os("__CARGO_FIX_YOLO")
        .map(|_| rustfix::Filter::Everything)
        .unwrap_or(rustfix::Filter::MachineApplicableOnly);

    // Sift through the output of the compiler to look for JSON messages.
    // indicating fixes that we can apply.
    let stderr = str::from_utf8(&output.stderr).context("failed to parse rustc stderr as UTF-8")?;

    let suggestions = stderr
        .lines()
        .filter(|x| !x.is_empty())
        .inspect(|y| trace!("line: {}", y))
        // Parse each line of stderr, ignoring errors, as they may not all be JSON.
        .filter_map(|line| serde_json::from_str::<Diagnostic>(line).ok())
        // From each diagnostic, try to extract suggestions from rustc.
        .filter_map(|diag| rustfix::collect_suggestions(&diag, &only, fix_mode));

    // Collect suggestions by file so we can apply them one at a time later.
    let mut file_map = HashMap::new();
    let mut num_suggestion = 0;
    // It's safe since we won't read any content under home dir.
    let home_path = config.home().as_path_unlocked();
    for suggestion in suggestions {
        trace!("suggestion");
        // Make sure we've got a file associated with this suggestion and all
        // snippets point to the same file. Right now it's not clear what
        // we would do with multiple files.
        let file_names = suggestion
            .solutions
            .iter()
            .flat_map(|s| s.replacements.iter())
            .map(|r| &r.snippet.file_name);

        let file_name = if let Some(file_name) = file_names.clone().next() {
            file_name.clone()
        } else {
            trace!("rejecting as it has no solutions {:?}", suggestion);
            continue;
        };

        // Do not write into registry cache. See rust-lang/cargo#9857.
        if Path::new(&file_name).starts_with(home_path) {
            continue;
        }

        if !file_names.clone().all(|f| f == &file_name) {
            trace!("rejecting as it changes multiple files: {:?}", suggestion);
            continue;
        }

        trace!("adding suggestion for {:?}: {:?}", file_name, suggestion);
        file_map
            .entry(file_name)
            .or_insert_with(Vec::new)
            .push(suggestion);
        num_suggestion += 1;
    }

    debug!(
        "collected {} suggestions for `{}`",
        num_suggestion,
        filename.display(),
    );

    for (file, suggestions) in file_map {
        // Attempt to read the source code for this file. If this fails then
        // that'd be pretty surprising, so log a message and otherwise keep
        // going.
        let code = match paths::read(file.as_ref()) {
            Ok(s) => s,
            Err(e) => {
                warn!("failed to read `{}`: {}", file, e);
                continue;
            }
        };
        let num_suggestions = suggestions.len();
        debug!("applying {} fixes to {}", num_suggestions, file);

        // If this file doesn't already exist then we just read the original
        // code, so save it. If the file already exists then the original code
        // doesn't need to be updated as we've just read an interim state with
        // some fixes but perhaps not all.
        let fixed_file = fixes
            .files
            .entry(file.clone())
            .or_insert_with(|| FixedFile {
                errors_applying_fixes: Vec::new(),
                fixes_applied: 0,
                original_code: code.clone(),
            });
        let mut fixed = CodeFix::new(&code);

        // As mentioned above in `rustfix_crate`, we don't immediately warn
        // about suggestions that fail to apply here, and instead we save them
        // off for later processing.
        for suggestion in suggestions.iter().rev() {
            match fixed.apply(suggestion) {
                Ok(()) => fixed_file.fixes_applied += 1,
                Err(e) => fixed_file.errors_applying_fixes.push(e.to_string()),
            }
        }
        let new_code = fixed.finish()?;
        paths::write(&file, new_code)?;
    }

    Ok(())
}
/// Executes `rustc` to apply one round of suggestions to the crate in question.
///
/// This will fill in the `fixes` map with original code, suggestions applied,
/// and any errors encountered while fixing files.
Executes `rustc` to apply one round of suggestions to the crate in question.
This will fill in the `fixes` map with original code, suggestions applied,
and any errors encountered while fixing files.
Executes ` rustc ` to apply one round of suggestions to the crate in question . This will fill in the ` fixes ` map with original code suggestions applied and any errors encountered while fixing files .
fn rustfix_and_fix(
    fixes: &mut FixedCrate,
    rustc: &ProcessBuilder,
    filename: &Path,
    args: &FixArgs,
    config: &Config,
) -> Result<(), Error> {
    // If not empty, filter by these lints.
    // TODO: implement a way to specify this.
    let only = HashSet::new();
    let mut cmd = rustc.build_command();
    cmd.arg("--error-format=json");
    args.apply(&mut cmd);
    debug!(
        "calling rustc to collect suggestions and validate previous fixes: {:?}",
        cmd
    );
    let output = cmd.output().with_context(|| {
        format!(
            "failed to execute `{}`",
            rustc.get_program().to_string_lossy()
        )
    })?;
    // If rustc didn't succeed for whatever reasons then we're very likely to be
    // looking at otherwise broken code. Let's not make things accidentally
    // worse by applying fixes where a bug could cause *more* broken code.
    // Instead, punt upwards which will reexec rustc over the original code,
    // displaying pretty versions of the diagnostics we just read out.
    if !output.status.success() && env::var_os(BROKEN_CODE_ENV).is_none() {
        debug!(
            "rustfixing `{:?}` failed, rustc exited with {:?}",
            filename,
            output.status.code()
        );
        return Ok(());
    }
    let fix_mode = env::var_os("__CARGO_FIX_YOLO")
        .map(|_| rustfix::Filter::Everything)
        .unwrap_or(rustfix::Filter::MachineApplicableOnly);
    // Sift through the output of the compiler to look for JSON messages.
    // indicating fixes that we can apply.
    let stderr = str::from_utf8(&output.stderr).context("failed to parse rustc stderr as UTF-8")?;
    let suggestions = stderr
        .lines()
        .filter(|x| !x.is_empty())
        .inspect(|y| trace!("line: {}", y))
        // Parse each line of stderr, ignoring errors, as they may not all be JSON.
        .filter_map(|line| serde_json::from_str::<Diagnostic>(line).ok())
        // From each diagnostic, try to extract suggestions from rustc.
        .filter_map(|diag| rustfix::collect_suggestions(&diag, &only, fix_mode));
    // Collect suggestions by file so we can apply them one at a time later.
    let mut file_map = HashMap::new();
    let mut num_suggestion = 0;
    // It's safe since we won't read any content under home dir.
    let home_path = config.home().as_path_unlocked();
    for suggestion in suggestions {
        trace!("suggestion");
        // Make sure we've got a file associated with this suggestion and all
        // snippets point to the same file. Right now it's not clear what
        // we would do with multiple files.
        let file_names = suggestion
            .solutions
            .iter()
            .flat_map(|s| s.replacements.iter())
            .map(|r| &r.snippet.file_name);
        let file_name = if let Some(file_name) = file_names.clone().next() {
            file_name.clone()
        } else {
            trace!("rejecting as it has no solutions {:?}", suggestion);
            continue;
        };
        // Do not write into registry cache. See rust-lang/cargo#9857.
        if Path::new(&file_name).starts_with(home_path) {
            continue;
        }
        if !file_names.clone().all(|f| f == &file_name) {
            trace!("rejecting as it changes multiple files: {:?}", suggestion);
            continue;
        }
        trace!("adding suggestion for {:?}: {:?}", file_name, suggestion);
        file_map
            .entry(file_name)
            .or_insert_with(Vec::new)
            .push(suggestion);
        num_suggestion += 1;
    }
    debug!(
        "collected {} suggestions for `{}`",
        num_suggestion,
        filename.display(),
    );
    for (file, suggestions) in file_map {
        // Attempt to read the source code for this file. If this fails then
        // that'd be pretty surprising, so log a message and otherwise keep
        // going.
        let code = match paths::read(file.as_ref()) {
            Ok(s) => s,
            Err(e) => {
                warn!("failed to read `{}`: {}", file, e);
                continue;
            }
        };
        let num_suggestions = suggestions.len();
        debug!("applying {} fixes to {}", num_suggestions, file);
        // If this file doesn't already exist then we just read the original
        // code, so save it. If the file already exists then the original code
        // doesn't need to be updated as we've just read an interim state with
        // some fixes but perhaps not all.
        let fixed_file = fixes
            .files
            .entry(file.clone())
            .or_insert_with(|| FixedFile {
                errors_applying_fixes: Vec::new(),
                fixes_applied: 0,
                original_code: code.clone(),
            });
        let mut fixed = CodeFix::new(&code);
        // As mentioned above in `rustfix_crate`, we don't immediately warn
        // about suggestions that fail to apply here, and instead we save them
        // off for later processing.
        for suggestion in suggestions.iter().rev() {
            match fixed.apply(suggestion) {
                Ok(()) => fixed_file.fixes_applied += 1,
                Err(e) => fixed_file.errors_applying_fixes.push(e.to_string()),
            }
        }
        let new_code = fixed.finish()?;
        paths::write(&file, new_code)?;
    }
    Ok(())
}
fn rustfix_and_fix ( fixes : & mut FixedCrate , rustc : & ProcessBuilder , filename : & Path , args : & FixArgs , config : & Config , ) -> Result < ( ) , Error > { // If not empty, filter by these lints. // TODO: implement a way to specify this. let only = HashSet :: new ( ) ; let mut cmd = rustc . build_command ( ) ; cmd . arg ( " " ) ; args . apply ( & mut cmd ) ; debug ! ( " " cmd ) ; let output = cmd . output ( ) . with_context ( | | { format ! ( " " rustc get_program ( ) to_string_lossy ( ) ) } ) ? ; // If rustc didn't succeed for whatever reasons then we're very likely to be // looking at otherwise broken code. Let's not make things accidentally // worse by applying fixes where a bug could cause *more* broken code. // Instead, punt upwards which will reexec rustc over the original code, // displaying pretty versions of the diagnostics we just read out. if ! output . status . success ( ) && env :: var_os ( BROKEN_CODE_ENV ) . is_none ( ) { debug ! ( " " filename output status code ( ) ) ; return Ok ( ( ) ) ; } let fix_mode = env :: var_os ( " " ) . map ( | _ | rustfix :: Filter :: Everything ) . unwrap_or ( rustfix :: Filter :: MachineApplicableOnly ) ; // Sift through the output of the compiler to look for JSON messages. // indicating fixes that we can apply. let stderr = str :: from_utf8 ( & output . stderr ) . context ( " " ) ? ; let suggestions = stderr . lines ( ) . filter ( | x | ! x . is_empty ( ) ) . inspect ( | y | trace ! ( " " y ) ) // Parse each line of stderr, ignoring errors, as they may not all be JSON. . filter_map ( | line | serde_json :: from_str :: < Diagnostic > ( line ) . ok ( ) ) // From each diagnostic, try to extract suggestions from rustc. . filter_map ( | diag | rustfix :: collect_suggestions ( & diag , & only , fix_mode ) ) ; // Collect suggestions by file so we can apply them one at a time later. let mut file_map = HashMap :: new ( ) ; let mut num_suggestion = 0 ; // It's safe since we won't read any content under home dir. let home_path = config . home ( ) . as_path_unlocked ( ) ; for suggestion in suggestions { trace ! ( " " ) ; // Make sure we've got a file associated with this suggestion and all // snippets point to the same file. Right now it's not clear what // we would do with multiple files. let file_names = suggestion . solutions . iter ( ) . flat_map ( | s | s . replacements . iter ( ) ) . map ( | r | & r . snippet . file_name ) ; let file_name = if let Some ( file_name ) = file_names . clone ( ) . next ( ) { file_name . clone ( ) } else { trace ! ( " " suggestion ) ; continue ; } ; // Do not write into registry cache. See rust-lang/cargo#9857. if Path :: new ( & file_name ) . starts_with ( home_path ) { continue ; } if ! file_names . clone ( ) . all ( | f | f == & file_name ) { trace ! ( " " suggestion ) ; continue ; } trace ! ( " " file_name suggestion ) ; file_map . entry ( file_name ) . or_insert_with ( Vec :: new ) . push ( suggestion ) ; num_suggestion += 1 ; } debug ! ( " " num_suggestion filename display ( ) ) ; for ( file , suggestions ) in file_map { // Attempt to read the source code for this file. If this fails then // that'd be pretty surprising, so log a message and otherwise keep // going. let code = match paths :: read ( file . as_ref ( ) ) { Ok ( s ) => s , Err ( e ) => { warn ! ( " " file e ) ; continue ; } } ; let num_suggestions = suggestions . len ( ) ; debug ! ( " " num_suggestions file ) ; // If this file doesn't already exist then we just read the original // code, so save it. If the file already exists then the original code // doesn't need to be updated as we've just read an interim state with // some fixes but perhaps not all. let fixed_file = fixes . files . entry ( file . clone ( ) ) . or_insert_with ( | | FixedFile { errors_applying_fixes : Vec :: new ( ) , fixes_applied : 0 , original_code : code . clone ( ) , } ) ; let mut fixed = CodeFix :: new ( & code ) ; // As mentioned above in `rustfix_crate`, we don't immediately warn // about suggestions that fail to apply here, and instead we save them // off for later processing. for suggestion in suggestions . iter ( ) . rev ( ) { match fixed . apply ( suggestion ) { Ok ( ( ) ) => fixed_file . fixes_applied += 1 , Err ( e ) => fixed_file . errors_applying_fixes . push ( e . to_string ( ) ) , } } let new_code = fixed . finish ( ) ? ; paths :: write ( & file , new_code ) ? ; } Ok ( ( ) ) }
Executes `rustc` to apply one round of suggestions to the crate in question.
Executes ` rustc ` to apply one round of suggestions to the crate in question .
fixes
&mut FixedCrate
rustc
&ProcessBuilder
filename
&Path
args
&FixArgs
config
&Config
fixes
&mut FixedCrate
rustc
&ProcessBuilder
filename
&Path
args
&FixArgs
config
&Config