First step towards rustfix compiletest mode
This is the first small step towards testing auto-fixable compiler suggestions using compiletest. Currently, it only checks if next to a UI test there also happens to a `*.rs.fixed` file, and then uses rustfix (added as external crate) on the original file, and asserts that it produces the fixed version. To show that this works, I've included one such test. I picked this test case at random (and because it was simple) -- It is not relevant to the 2018 edition. Indeed, in the near future, we want to be able to restrict rustfix to edition-lints, so this test cast might go away soon. In case you still think this is somewhat feature-complete, here's a quick list of things currently missing that I want to add before telling people they can use this: - [ ] Make this an actual compiletest mode, with `test [fix] …` output and everything - [ ] Assert that fixed files still compile - [ ] Assert that fixed files produce no (or a known set of) diagnostics output - [ ] Update `update-references.sh` to support rustfix - [ ] Use a published version of rustfix (i.e.: publish a new version rustfix that exposes a useful API for this)
This commit is contained in:
parent
91db9dcf37
commit
fd6aa149bc
6 changed files with 110 additions and 2 deletions
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Point at the captured immutable outer variable
|
||||||
|
|
||||||
|
fn foo(mut f: Box<FnMut()>) {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut y = true;
|
||||||
|
foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ regex = "0.2"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
rustfix = { git = "https://github.com/rust-lang-nursery/rustfix" }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|
70
src/tools/compiletest/src/autofix.rs
Normal file
70
src/tools/compiletest/src/autofix.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use rustfix::{get_suggestions_from_json, Replacement};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub fn run_rustfix(code: &str, json: &str) -> String {
|
||||||
|
let suggestions = get_suggestions_from_json(&json, &HashSet::new())
|
||||||
|
.expect("could not load suggestions");
|
||||||
|
|
||||||
|
let mut fixed = code.to_string();
|
||||||
|
|
||||||
|
for sug in suggestions.into_iter().rev() {
|
||||||
|
for sol in sug.solutions {
|
||||||
|
for r in sol.replacements {
|
||||||
|
fixed = apply_suggestion(&mut fixed, &r)
|
||||||
|
.expect("could not apply suggestion");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_suggestion(
|
||||||
|
file_content: &mut String,
|
||||||
|
suggestion: &Replacement,
|
||||||
|
) -> Result<String, Box<Error>> {
|
||||||
|
use std::cmp::max;
|
||||||
|
|
||||||
|
let mut new_content = String::new();
|
||||||
|
|
||||||
|
// Add the lines before the section we want to replace
|
||||||
|
new_content.push_str(&file_content
|
||||||
|
.lines()
|
||||||
|
.take(max(suggestion.snippet.line_range.start.line - 1, 0) as usize)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n"));
|
||||||
|
new_content.push_str("\n");
|
||||||
|
|
||||||
|
// Parts of line before replacement
|
||||||
|
new_content.push_str(&file_content
|
||||||
|
.lines()
|
||||||
|
.nth(suggestion.snippet.line_range.start.line - 1)
|
||||||
|
.unwrap_or("")
|
||||||
|
.chars()
|
||||||
|
.take(suggestion.snippet.line_range.start.column - 1)
|
||||||
|
.collect::<String>());
|
||||||
|
|
||||||
|
// Insert new content! Finally!
|
||||||
|
new_content.push_str(&suggestion.replacement);
|
||||||
|
|
||||||
|
// Parts of line after replacement
|
||||||
|
new_content.push_str(&file_content
|
||||||
|
.lines()
|
||||||
|
.nth(suggestion.snippet.line_range.end.line - 1)
|
||||||
|
.unwrap_or("")
|
||||||
|
.chars()
|
||||||
|
.skip(suggestion.snippet.line_range.end.column - 1)
|
||||||
|
.collect::<String>());
|
||||||
|
|
||||||
|
// Add the lines after the section we want to replace
|
||||||
|
new_content.push_str("\n");
|
||||||
|
new_content.push_str(&file_content
|
||||||
|
.lines()
|
||||||
|
.skip(suggestion.snippet.line_range.end.line as usize)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n"));
|
||||||
|
new_content.push_str("\n");
|
||||||
|
|
||||||
|
Ok(new_content)
|
||||||
|
}
|
|
@ -269,6 +269,7 @@ pub fn expected_output_path(testpaths: &TestPaths,
|
||||||
testpaths.file.with_extension(extension)
|
testpaths.file.with_extension(extension)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT];
|
pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
|
||||||
pub const UI_STDERR: &str = "stderr";
|
pub const UI_STDERR: &str = "stderr";
|
||||||
pub const UI_STDOUT: &str = "stdout";
|
pub const UI_STDOUT: &str = "stdout";
|
||||||
|
pub const UI_FIXED: &str = "rs.fixed";
|
||||||
|
|
|
@ -26,6 +26,7 @@ extern crate regex;
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
extern crate rustfix;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
@ -52,6 +53,7 @@ pub mod common;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod raise_fd_limit;
|
mod raise_fd_limit;
|
||||||
mod read2;
|
mod read2;
|
||||||
|
mod autofix;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
|
@ -12,7 +12,7 @@ use common::{Config, TestPaths};
|
||||||
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
|
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
|
||||||
use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc};
|
use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc};
|
||||||
use common::{Incremental, MirOpt, RunMake, Ui};
|
use common::{Incremental, MirOpt, RunMake, Ui};
|
||||||
use common::{expected_output_path, UI_STDERR, UI_STDOUT};
|
use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED};
|
||||||
use common::CompareMode;
|
use common::CompareMode;
|
||||||
use diff;
|
use diff;
|
||||||
use errors::{self, Error, ErrorKind};
|
use errors::{self, Error, ErrorKind};
|
||||||
|
@ -35,6 +35,7 @@ use std::path::{Path, PathBuf};
|
||||||
use std::process::{Child, Command, ExitStatus, Output, Stdio};
|
use std::process::{Child, Command, ExitStatus, Output, Stdio};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use autofix::run_rustfix;
|
||||||
use extract_gdb_version;
|
use extract_gdb_version;
|
||||||
|
|
||||||
/// The name of the environment variable that holds dynamic library locations.
|
/// The name of the environment variable that holds dynamic library locations.
|
||||||
|
@ -2603,6 +2604,19 @@ impl<'test> TestCx<'test> {
|
||||||
self.check_error_patterns(&proc_res.stderr, &proc_res);
|
self.check_error_patterns(&proc_res.stderr, &proc_res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fixture_path = expected_output_path(&self.testpaths, None, &None, UI_FIXED);
|
||||||
|
if fixture_path.exists() {
|
||||||
|
let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file)
|
||||||
|
.unwrap();
|
||||||
|
let expected_fixed = self.load_expected_output_from_path(&fixture_path).unwrap();
|
||||||
|
let fixed_code = run_rustfix(&unfixed_code, &proc_res.stderr);
|
||||||
|
let errors = self.compare_output("rs.fixed", &fixed_code, &expected_fixed);
|
||||||
|
if errors > 0 {
|
||||||
|
panic!("rustfix produced different fixed file!");
|
||||||
|
// TODO: Add info for update-references.sh call
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_mir_opt_test(&self) {
|
fn run_mir_opt_test(&self) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue