about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/test/ui/suggestions/closure-immutable-outer-variable.rs.fixed20
-rw-r--r--src/tools/compiletest/Cargo.toml1
-rw-r--r--src/tools/compiletest/src/autofix.rs70
-rw-r--r--src/tools/compiletest/src/common.rs3
-rw-r--r--src/tools/compiletest/src/main.rs2
-rw-r--r--src/tools/compiletest/src/runtest.rs16
6 files changed, 110 insertions, 2 deletions
diff --git a/src/test/ui/suggestions/closure-immutable-outer-variable.rs.fixed b/src/test/ui/suggestions/closure-immutable-outer-variable.rs.fixed
new file mode 100644
index 00000000000..80a5a45a305
--- /dev/null
+++ b/src/test/ui/suggestions/closure-immutable-outer-variable.rs.fixed
@@ -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
+}
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index 7d02f0b746d..1710a44380f 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -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"
diff --git a/src/tools/compiletest/src/autofix.rs b/src/tools/compiletest/src/autofix.rs
new file mode 100644
index 00000000000..29ea5cdff8f
--- /dev/null
+++ b/src/tools/compiletest/src/autofix.rs
@@ -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)
+}
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 365b47447f2..5159b1a692e 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -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";
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 37f7af0abe8..3e7c6500433 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -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();
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 01d9f52424d..20456a21cb5 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -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) {