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_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
rustfix = { git = "https://github.com/rust-lang-nursery/rustfix" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
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)
|
||||
}
|
||||
|
||||
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_STDOUT: &str = "stdout";
|
||||
pub const UI_FIXED: &str = "rs.fixed";
|
||||
|
|
|
@ -26,6 +26,7 @@ extern crate regex;
|
|||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate test;
|
||||
extern crate rustfix;
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
|
@ -52,6 +53,7 @@ pub mod common;
|
|||
pub mod errors;
|
||||
mod raise_fd_limit;
|
||||
mod read2;
|
||||
mod autofix;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
|
|
@ -12,7 +12,7 @@ use common::{Config, TestPaths};
|
|||
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
|
||||
use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc};
|
||||
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 diff;
|
||||
use errors::{self, Error, ErrorKind};
|
||||
|
@ -35,6 +35,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::process::{Child, Command, ExitStatus, Output, Stdio};
|
||||
use std::str;
|
||||
|
||||
use autofix::run_rustfix;
|
||||
use extract_gdb_version;
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue