]> git.lizzy.rs Git - rust.git/commitdiff
port compiletest to use JSON output
authorNiko Matsakis <niko@alum.mit.edu>
Sat, 16 Apr 2016 01:23:50 +0000 (21:23 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 21 Apr 2016 08:42:24 +0000 (04:42 -0400)
This uncovered a lot of bugs in compiletest and also some shortcomings
of our existing JSON output. We had to add information to the JSON
output, such as suggested text and macro backtraces. We also had to fix
various bugs in the existing tests.

Joint work with jntrnr.

64 files changed:
mk/crates.mk
src/compiletest/json.rs [new file with mode: 0644]
src/libsyntax/codemap.rs
src/libsyntax/errors/emitter.rs
src/libsyntax/errors/json.rs
src/test/compile-fail/associated-types-path-2.rs
src/test/compile-fail/block-must-not-have-result-do.rs
src/test/compile-fail/block-must-not-have-result-res.rs
src/test/compile-fail/cast-as-bool.rs
src/test/compile-fail/cast-rfc0401.rs
src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs
src/test/compile-fail/consider-removing-last-semi.rs
src/test/compile-fail/empty-struct-unit-pat.rs
src/test/compile-fail/integer-literal-suffix-inference.rs
src/test/compile-fail/issue-11714.rs
src/test/compile-fail/issue-13058.rs
src/test/compile-fail/issue-13428.rs
src/test/compile-fail/issue-13482.rs
src/test/compile-fail/issue-16747.rs
src/test/compile-fail/issue-19707.rs
src/test/compile-fail/issue-20862.rs
src/test/compile-fail/issue-21221-1.rs
src/test/compile-fail/issue-21221-2.rs
src/test/compile-fail/issue-21221-3.rs
src/test/compile-fail/issue-21221-4.rs
src/test/compile-fail/issue-21600.rs
src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs
src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs
src/test/compile-fail/issue-24036.rs
src/test/compile-fail/issue-24446.rs
src/test/compile-fail/issue-26638.rs
src/test/compile-fail/issue-30123.rs
src/test/compile-fail/issue-30302.rs
src/test/compile-fail/issue-32709.rs [new file with mode: 0644]
src/test/compile-fail/issue-3563.rs
src/test/compile-fail/issue-6702.rs
src/test/compile-fail/keyword-false-as-identifier.rs
src/test/compile-fail/keyword-true-as-identifier.rs
src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs
src/test/compile-fail/lint-change-warnings.rs
src/test/compile-fail/lint-malformed.rs
src/test/compile-fail/lint-removed-allow.rs
src/test/compile-fail/lint-removed.rs
src/test/compile-fail/lint-renamed-allow.rs
src/test/compile-fail/lint-renamed.rs
src/test/compile-fail/lint-unknown-lint.rs
src/test/compile-fail/liveness-return-last-stmt-semi.rs
src/test/compile-fail/ref-suggestion.rs
src/test/compile-fail/substs-ppaux.rs
src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs
src/test/compile-fail/symbol-names/issue-32709.rs [deleted file]
src/test/compile-fail/trait-bounds-impl-comparison-1.rs
src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs
src/test/compile-fail/trait-suggest-where-clause.rs
src/test/compile-fail/type-mismatch-same-crate-name.rs
src/test/compile-fail/variance-unused-type-param.rs
src/test/parse-fail/column-offset-1-based.rs
src/test/parse-fail/keyword-do-as-identifier.rs [deleted file]
src/test/parse-fail/keyword-priv-as-identifier.rs [deleted file]
src/test/run-make/json-errors/Makefile
src/test/run-make/json-errors/foo.json [new file with mode: 0644]
src/tools/compiletest/src/errors.rs
src/tools/compiletest/src/main.rs
src/tools/compiletest/src/runtest.rs

index c55a6e791b68da3a3e3c5a05572d64cd26698502..02b95f5b1a9276ecd6aa7d1ce23f3cf41f3e98e7 100644 (file)
@@ -128,7 +128,7 @@ DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \
                 test rustc_lint rustc_const_eval
 
 
-TOOL_DEPS_compiletest := test getopts log
+TOOL_DEPS_compiletest := test getopts log serialize
 TOOL_DEPS_rustdoc := rustdoc
 TOOL_DEPS_rustc := rustc_driver
 TOOL_DEPS_rustbook := std rustdoc
diff --git a/src/compiletest/json.rs b/src/compiletest/json.rs
new file mode 100644 (file)
index 0000000..6927468
--- /dev/null
@@ -0,0 +1,185 @@
+// Copyright 2012-2015 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.
+
+use errors::{Error, ErrorKind};
+use rustc_serialize::json;
+use std::str::FromStr;
+
+// These structs are a subset of the ones found in
+// `syntax::errors::json`.
+
+#[derive(RustcEncodable, RustcDecodable)]
+struct Diagnostic {
+    message: String,
+    code: Option<DiagnosticCode>,
+    level: String,
+    spans: Vec<DiagnosticSpan>,
+    children: Vec<Diagnostic>,
+    rendered: Option<String>,
+}
+
+#[derive(RustcEncodable, RustcDecodable, Clone)]
+struct DiagnosticSpan {
+    file_name: String,
+    line_start: usize,
+    line_end: usize,
+    column_start: usize,
+    column_end: usize,
+    expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
+}
+
+#[derive(RustcEncodable, RustcDecodable, Clone)]
+struct DiagnosticSpanMacroExpansion {
+    /// span where macro was applied to generate this code
+    span: DiagnosticSpan,
+
+    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+    macro_decl_name: String,
+}
+
+#[derive(RustcEncodable, RustcDecodable, Clone)]
+struct DiagnosticCode {
+    /// The code itself.
+    code: String,
+    /// An explanation for the code.
+    explanation: Option<String>,
+}
+
+pub fn parse_output(file_name: &str, output: &str) -> Vec<Error> {
+    output.lines()
+          .flat_map(|line| parse_line(file_name, line))
+          .collect()
+}
+
+fn parse_line(file_name: &str, line: &str) -> Vec<Error> {
+    // The compiler sometimes intermingles non-JSON stuff into the
+    // output.  This hack just skips over such lines. Yuck.
+    if line.chars().next() == Some('{') {
+        match json::decode::<Diagnostic>(line) {
+            Ok(diagnostic) => {
+                let mut expected_errors = vec![];
+                push_expected_errors(&mut expected_errors, &diagnostic, file_name);
+                expected_errors
+            }
+            Err(error) => {
+                println!("failed to decode compiler output as json: `{}`", error);
+                panic!("failed to decode compiler output as json");
+            }
+        }
+    } else {
+        vec![]
+    }
+}
+
+fn push_expected_errors(expected_errors: &mut Vec<Error>,
+                        diagnostic: &Diagnostic,
+                        file_name: &str) {
+    // We only consider messages pertaining to the current file.
+    let matching_spans =
+        || diagnostic.spans.iter().filter(|span| span.file_name == file_name);
+    let with_code =
+        |span: &DiagnosticSpan, text: &str| match diagnostic.code {
+            Some(ref code) =>
+                // FIXME(#33000) -- it'd be better to use a dedicated
+                // UI harness than to include the line/col number like
+                // this, but some current tests rely on it.
+                //
+                // Note: Do NOT include the filename. These can easily
+                // cause false matches where the expected message
+                // appears in the filename, and hence the message
+                // changes but the test still passes.
+                format!("{}:{}: {}:{}: {} [{}]",
+                        span.line_start, span.column_start,
+                        span.line_end, span.column_end,
+                        text, code.code.clone()),
+            None =>
+                // FIXME(#33000) -- it'd be better to use a dedicated UI harness
+                format!("{}:{}: {}:{}: {}",
+                        span.line_start, span.column_start,
+                        span.line_end, span.column_end,
+                        text),
+        };
+
+    // Convert multi-line messages into multiple expected
+    // errors. We expect to replace these with something
+    // more structured shortly anyhow.
+    let mut message_lines = diagnostic.message.lines();
+    if let Some(first_line) = message_lines.next() {
+        for span in matching_spans() {
+            let msg = with_code(span, first_line);
+            let kind = ErrorKind::from_str(&diagnostic.level).ok();
+            expected_errors.push(
+                Error {
+                    line_num: span.line_start,
+                    kind: kind,
+                    msg: msg,
+                }
+            );
+        }
+    }
+    for next_line in message_lines {
+        for span in matching_spans() {
+            expected_errors.push(
+                Error {
+                    line_num: span.line_start,
+                    kind: None,
+                    msg: with_code(span, next_line),
+                }
+            );
+        }
+    }
+
+    // If the message has a suggestion, register that.
+    if let Some(ref rendered) = diagnostic.rendered {
+        let start_line = matching_spans().map(|s| s.line_start).min().expect("\
+            every suggestion should have at least one span");
+        for (index, line) in rendered.lines().enumerate() {
+            expected_errors.push(
+                Error {
+                    line_num: start_line + index,
+                    kind: Some(ErrorKind::Suggestion),
+                    msg: line.to_string()
+                }
+            );
+        }
+    }
+
+    // Add notes for the backtrace
+    for span in matching_spans() {
+        for frame in &span.expansion {
+            push_backtrace(expected_errors,
+                           frame,
+                           file_name);
+        }
+    }
+
+    // Flatten out the children.
+    for child in &diagnostic.children {
+        push_expected_errors(expected_errors, child, file_name);
+    }
+}
+
+fn push_backtrace(expected_errors: &mut Vec<Error>,
+                  expansion: &DiagnosticSpanMacroExpansion,
+                  file_name: &str) {
+    if expansion.span.file_name == file_name {
+        expected_errors.push(
+            Error {
+                line_num: expansion.span.line_start,
+                kind: Some(ErrorKind::Note),
+                msg: format!("in this expansion of {}", expansion.macro_decl_name),
+            }
+        );
+    }
+
+    for previous_expansion in &expansion.span.expansion {
+        push_backtrace(expected_errors, previous_expansion, file_name);
+    }
+}
index f771ee95bd1215696ca6557771290ca1a372755d..35aa827782ddfee63bcd845734811e3a0562de64 100644 (file)
@@ -1394,6 +1394,56 @@ pub fn span_allows_unstable(&self, span: Span) -> bool {
     pub fn count_lines(&self) -> usize {
         self.files.borrow().iter().fold(0, |a, f| a + f.count_lines())
     }
+
+    pub fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
+        let mut last_span = DUMMY_SP;
+        let mut span = span;
+        let mut result = vec![];
+        loop {
+            let span_name_span = self.with_expn_info(span.expn_id, |expn_info| {
+                expn_info.map(|ei| {
+                    let (pre, post) = match ei.callee.format {
+                        MacroAttribute(..) => ("#[", "]"),
+                        MacroBang(..) => ("", "!"),
+                    };
+                    let macro_decl_name = format!("{}{}{}",
+                                                  pre,
+                                                  ei.callee.name(),
+                                                  post);
+                    let def_site_span = ei.callee.span;
+                    (ei.call_site, macro_decl_name, def_site_span)
+                })
+            });
+
+            match span_name_span {
+                None => break,
+                Some((call_site, macro_decl_name, def_site_span)) => {
+                    // Don't print recursive invocations
+                    if !call_site.source_equal(&last_span) {
+                        result.push(MacroBacktrace {
+                            call_site: call_site,
+                            macro_decl_name: macro_decl_name,
+                            def_site_span: def_site_span,
+                        });
+                    }
+                    last_span = span;
+                    span = call_site;
+                }
+            }
+        }
+        result
+    }
+}
+
+pub struct MacroBacktrace {
+    /// span where macro was applied to generate this code
+    pub call_site: Span,
+
+    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+    pub macro_decl_name: String,
+
+    /// span where macro was defined (if known)
+    pub def_site_span: Option<Span>,
 }
 
 // _____________________________________________________________________________
index 61fdc8453d8fdf63853c7cce40855ceb077ba1cc..0b5234769b219d6f83484b3dcd89870919d24aa2 100644 (file)
@@ -577,46 +577,17 @@ fn end_highlight_lines(&mut self,
     fn print_macro_backtrace(&mut self,
                              sp: Span)
                              -> io::Result<()> {
-        let mut last_span = codemap::DUMMY_SP;
-        let mut span = sp;
-
-        loop {
-            let span_name_span = self.cm.with_expn_info(span.expn_id, |expn_info| {
-                expn_info.map(|ei| {
-                    let (pre, post) = match ei.callee.format {
-                        codemap::MacroAttribute(..) => ("#[", "]"),
-                        codemap::MacroBang(..) => ("", "!"),
-                    };
-                    let macro_decl_name = format!("in this expansion of {}{}{}",
-                                                  pre,
-                                                  ei.callee.name(),
-                                                  post);
-                    let def_site_span = ei.callee.span;
-                    (ei.call_site, macro_decl_name, def_site_span)
-                })
-            });
-            let (macro_decl_name, def_site_span) = match span_name_span {
-                None => break,
-                Some((sp, macro_decl_name, def_site_span)) => {
-                    span = sp;
-                    (macro_decl_name, def_site_span)
-                }
-            };
-
-            // Don't print recursive invocations
-            if !span.source_equal(&last_span) {
-                let mut diag_string = macro_decl_name;
-                if let Some(def_site_span) = def_site_span {
-                    diag_string.push_str(&format!(" (defined in {})",
-                                                  self.cm.span_to_filename(def_site_span)));
-                }
-
-                let snippet = self.cm.span_to_string(span);
-                print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
+        for trace in self.cm.macro_backtrace(sp) {
+            let mut diag_string =
+                format!("in this expansion of {}", trace.macro_decl_name);
+            if let Some(def_site_span) = trace.def_site_span {
+                diag_string.push_str(
+                    &format!(" (defined in {})",
+                        self.cm.span_to_filename(def_site_span)));
             }
-            last_span = span;
+            let snippet = self.cm.span_to_string(sp);
+            print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
         }
-
         Ok(())
     }
 }
index f369582bc5c30f674c5a8fe78462ab9345a520d0..5a195e9f0788b46828e75a43b69537a26af64cb2 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
 // FIXME spec the JSON output properly.
 
 
-use codemap::{self, Span, MultiSpan, CodeMap};
+use codemap::{self, Span, MacroBacktrace, MultiSpan, CodeMap};
 use diagnostics::registry::Registry;
 use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion};
 use errors::emitter::Emitter;
 
 use std::rc::Rc;
 use std::io::{self, Write};
+use std::vec;
 
 use rustc_serialize::json::as_json;
 
@@ -84,8 +85,12 @@ struct Diagnostic<'a> {
     /// "error: internal compiler error", "error", "warning", "note", "help".
     level: &'static str,
     spans: Vec<DiagnosticSpan>,
-    /// Assocaited diagnostic messages.
+    /// Associated diagnostic messages.
     children: Vec<Diagnostic<'a>>,
+    /// The message as rustc would render it. Currently this is only
+    /// `Some` for "suggestions", but eventually it will include all
+    /// snippets.
+    rendered: Option<String>,
 }
 
 #[derive(RustcEncodable)]
@@ -101,16 +106,39 @@ struct DiagnosticSpan {
     column_end: usize,
     /// Source text from the start of line_start to the end of line_end.
     text: Vec<DiagnosticSpanLine>,
+    /// If we are suggesting a replacement, this will contain text
+    /// that should be sliced in atop this span. You may prefer to
+    /// load the fully rendered version from the parent `Diagnostic`,
+    /// however.
+    suggested_replacement: Option<String>,
+    /// Macro invocations that created the code at this span, if any.
+    expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
 }
 
 #[derive(RustcEncodable)]
 struct DiagnosticSpanLine {
     text: String,
+
     /// 1-based, character offset in self.text.
     highlight_start: usize,
+
     highlight_end: usize,
 }
 
+#[derive(RustcEncodable)]
+struct DiagnosticSpanMacroExpansion {
+    /// span where macro was applied to generate this code; note that
+    /// this may itself derive from a macro (if
+    /// `span.expansion.is_some()`)
+    span: DiagnosticSpan,
+
+    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+    macro_decl_name: String,
+
+    /// span where macro was defined (if known)
+    def_site_span: Option<DiagnosticSpan>,
+}
+
 #[derive(RustcEncodable)]
 struct DiagnosticCode {
     /// The code itself.
@@ -132,6 +160,7 @@ fn new(msp: Option<&MultiSpan>,
             level: level.to_str(),
             spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)),
             children: vec![],
+            rendered: None,
         }
     }
 
@@ -146,6 +175,7 @@ fn from_render_span(span: &RenderSpan,
             level: level.to_str(),
             spans: DiagnosticSpan::from_render_span(span, je),
             children: vec![],
+            rendered: je.render(span),
         }
     }
 
@@ -160,6 +190,7 @@ fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
             children: db.children.iter().map(|c| {
                 Diagnostic::from_sub_diagnostic(c, je)
             }).collect(),
+            rendered: None,
         }
     }
 
@@ -173,37 +204,93 @@ fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnosti
                      .or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je)))
                      .unwrap_or(vec![]),
             children: vec![],
+            rendered: db.render_span.as_ref()
+                                    .and_then(|rsp| je.render(rsp)),
         }
     }
 }
 
 impl DiagnosticSpan {
+    fn from_span(span: Span, suggestion: Option<&String>, je: &JsonEmitter)
+                 -> DiagnosticSpan {
+        // obtain the full backtrace from the `macro_backtrace`
+        // helper; in some ways, it'd be better to expand the
+        // backtrace ourselves, but the `macro_backtrace` helper makes
+        // some decision, such as dropping some frames, and I don't
+        // want to duplicate that logic here.
+        let backtrace = je.cm.macro_backtrace(span).into_iter();
+        DiagnosticSpan::from_span_and_backtrace(span, suggestion, backtrace, je)
+    }
+
+    fn from_span_and_backtrace(span: Span,
+                               suggestion: Option<&String>,
+                               mut backtrace: vec::IntoIter<MacroBacktrace>,
+                               je: &JsonEmitter)
+                               -> DiagnosticSpan {
+        let start = je.cm.lookup_char_pos(span.lo);
+        let end = je.cm.lookup_char_pos(span.hi);
+        let backtrace_step =
+            backtrace.next()
+                     .map(|bt| {
+                         let call_site =
+                             Self::from_span_and_backtrace(bt.call_site,
+                                                           None,
+                                                           backtrace,
+                                                           je);
+                         let def_site_span =
+                             bt.def_site_span
+                               .map(|sp| {
+                                   Self::from_span_and_backtrace(sp,
+                                                                 None,
+                                                                 vec![].into_iter(),
+                                                                 je)
+                               });
+                         Box::new(DiagnosticSpanMacroExpansion {
+                             span: call_site,
+                             macro_decl_name: bt.macro_decl_name,
+                             def_site_span: def_site_span,
+                         })
+                     });
+        DiagnosticSpan {
+            file_name: start.file.name.clone(),
+            byte_start: span.lo.0,
+            byte_end: span.hi.0,
+            line_start: start.line,
+            line_end: end.line,
+            column_start: start.col.0 + 1,
+            column_end: end.col.0 + 1,
+            text: DiagnosticSpanLine::from_span(span, je),
+            suggested_replacement: suggestion.cloned(),
+            expansion: backtrace_step,
+        }
+    }
+
     fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
-        msp.spans.iter().map(|span| {
-            let start = je.cm.lookup_char_pos(span.lo);
-            let end = je.cm.lookup_char_pos(span.hi);
-            DiagnosticSpan {
-                file_name: start.file.name.clone(),
-                byte_start: span.lo.0,
-                byte_end: span.hi.0,
-                line_start: start.line,
-                line_end: end.line,
-                column_start: start.col.0 + 1,
-                column_end: end.col.0 + 1,
-                text: DiagnosticSpanLine::from_span(span, je),
-            }
-        }).collect()
+        msp.spans.iter().map(|&span| Self::from_span(span, None, je)).collect()
+    }
+
+    fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter)
+                       -> Vec<DiagnosticSpan> {
+        assert_eq!(suggestion.msp.spans.len(), suggestion.substitutes.len());
+        suggestion.msp.spans.iter()
+                            .zip(&suggestion.substitutes)
+                            .map(|(&span, suggestion)| {
+                                DiagnosticSpan::from_span(span, Some(suggestion), je)
+                            })
+                            .collect()
     }
 
     fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
         match *rsp {
-            RenderSpan::FullSpan(ref msp) |
-            // FIXME(#30701) handle Suggestion properly
-            RenderSpan::Suggestion(CodeSuggestion { ref msp, .. }) => {
+            RenderSpan::FileLine(ref msp) |
+            RenderSpan::FullSpan(ref msp) => {
                 DiagnosticSpan::from_multispan(msp, je)
             }
+            RenderSpan::Suggestion(ref suggestion) => {
+                DiagnosticSpan::from_suggestion(suggestion, je)
+            }
             RenderSpan::EndSpan(ref msp) => {
-                msp.spans.iter().map(|span| {
+                msp.spans.iter().map(|&span| {
                     let end = je.cm.lookup_char_pos(span.hi);
                     DiagnosticSpan {
                         file_name: end.file.name.clone(),
@@ -214,37 +301,11 @@ fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
                         column_start: end.col.0 + 1,
                         column_end: end.col.0 + 1,
                         text: DiagnosticSpanLine::from_span_end(span, je),
+                        suggested_replacement: None,
+                        expansion: None,
                     }
                 }).collect()
             }
-            RenderSpan::FileLine(ref msp) => {
-                msp.spans.iter().map(|span| {
-                    let start = je.cm.lookup_char_pos(span.lo);
-                    let end = je.cm.lookup_char_pos(span.hi);
-                    DiagnosticSpan {
-                        file_name: start.file.name.clone(),
-                        byte_start: span.lo.0,
-                        byte_end: span.hi.0,
-                        line_start: start.line,
-                        line_end: end.line,
-                        column_start: 0,
-                        column_end: 0,
-                        text: DiagnosticSpanLine::from_span(span, je),
-                    }
-                }).collect()
-            }
-        }
-    }
-}
-
-macro_rules! get_lines_for_span {
-    ($span: ident, $je: ident) => {
-        match $je.cm.span_to_lines(*$span) {
-            Ok(lines) => lines,
-            Err(_) => {
-                debug!("unprintable span");
-                return Vec::new();
-            }
         }
     }
 }
@@ -265,45 +326,49 @@ fn line_from_filemap(fm: &codemap::FileMap,
     /// Create a list of DiagnosticSpanLines from span - each line with any part
     /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
     /// `span` within the line.
-    fn from_span(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
-        let lines = get_lines_for_span!(span, je);
-
-        let mut result = Vec::new();
-        let fm = &*lines.file;
-
-        for line in &lines.lines {
-            result.push(DiagnosticSpanLine::line_from_filemap(fm,
-                                                              line.line_index,
-                                                              line.start_col.0 + 1,
-                                                              line.end_col.0 + 1));
-        }
-
-        result
+    fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
+        je.cm.span_to_lines(span)
+             .map(|lines| {
+                 let fm = &*lines.file;
+                 lines.lines
+                      .iter()
+                      .map(|line| {
+                          DiagnosticSpanLine::line_from_filemap(fm,
+                                                                line.line_index,
+                                                                line.start_col.0 + 1,
+                                                                line.end_col.0 + 1)
+                      })
+                     .collect()
+             })
+            .unwrap_or(vec![])
     }
 
     /// Create a list of DiagnosticSpanLines from span - the result covers all
     /// of `span`, but the highlight is zero-length and at the end of `span`.
-    fn from_span_end(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
-        let lines = get_lines_for_span!(span, je);
-
-        let mut result = Vec::new();
-        let fm = &*lines.file;
-
-        for (i, line) in lines.lines.iter().enumerate() {
-            // Invariant - CodeMap::span_to_lines will not return extra context
-            // lines - the last line returned is the last line of `span`.
-            let highlight = if i == lines.lines.len() - 1 {
-                (line.end_col.0 + 1, line.end_col.0 + 1)
-            } else {
-                (0, 0)
-            };
-            result.push(DiagnosticSpanLine::line_from_filemap(fm,
-                                                              line.line_index,
-                                                              highlight.0,
-                                                              highlight.1));
-        }
-
-        result
+    fn from_span_end(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
+        je.cm.span_to_lines(span)
+             .map(|lines| {
+                 let fm = &*lines.file;
+                 lines.lines.iter()
+                            .enumerate()
+                            .map(|(i, line)| {
+                                // Invariant - CodeMap::span_to_lines
+                                // will not return extra context lines
+                                // - the last line returned is the last
+                                // line of `span`.
+                                let highlight = if i == lines.lines.len() - 1 {
+                                    (line.end_col.0 + 1, line.end_col.0 + 1)
+                                } else {
+                                    (0, 0)
+                                };
+                                DiagnosticSpanLine::line_from_filemap(fm,
+                                                                      line.line_index,
+                                                                      highlight.0,
+                                                                      highlight.1)
+                            })
+                            .collect()
+             })
+            .unwrap_or(vec![])
     }
 }
 
@@ -322,3 +387,21 @@ fn map_opt_string(s: Option<String>, je: &JsonEmitter) -> Option<DiagnosticCode>
         })
     }
 }
+
+impl JsonEmitter {
+    fn render(&self, render_span: &RenderSpan) -> Option<String> {
+        match *render_span {
+            RenderSpan::FileLine(_) |
+            RenderSpan::FullSpan(_) => {
+                None
+            }
+            RenderSpan::Suggestion(ref suggestion) => {
+                Some(suggestion.splice_lines(&self.cm))
+            }
+            RenderSpan::EndSpan(_) => {
+                None
+            }
+        }
+    }
+}
+
index 0c077e37e43bebe23675326ecb13a34160579db6..68fba56427cc50f59617b761f90cee277945f71f 100644 (file)
@@ -28,8 +28,8 @@ pub fn f2<T: Foo>(a: T) -> T::A {
 pub fn f1_int_int() {
     f1(2i32, 4i32);
     //~^ ERROR mismatched types
-    //~| expected u32
-    //~| found i32
+    //~| expected `u32`
+    //~| found `i32`
 }
 
 pub fn f1_int_uint() {
index 30039a1c54c3a2d9fba92f471cba2d386bddc255..2a6c71dbe39232abb0412ef6ff1e0f633cb31585 100644 (file)
@@ -11,9 +11,5 @@
 fn main() {
     loop {
         true //~  ERROR mismatched types
-             //~| expected ()
-             //~| found bool
-             //~| expected ()
-             //~| found bool
     }
 }
index 6161660ddf7b3ed9daf2ecd36ffb67067256cc9e..8728685fc8b026e4f8cf4f4559278eb96c119e5c 100644 (file)
 impl Drop for r {
     fn drop(&mut self) {
         true //~  ERROR mismatched types
-             //~| expected ()
-             //~| found bool
-             //~| expected ()
-             //~| found bool
     }
 }
 
index 4764ae380ff44ce9bcd7950df8d5ea7459a05228..af42d5c275c75a49055a0339bd40323ff20ee50e 100644 (file)
@@ -11,6 +11,5 @@
 fn main() {
     let u = 5 as bool;
     //~^ ERROR cannot cast as `bool`
-    //~^^ HELP compare with zero instead
-    //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
+    //~| HELP compare with zero instead
 }
index dcd49e34bb26c9f0a7678be2871f27a502cba55f..b81617abcf4b0618c159b42429947885c4a696b3 100644 (file)
@@ -60,12 +60,10 @@ fn main()
     //~^^ HELP through a usize first
     let _ = 3 as bool;
     //~^ ERROR cannot cast as `bool`
-    //~^^ HELP compare with zero
-    //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
+    //~| HELP compare with zero
     let _ = E::A as bool;
     //~^ ERROR cannot cast as `bool`
-    //~^^ HELP compare with zero
-    //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
+    //~| HELP compare with zero
     let _ = 0x61u32 as char; //~ ERROR only `u8` can be cast
 
     let _ = false as f32;
@@ -92,9 +90,8 @@ fn main()
     let _ = v as *const [u8]; //~ ERROR cannot cast
     let _ = fat_v as *const Foo;
     //~^ ERROR the trait bound `[u8]: std::marker::Sized` is not satisfied
-    //~^^ HELP run `rustc --explain E0277` to see a detailed explanation
-    //~^^^ NOTE `[u8]` does not have a constant size known at compile-time
-    //~^^^^ NOTE required for the cast to the object type `Foo`
+    //~| NOTE `[u8]` does not have a constant size known at compile-time
+    //~| NOTE required for the cast to the object type `Foo`
     let _ = foo as *const str; //~ ERROR casting
     let _ = foo as *mut str; //~ ERROR casting
     let _ = main as *mut str; //~ ERROR casting
@@ -107,9 +104,8 @@ fn main()
     let a : *const str = "hello";
     let _ = a as *const Foo;
     //~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied
-    //~^^ HELP run `rustc --explain E0277` to see a detailed explanation
-    //~^^^ NOTE `str` does not have a constant size known at compile-time
-    //~^^^^ NOTE required for the cast to the object type `Foo`
+    //~| NOTE `str` does not have a constant size known at compile-time
+    //~| NOTE required for the cast to the object type `Foo`
 
     // check no error cascade
     let _ = main.f as *const u32; //~ ERROR attempted access of field
index afc3b8d4ccddcbc7ed5a3395f7b4d9caf7caa02d..7fd1b17f2966c9c1baae5155e76c9426f1aa0845 100644 (file)
@@ -20,7 +20,7 @@ impl<T: MyTrait> !Send for TestType<T> {}
 //~^ ERROR conflicting implementations of trait `std::marker::Send`
 
 unsafe impl<T:'static> Send for TestType<T> {}
-//~^ ERROR error: conflicting implementations of trait `std::marker::Send`
+//~^ ERROR conflicting implementations of trait `std::marker::Send`
 
 impl !Send for TestType<i32> {}
 
index 02148a138c9d905c9f02d23c2ae5476d2b694db3..2e110cb3d0bc88f16f89d6bb4742b5513c7cf8d5 100644 (file)
@@ -9,13 +9,11 @@
 // except according to those terms.
 
 fn f() -> String {  //~ ERROR E0269
-                    //~^ HELP detailed explanation
     0u8;
     "bla".to_string();  //~ HELP consider removing this semicolon
 }
 
 fn g() -> String {  //~ ERROR E0269
-                    //~^ HELP detailed explanation
     "this won't work".to_string();
     "removeme".to_string(); //~ HELP consider removing this semicolon
 }
index a75290c94053d8df912a6a642c8b91de32f47109..05733762d37ad1f637e49ad6bd433196dad89378 100644 (file)
@@ -37,11 +37,11 @@ fn main() {
     // }
     match e2 {
         Empty2(..) => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct
-            //~^ ERROR hard error
+            //~^ WARNING hard error
     }
     match xe2 {
         XEmpty2(..) => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct
-            //~^ ERROR hard error
+            //~^ WARNING hard error
     }
     // Rejected by parser as yet
     // match e4 {
@@ -53,11 +53,11 @@ fn main() {
     // }
     match e4 {
         E::Empty4(..) => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct
-            //~^ ERROR hard error
+            //~^ WARNING hard error
     }
     match xe4 {
         XE::XEmpty4(..) => (), //~ ERROR `XE::XEmpty4` does not name a tuple variant or a tuple
-            //~^ ERROR hard error
+            //~^ WARNING hard error
         _ => {},
     }
 }
index 8f04b58b77868a3076b820651ec88d1c97c58407..a01db18a34a7fcfc4aff97615c98c3a9ad67e19d 100644 (file)
@@ -143,7 +143,7 @@ fn id_u64(n: u64) -> u64 { n }
     id_i64(a16);
     //~^ ERROR mismatched types
     //~| expected `i64`
-    //~| found i16
+    //~| found `i16`
     id_i64(a32);
     //~^ ERROR mismatched types
     //~| expected `i64`
index 6dde59d4a2e6098353e0374eab134060ee773c37..998576097a0a06887275de88b1d82149f2fcae6e 100644 (file)
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 fn blah() -> i32 { //~ ERROR not all control paths return a value
-    //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     1
 
     ; //~ HELP consider removing this semicolon:
index b552d7678d563701f3282fa6ef290365ecc67fc4..de578257e46848201f1421a7468aca877eb1846e 100644 (file)
@@ -36,5 +36,4 @@ fn check<'r, I: Iterator<Item=usize>, T: Itble<'r, usize, I>>(cont: &T) -> bool
 fn main() {
     check((3, 5));
 //~^ ERROR mismatched types
-//~| HELP run `rustc --explain E0308` to see a detailed explanation
 }
index 5b8ab08aefca11a4bed1b68f6dc66f3dd0350ab3..c771970650d31e2bee64641e787dabc1c188d04f 100644 (file)
@@ -11,7 +11,6 @@
 // Regression test for #13428
 
 fn foo() -> String {  //~ ERROR not all control paths return a value
-    //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     format!("Hello {}",
             "world")
     // Put the trailing semicolon on its own line to test that the
@@ -20,7 +19,6 @@ fn foo() -> String {  //~ ERROR not all control paths return a value
 }
 
 fn bar() -> String {  //~ ERROR not all control paths return a value
-    //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     "foobar".to_string()
     ;   //~ HELP consider removing this semicolon
 }
index 2fbfd6cc84eadccb0a1a1e83cfc1ed0ad514e56d..8d98fe313349b89a59614394ce2e9f562046beac 100644 (file)
@@ -17,7 +17,7 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `[_; 2]`
     //~| found `[_; 0]`
-    //~| expected array with a fixed size of 2 elements
+    //~| expected an array with a fixed size of 2 elements
     [a,_] => Some(a)
   };
 }
index 0fdb5f74e829961a575921f634449c087af01b44..dd7e8a869eca9c3c9d0119f70a03cf281aed76e4 100644 (file)
@@ -19,7 +19,6 @@ struct List<'a, T: ListItem<'a>> {
 //~^ ERROR the parameter type `T` may not live long enough
 //~| HELP consider adding an explicit lifetime bound
 //~| NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at
-//~| HELP run `rustc --explain E0309` to see a detailed explanation
 }
 impl<'a, T: ListItem<'a>> Collection for List<'a, T> {
     fn len(&self) -> usize {
index 814c1a4131d381faf81a0e469932e5f61f4be662..9affb44b7445c7d07736851368efa81e3e7ddc9d 100644 (file)
 
 type foo = fn(&u8, &u8) -> &u8; //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say whether it is borrowed from argument 1 or argument 2
-//~^^ HELP run `rustc --explain E0106` to see a detailed explanation
 
 fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {} //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say whether it is borrowed from argument 1 or argument 2
-//~^^ HELP run `rustc --explain E0106` to see a detailed explanation
 
 fn main() {}
index 729311416386d6e5620368db76d4d26adc5cb918..9df63583998692931f52c1496887aa130881e382 100644 (file)
 
 fn foo(x: i32) {
     |y| x + y
-//~^ ERROR: mismatched types:
-//~| expected `()`,
-//~|     found closure
-//~| (expected (),
-//~|     found closure) [E0308]
+//~^ ERROR: mismatched types
 }
 
 fn main() {
index c53d5a0922e647737fa8f21a61623a300e810321..2bc9ec3289a5d4afa3f1e90321693d3658f46578 100644 (file)
@@ -55,7 +55,6 @@ impl Mul for Foo {
 //~| HELP `mul1::Mul`
 //~| HELP `mul2::Mul`
 //~| HELP `std::ops::Mul`
-//~| HELP run `rustc --explain E0405` to see a detailed explanation
 //~| HELP you can import several candidates into scope (`use ...;`):
 }
 
@@ -77,22 +76,19 @@ fn getMul() -> Mul {
 //~| HELP `mul3::Mul`
 //~| HELP `mul4::Mul`
 //~| HELP and 2 other candidates
-//~| HELP run `rustc --explain E0412` to see a detailed explanation
 //~| HELP you can import several candidates into scope (`use ...;`):
 }
 
 // Let's also test what happens if the trait doesn't exist:
 impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo {
 //~^ ERROR trait `ThisTraitReallyDoesntExistInAnyModuleReally` is not in scope
-//~^^ HELP run `rustc --explain E0405` to see a detailed explanation
-//~^^^ HELP no candidates by the name of `ThisTraitReallyDoesntExistInAnyModuleReally` found
+//~| HELP no candidates by the name of `ThisTraitReallyDoesntExistInAnyModuleReally` found
 }
 
 // Let's also test what happens if there's just one alternative:
 impl Div for Foo {
 //~^ ERROR trait `Div` is not in scope
 //~| HELP `use std::ops::Div;`
-//~| HELP run `rustc --explain E0405` to see a detailed explanation
 }
 
 fn main() {
index cf5c6e8a3b4f09c0dca79d50436dbec4cd1e14ec..861acf62d0b58f70e9484a44e54f052264aa6316 100644 (file)
@@ -28,4 +28,3 @@ pub mod baz {
 impl T for Foo { }
 //~^ ERROR trait `T` is not in scope
 //~| HELP you can import it into scope: `use foo::bar::T;`.
-//~| HELP run `rustc --explain E0405` to see a detailed explanation
index a1a712d142197ddc485312fb68102fabbe71243a..05786e69cefd231e2984ee2ad28dfd9f9eab85f0 100644 (file)
@@ -25,7 +25,6 @@
 impl OuterTrait for Foo {}
 //~^ ERROR trait `OuterTrait` is not in scope
 //~| HELP you can import it into scope: `use issue_21221_3::outer::OuterTrait;`.
-//~| HELP run `rustc --explain E0405` to see a detailed explanation
 fn main() {
     println!("Hello, world!");
 }
index 1ef205bd8be8560a565fe03f4d2bd5a56fef7f7d..bcbee16cdcf321f27260056c017b1f9b7e95f95c 100644 (file)
@@ -20,7 +20,6 @@
 impl T for Foo {}
 //~^ ERROR trait `T` is not in scope
 //~| HELP you can import it into scope: `use issue_21221_4::T;`.
-//~| HELP run `rustc --explain E0405` to see a detailed explanation
 
 fn main() {
     println!("Hello, world!");
index d9dcebfda6a1dbafb93d20323b04219a7c6ab947..1d0473ec4b6510ea58ec5922041b8b654cadaac0 100644 (file)
@@ -23,8 +23,6 @@ fn main() {
         call_it(|| x.gen());
         call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer
         //~^ ERROR cannot borrow data mutably in a captured outer
-        //~^^ HELP run `rustc --explain E0387` to see a detailed explanation
-        //~^^^ HELP run `rustc --explain E0387` to see a detailed explanation
-        //~^^^^ HELP consider changing this closure to take self by mutable reference
+        //~| HELP consider changing this closure to take self by mutable reference
     });
 }
index e880a8b212bbc101255d60147d62756d9ff85320..99035209e14bc48aa4482f681a2ddf3d5f27796b 100644 (file)
@@ -36,5 +36,4 @@ fn main() {
     //~| help: the following implementations were found:
     //~| help:   <Bar as Foo<i32>>
     //~| help:   <Bar as Foo<u8>>
-    //~| help: run `rustc --explain E0277`
 }
index 2c5b18a8113f7ac7d95ccb18648da5facb75165f..2009c32c854365a6bb91973f8f101d9d2b97e5e4 100644 (file)
@@ -43,5 +43,4 @@ fn main() {
     //~| help:   <Bar as Foo<i32>>
     //~| help:   <Bar as Foo<u8>>
     //~| help: and 2 others
-    //~| help: run `rustc --explain E0277`
 }
index 28eebea749cce5a14ad206b62c025089025edb62..06b058cbfe1795a5e7ef5b52cd301b8e40731b1d 100644 (file)
@@ -14,7 +14,6 @@ fn closure_to_loc() {
     //~^ ERROR mismatched types
     //~| NOTE no two closures, even if identical, have the same type
     //~| HELP consider boxing your closure and/or using it as a trait object
-    //~| HELP run `rustc --explain E0308` to see a detailed explanation
 }
 
 fn closure_from_match() {
@@ -27,7 +26,6 @@ fn closure_from_match() {
     //~^^^^^^ ERROR match arms have incompatible types
     //~| NOTE no two closures, even if identical, have the same type
     //~| HELP consider boxing your closure and/or using it as a trait object
-    //~| HELP run `rustc --explain E0308` to see a detailed explanation
 }
 
 fn main() { }
index cafe6d1bb58298b2ac49257a7d3b88137badc0dc..a2831fd2b5ac846aeb6304fc934c2987ac03bbad 100644 (file)
 fn main() {
     static foo: Fn() -> u32 = || -> u32 {
         //~^ ERROR: mismatched types:
-        //~| expected `std::ops::Fn() -> u32 + 'static`,
-        //~| found closure
-        //~| (expected trait std::ops::Fn,
-        //~| found closure)
         0
     };
 }
index 9cbb64c2311bc50d94dd8fb2615a70cc6083bf26..f918f0aed7a1098cdab57754bbed8b2a69a1df47 100644 (file)
 fn parse_type(iter: Box<Iterator<Item=&str>+'static>) -> &str { iter.next() }
 //~^ ERROR missing lifetime specifier [E0106]
 //~^^ HELP 2 elided lifetimes
-//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation
 
 fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() }
 //~^ ERROR missing lifetime specifier [E0106]
 //~^^ HELP lifetime cannot be derived
-//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation
-//~^^^^ HELP consider giving it an explicit bounded or 'static lifetime
+//~^^^ HELP consider giving it an explicit bounded or 'static lifetime
 
 fn parse_type_3() -> &str { unimplemented!() }
 //~^ ERROR missing lifetime specifier [E0106]
 //~^^ HELP no value for it to be borrowed from
-//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation
-//~^^^^ HELP consider giving it a 'static lifetime
+//~^^^ HELP consider giving it a 'static lifetime
 
 fn main() {}
index cfd3cd3af3ebc7935eb6169a7d323b478d274a34..ae1320c821f8c96ec43abc5f57884a851939addc 100644 (file)
@@ -15,5 +15,5 @@
 
 fn main() {
     let ug = Graph::<i32, i32>::new_undirected();
-    //~^ ERR no associated item named `new_undirected` found for type
+    //~^ ERROR no associated item named `new_undirected` found for type
 }
index 56f0b31da0d80ba3997ba08503a6e767980a1f15..26508a47224253462be8ff723601771a5af4383f 100644 (file)
@@ -18,10 +18,8 @@ fn is_empty<T>(s: Stack<T>) -> bool {
         Nil => true,
 //~^ WARN pattern binding `Nil` is named the same as one of the variants of the type `Stack`
 //~| HELP consider making the path in the pattern qualified: `Stack::Nil`
-//~| HELP run `rustc --explain E0170` to see a detailed explanation
         _ => false
 //~^ ERROR unreachable pattern
-//~| HELP run `rustc --explain E0001` to see a detailed explanation
     }
 }
 
diff --git a/src/test/compile-fail/issue-32709.rs b/src/test/compile-fail/issue-32709.rs
new file mode 100644 (file)
index 0000000..f9d11f3
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 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.
+
+#![feature(question_mark)]
+
+// Make sure that the span of try shorthand does not include the trailing
+// semicolon;
+fn a() -> Result<i32, ()> {
+    Err(5)?; //~ ERROR 16:5: 16:12
+    Ok(1)
+}
+
+fn main() {}
index 29c1c584eed247aab1fea4511e1aec47d10a9039..7928c04b9df87b5f3130ea6158933a6a010ada15 100644 (file)
@@ -13,10 +13,6 @@ fn a(&self) {
         || self.b()
         //~^ ERROR no method named `b` found for type `&Self` in the current scope
         //~| ERROR mismatched types
-        //~| expected `()`
-        //~| found closure
-        //~| expected ()
-        //~| found closure
     }
 }
 fn main() {}
index 6cb825a9be736f7f9c3dbd049193e6f4c92bcaa1..66ed817ffa82637ac093bf2c6a01ff67402c62db 100644 (file)
@@ -16,5 +16,4 @@ struct Monster {
 fn main() {
     let _m = Monster(); //~ ERROR `Monster` is the name of a struct or
     //~^ HELP did you mean to write: `Monster { /* fields */ }`?
-    //~| HELP run `rustc --explain E0423` to see a detailed explanation
 }
index d875898f8bcc4004099b5965542c2cebbda215d6..60caca3da57edecfdd1528e3e80698d7317e3dd3 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-test -- FIXME #33010
+
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py false'
 
 fn main() {
index 048b640c0b311069b56710b0b2aad1ad595ad324..716a0ebf21cecc1baea852473cfdc83695653668 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-test -- FIXME #33010
+
 // This file was auto-generated using 'src/etc/generate-keyword-tests.py true'
 
 fn main() {
index be4166e43b504af4b790327367e33cbc053ff949..7355c70ff95e8af82e8477d9eca2a58538bbb791 100644 (file)
@@ -11,7 +11,6 @@
 // Lifetime annotation needed because we have no arguments.
 fn f() -> &isize {    //~ ERROR missing lifetime specifier
 //~^ HELP there is no value for it to be borrowed from
-//~| HELP run `rustc --explain E0106` to see a detailed explanation
 //~| HELP consider giving it a 'static lifetime
     panic!()
 }
@@ -19,7 +18,6 @@ fn f() -> &isize {    //~ ERROR missing lifetime specifier
 // Lifetime annotation needed because we have two by-reference parameters.
 fn g(_x: &isize, _y: &isize) -> &isize {    //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say whether it is borrowed from `_x` or `_y`
-//~| HELP run `rustc --explain E0106` to see a detailed explanation
     panic!()
 }
 
@@ -31,13 +29,11 @@ struct Foo<'a> {
 // and one on the reference.
 fn h(_x: &Foo) -> &isize { //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from
-//~| HELP run `rustc --explain E0106` to see a detailed explanation
     panic!()
 }
 
 fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier
 //~^ HELP this function's return type contains a borrowed value
-//~| HELP run `rustc --explain E0106` to see a detailed explanation
 //~| HELP consider giving it an explicit bounded or 'static lifetime
     panic!()
 }
index 441a8410700bfeff4a32a3309354bc443cca9d2d..19e253e3b8e53c26f6897a84a31a124716fdaa82 100644 (file)
@@ -27,5 +27,5 @@ fn bar() {
 
 #[forbid(warnings)]
 fn baz() {
-    while true {} //~ ERROR: warnings
+    while true {} //~ ERROR: infinite
 }
index 592e2b11905b335797b7b416ad12fbf172a5b62e..ad5e3aa3f0606c50bd2ffe1c6248b5b5c4b9dc88 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![deny = "foo"] //~ ERR malformed lint attribute
-#![allow(bar = "baz")] //~ ERR malformed lint attribute
+#![deny = "foo"] //~ ERROR malformed lint attribute
+#![allow(bar = "baz")] //~ ERROR malformed lint attribute
 
 fn main() { }
index 159a3d746034e81da9d2737b349a34c34cca6740..1498ed4d17ed71513f9d72c0aa49292bb60dc42c 100644 (file)
@@ -14,4 +14,4 @@
 #[deny(raw_pointer_derive)]
 #[allow(renamed_and_removed_lints)]
 #[deny(unused_variables)]
-fn main() { let unused = (); } //~ ERR unused
+fn main() { let unused = (); } //~ ERROR unused
index 9069356604131a789305a1a10202d9ce5afdfc24..aa7f535aa64b733849a6aba2e92a1ab6a8be815d 100644 (file)
@@ -15,4 +15,4 @@
 
 #[deny(raw_pointer_derive)] //~ WARN raw_pointer_derive has been removed
 #[deny(unused_variables)]
-fn main() { let unused = (); } //~ ERR unused
+fn main() { let unused = (); } //~ ERROR unused
index a2426d80f714dec1b0de3639e07257fb8e6abd64..ea26c3656e6911fa1daa742cdab1cf50bfdaa786 100644 (file)
@@ -14,4 +14,4 @@
 #[deny(unknown_features)]
 #[allow(renamed_and_removed_lints)]
 #[deny(unused)]
-fn main() { let unused = (); } //~ ERR unused
+fn main() { let unused = (); } //~ ERROR unused
index 2e85a323a1c414fffab214266913366a94a0ac1c..9e10ddf89ac33143887eaee31d55eec38c15a271 100644 (file)
@@ -10,4 +10,4 @@
 
 #[deny(unknown_features)] //~ WARN lint unknown_features has been renamed to unused_features
 #[deny(unused)]
-fn main() { let unused = (); } //~ ERR unused
+fn main() { let unused = (); } //~ ERROR unused
index 8f20a2c8ab7588ee2f981dd325d5f7757a8bac29..2de8d849d1915c3c67e4014a8da67c94e5625bee 100644 (file)
@@ -10,4 +10,4 @@
 
 #![allow(not_a_real_lint)] //~ WARN unknown lint
 #![deny(unused)]
-fn main() { let unused = (); } //~ ERR unused variable
+fn main() { let unused = (); } //~ ERROR unused variable
index 343622c5c1b048a714dfcaf5475606377c224cc1..03733cc2eb59600003c05546b0349a8e8ebfe279 100644 (file)
 
 macro_rules! test { () => { fn foo() -> i32 { 1; } } }
                                            //~^ ERROR not all control paths return a value
-                                           //~^^ HELP consider removing this semicolon
-                                           //~^^^ HELP run `rustc --explain E0269` to see a
+                                           //~| HELP consider removing this semicolon
 
 fn no_return() -> i32 {} //~ ERROR  not all control paths return a value
-                         //~^ HELP run `rustc --explain E0269` to see a detailed explanation
 
 fn bar(x: u32) -> u32 { //~ ERROR  not all control paths return a value
-                        //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     x * 2; //~ HELP consider removing this semicolon
 }
 
 fn baz(x: u64) -> u32 { //~ ERROR  not all control paths return a value
-                        //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     x * 2;
 }
 
index 4625669d5ecfe6b15b7a5ebe37c3288d20bdd4e3..815f75266322354be33e7e7d0742778020d17d98 100644 (file)
@@ -14,14 +14,12 @@ fn main() {
     //~^ HELP use a `ref` binding as shown
     //~| SUGGESTION let ref y = x;
     x; //~ ERROR use of moved value
-    //~^ HELP run `rustc --explain E0382` to see a detailed explanation
 
     let x = vec![1];
     let mut y = x;
     //~^ HELP use a `ref` binding as shown
     //~| SUGGESTION let ref mut y = x;
     x; //~ ERROR use of moved value
-    //~^ HELP run `rustc --explain E0382` to see a detailed explanation
 
     let x = (Some(vec![1]), ());
 
@@ -32,5 +30,4 @@ fn main() {
         _ => {},
     }
     x; //~ ERROR use of partially moved value
-    //~^ HELP run `rustc --explain E0382` to see a detailed explanation
 }
index 851e31b942ed1bca99742e3468edf8e7ca8b8c4b..8dd9994b234bac1a99aebfdecf5fbb2781d075eb 100644 (file)
@@ -28,7 +28,7 @@ fn foo<'z>() where &'z (): Sized {
     //[verbose]~| found `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
     //[normal]~^^^^ ERROR mismatched types
     //[normal]~| expected `()`
-    //[normal]~| found  `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
+    //[normal]~| found `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
 
 
     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>;
index 8877377a6ec9676355a20ce148cf0f067f2bc9b5..1d04679fd11e799dd9ebb620163e378d939b02e6 100644 (file)
@@ -27,54 +27,46 @@ fn h1() -> i32 {
     a.I
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a` module, use `a::I`
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h2() -> i32 {
     a.g()
         //~^ ERROR E0425
         //~| HELP To call a function from the `a` module, use `a::g(..)`
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h3() -> i32 {
     a.b.J
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a` module, use `a::b`
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h4() -> i32 {
     a::b.J
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a::b` module, use `a::b::J`
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h5() -> i32 {
     a.b.f()
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a` module, use `a::b`
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h6() -> i32 {
     a::b.f()
         //~^ ERROR E0425
         //~| HELP To call a function from the `a::b` module, use `a::b::f(..)`
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h7() {
     a::b
         //~^ ERROR E0425
         //~| HELP Module `a::b` cannot be the value of an expression
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h8() -> i32 {
     a::b()
         //~^ ERROR E0425
         //~| HELP No function corresponds to `a::b(..)`
-        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/symbol-names/issue-32709.rs b/src/test/compile-fail/symbol-names/issue-32709.rs
deleted file mode 100644 (file)
index f9d11f3..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 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.
-
-#![feature(question_mark)]
-
-// Make sure that the span of try shorthand does not include the trailing
-// semicolon;
-fn a() -> Result<i32, ()> {
-    Err(5)?; //~ ERROR 16:5: 16:12
-    Ok(1)
-}
-
-fn main() {}
index 3fffb2e19f289c5c96c390a30b7788d62b6f3cdf..9cf65a9d00d07366a3d44f87eae122c00ebeb63b 100644 (file)
@@ -74,7 +74,7 @@ trait Trait {
 
 impl Trait for usize {
     fn method<G: Getter<usize>>(&self) {}
-    //~^ G : Getter<usize>` appears on the impl method but not on the corresponding trait method
+    //~^ ERROR `G: Getter<usize>` appears on the impl method
 }
 
 fn main() {}
index 29360e58b5bd3471424cdcb5d5e25c497ac31172..fc2ed83b2724d0a2d0be5e9b2b04ab1ca592fb28 100644 (file)
@@ -13,10 +13,8 @@ fn main() {
     //~^ ERROR expected a path
     //~| HELP try adding parentheses
     //~| SUGGESTION let _: &(Copy + 'static);
-    //~| HELP run `rustc --explain E0178` to see a detailed explanation
     let _: &'static Copy + 'static;
     //~^ ERROR expected a path
     //~| HELP try adding parentheses
     //~| SUGGESTION let _: &'static (Copy + 'static);
-    //~| HELP run `rustc --explain E0178` to see a detailed explanation
 }
index 6950bce7304c0256cca77c90114392471240b268..a8ff1bae7a71a7ebe1a114721776811cf99bc7ba 100644 (file)
@@ -16,13 +16,11 @@ fn check<T: Iterator, U: ?Sized>() {
     // suggest a where-clause, if needed
     mem::size_of::<U>();
     //~^ ERROR `U: std::marker::Sized` is not satisfied
-    //~| HELP E0277
     //~| HELP consider adding a `where U: std::marker::Sized` bound
     //~| NOTE required by `std::mem::size_of`
 
     mem::size_of::<Misc<U>>();
     //~^ ERROR `U: std::marker::Sized` is not satisfied
-    //~| HELP E0277
     //~| HELP consider adding a `where U: std::marker::Sized` bound
     //~| NOTE required because it appears within the type `Misc<U>`
     //~| NOTE required by `std::mem::size_of`
@@ -31,13 +29,11 @@ fn check<T: Iterator, U: ?Sized>() {
 
     <u64 as From<T>>::from;
     //~^ ERROR `u64: std::convert::From<T>` is not satisfied
-    //~| HELP E0277
     //~| HELP consider adding a `where u64: std::convert::From<T>` bound
     //~| NOTE required by `std::convert::From::from`
 
     <u64 as From<<T as Iterator>::Item>>::from;
     //~^ ERROR `u64: std::convert::From<<T as std::iter::Iterator>::Item>` is not satisfied
-    //~| HELP E0277
     //~| HELP consider adding a `where u64:
     //~| NOTE required by `std::convert::From::from`
 
@@ -45,20 +41,17 @@ fn check<T: Iterator, U: ?Sized>() {
 
     <Misc<_> as From<T>>::from;
     //~^ ERROR `Misc<_>: std::convert::From<T>` is not satisfied
-    //~| HELP E0277
     //~| NOTE required by `std::convert::From::from`
 
     // ... and also not if the error is not related to the type
 
     mem::size_of::<[T]>();
     //~^ ERROR `[T]: std::marker::Sized` is not satisfied
-    //~| HELP E0277
     //~| NOTE `[T]` does not have a constant size
     //~| NOTE required by `std::mem::size_of`
 
     mem::size_of::<[&U]>();
     //~^ ERROR `[&U]: std::marker::Sized` is not satisfied
-    //~| HELP E0277
     //~| NOTE `[&U]` does not have a constant size
     //~| NOTE required by `std::mem::size_of`
 }
index 014fa35c309e6cb79ce9f542733fe7d971e829cd..e81ae5d743939276ec3bb78eae3be3cca259ece9 100644 (file)
@@ -24,10 +24,8 @@ fn main() {
     {
         extern crate crate_a1 as a;
         a::try_foo(foo2); //~ ERROR mismatched types
-                          //~^ HELP run
-                          //~^^ NOTE Perhaps two different versions of crate `crate_a1`
+                          //~^ NOTE Perhaps two different versions of crate `crate_a1`
         a::try_bar(bar2); //~ ERROR mismatched types
-                          //~^ HELP run
-                          //~^^ NOTE Perhaps two different versions of crate `crate_a1`
+                          //~^ NOTE Perhaps two different versions of crate `crate_a1`
     }
 }
index f7fed32cb5af2ec5f1b9547f3505b33b15acf8e2..862d842d62c2316abdd7a1584fe5744a09182920 100644 (file)
 struct SomeStruct<A> { x: u32 }
 //~^ ERROR parameter `A` is never used
 //~| HELP PhantomData
-//~| HELP run `rustc --explain E0392` to see a detailed explanation
 
 enum SomeEnum<A> { Nothing }
 //~^ ERROR parameter `A` is never used
 //~| HELP PhantomData
-//~| HELP run `rustc --explain E0392` to see a detailed explanation
 
 // Here T might *appear* used, but in fact it isn't.
 enum ListCell<T> {
 //~^ ERROR parameter `T` is never used
 //~| HELP PhantomData
-//~| HELP run `rustc --explain E0392` to see a detailed explanation
     Cons(Box<ListCell<T>>),
     Nil
 }
index bcadd1f39fa98eb55eaa7bdfd23906594eeb3785..8caf2e0c0a47732cb6991a18deed40722ea131d0 100644 (file)
@@ -10,4 +10,4 @@
 
 // compile-flags: -Z parse-only
 
-# //~ ERROR 13:1: 13:2 error: expected `[`, found `<eof>`
+# //~ ERROR 13:1: 13:2: expected `[`, found `<eof>`
diff --git a/src/test/parse-fail/keyword-do-as-identifier.rs b/src/test/parse-fail/keyword-do-as-identifier.rs
deleted file mode 100644 (file)
index 5cc14df..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2013 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.
-
-// compile-flags: -Z parse-only
-
-fn main() {
-    let do = "bar"; //~ error: ident
-}
diff --git a/src/test/parse-fail/keyword-priv-as-identifier.rs b/src/test/parse-fail/keyword-priv-as-identifier.rs
deleted file mode 100644 (file)
index e80feb6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2013 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.
-
-// compile-flags: -Z parse-only
-
-// This file was auto-generated using 'src/etc/generate-keyword-tests.py priv'
-
-fn main() {
-    let priv = "foo"; //~ error: ident
-}
index bb73fda67bddb962e79361d3be5ed95030779a20..30bcafd104945cfa5e775fe59a56aef81665b215 100644 (file)
@@ -6,5 +6,4 @@ all:
        cp foo.rs $(TMPDIR)
        cd $(TMPDIR)
        -$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>$(LOG)
-       grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","spans":\[{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19,"text":\[{"text":"    let x = 42 + y;","highlight_start":18,"highlight_end":19}\]}\],"children":\[\]}' $(LOG)
-       grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","spans":\[{.*}\],"children":\[{"message":"the .*","code":null,"level":"help","spans":\[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0,"text":\[{.*}\]}\],"children":\[\]},{"message":"  <u8 as std::ops::Add>","code":null,"level":"help",' $(LOG)
+       diff foo.json $(LOG)
diff --git a/src/test/run-make/json-errors/foo.json b/src/test/run-make/json-errors/foo.json
new file mode 100644 (file)
index 0000000..bde669a
--- /dev/null
@@ -0,0 +1,4 @@
+{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\nAn unresolved name was used. Example of erroneous codes:\n\n```compile_fail\nsomething_that_doesnt_exist::foo;\n// error: unresolved name `something_that_doesnt_exist::foo`\n\n// or:\n\ntrait Foo {\n    fn bar() {\n        Self; // error: unresolved name `Self`\n    }\n}\n\n// or:\n\nlet x = unknown_variable;  // error: unresolved name `unknown_variable`\n```\n\nPlease verify that the name wasn't misspelled and ensure that the\nidentifier being referred to is valid for the given situation. Example:\n\n```\nenum something_that_does_exist {\n    Foo,\n}\n```\n\nOr:\n\n```\nmod something_that_does_exist {\n    pub static foo : i32 = 0i32;\n}\n\nsomething_that_does_exist::foo; // ok!\n```\n\nOr:\n\n```\nlet unknown_variable = 12u32;\nlet x = unknown_variable; // ok!\n```\n"},"level":"error","spans":[{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19,"text":[{"text":"    let x = 42 + y;","highlight_start":18,"highlight_end":19}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null}
+{"message":"mismatched types:\n expected `u8`,\n    found `i32`","code":{"code":"E0308","explanation":"\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail\nlet x: i32 = \"I am not a number!\";\n//     ~~~   ~~~~~~~~~~~~~~~~~~~~\n//      |             |\n//      |    initializing expression;\n//      |    compiler infers type `&str`\n//      |\n//    type `i32` assigned to variable `x`\n```\n\nAnother situation in which this occurs is when you attempt to use the `try!`\nmacro inside a function that does not return a `Result<T, E>`:\n\n```compile_fail\nuse std::fs::File;\n\nfn main() {\n    let mut f = try!(File::create(\"foo.txt\"));\n}\n```\n\nThis code gives an error like this:\n\n```text\n<std macros>:5:8: 6:42 error: mismatched types:\n expected `()`,\n     found `core::result::Result<_, _>`\n (expected (),\n     found enum `core::result::Result`) [E0308]\n```\n\n`try!` returns a `Result<T, E>`, and so the function must. But `main()` has\n`()` as its return type, hence the error.\n"},"level":"error","spans":[{"file_name":"foo.rs","byte_start":511,"byte_end":516,"line_start":14,"line_end":14,"column_start":12,"column_end":17,"text":[{"text":"    42u8 + 42i32;","highlight_start":12,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null}
+{"message":"the trait bound `u8: std::ops::Add<i32>` is not satisfied","code":{"code":"E0277","explanation":"\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail\n// here we declare the Foo trait with a bar method\ntrait Foo {\n    fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n    foo.bar();\n}\n\nfn main() {\n    // we now call the method with the i32 type, which doesn't implement\n    // the Foo trait\n    some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n    fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n    foo.bar(); // we can now use this method since i32 implements the\n               // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n    fn bar(&self) {}\n}\n\nfn main() {\n    some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n```compile_fail\nfn some_func<T>(foo: T) {\n    println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n                           //        implemented for the type `T`\n}\n\nfn main() {\n    // We now call the method with the i32 type,\n    // which *does* implement the Debug trait.\n    some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n    println!(\"{:?}\", foo);\n}\n\nfn main() {\n    // Calling the method is still fine, as i32 implements Debug.\n    some_func(5i32);\n\n    // This would fail to compile now:\n    // struct WithoutDebug;\n    // some_func(WithoutDebug);\n}\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n```\n\n"},"level":"error","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":"    42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[{"message":"the following implementations were found:","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":"    42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":"  <u8 as std::ops::Add>","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":"    42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":"  <&'a u8 as std::ops::Add<u8>>","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":"    42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":"  <u8 as std::ops::Add<&'a u8>>","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":"    42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":"  <&'b u8 as std::ops::Add<&'a u8>>","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":"    42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null}],"rendered":null}
+{"message":"aborting due to 2 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":null}
index 418a0bc7121cbd624be4797e47fd78b5226b59be..c3da891933f6df1bb7fcf054063a6afa036d1032 100644 (file)
@@ -28,7 +28,9 @@ pub enum ErrorKind {
 impl FromStr for ErrorKind {
     type Err = ();
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match &s.trim_right_matches(':') as &str {
+        let s = s.to_uppercase();
+        let part0: &str = s.split(':').next().unwrap();
+        match part0 {
             "HELP" => Ok(ErrorKind::Help),
             "ERROR" => Ok(ErrorKind::Error),
             "NOTE" => Ok(ErrorKind::Note),
@@ -52,7 +54,8 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
-pub struct ExpectedError {
+#[derive(Debug)]
+pub struct Error {
     pub line_num: usize,
     /// What kind of message we expect (e.g. warning, error, suggestion).
     /// `None` if not specified or unknown message kind.
@@ -73,7 +76,7 @@ enum WhichLine { ThisLine, FollowPrevious(usize), AdjustBackward(usize) }
 ///
 /// If cfg is not None (i.e., in an incremental test), then we look
 /// for `//[X]~` instead, where `X` is the current `cfg`.
-pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<ExpectedError> {
+pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
     let rdr = BufReader::new(File::open(testfile).unwrap());
 
     // `last_nonfollow_error` tracks the most recently seen
@@ -113,7 +116,7 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
                   line_num: usize,
                   line: &str,
                   tag: &str)
-                  -> Option<(WhichLine, ExpectedError)> {
+                  -> Option<(WhichLine, Error)> {
     let start = match line.find(tag) { Some(i) => i, None => return None };
     let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' {
         (true, 0)
@@ -121,15 +124,30 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
         (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count())
     };
     let kind_start = start + tag.len() + adjusts + (follow as usize);
-    let kind = line[kind_start..].split_whitespace()
-                                 .next()
-                                 .expect("Encountered unexpected empty comment")
-                                 .parse::<ErrorKind>()
-                                 .ok();
-    let letters = line[kind_start..].chars();
-    let msg = letters.skip_while(|c| c.is_whitespace())
-                     .skip_while(|c| !c.is_whitespace())
-                     .collect::<String>().trim().to_owned();
+    let (kind, msg);
+    match
+        line[kind_start..].split_whitespace()
+                          .next()
+                          .expect("Encountered unexpected empty comment")
+                          .parse::<ErrorKind>()
+    {
+        Ok(k) => {
+            // If we find `//~ ERROR foo` or something like that:
+            kind = Some(k);
+            let letters = line[kind_start..].chars();
+            msg = letters.skip_while(|c| c.is_whitespace())
+                         .skip_while(|c| !c.is_whitespace())
+                         .collect::<String>();
+        }
+        Err(_) => {
+            // Otherwise we found `//~ foo`:
+            kind = None;
+            let letters = line[kind_start..].chars();
+            msg = letters.skip_while(|c| c.is_whitespace())
+                         .collect::<String>();
+        }
+    }
+    let msg = msg.trim().to_owned();
 
     let (which, line_num) = if follow {
         assert!(adjusts == 0, "use either //~| or //~^, not both.");
@@ -145,7 +163,7 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
 
     debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}",
            line_num, tag, which, kind, msg);
-    Some((which, ExpectedError { line_num: line_num,
-                                 kind: kind,
-                                 msg: msg, }))
+    Some((which, Error { line_num: line_num,
+                         kind: kind,
+                         msg: msg, }))
 }
index e92b0c8728099a54101294e91e34be0a545af156..8e999139a249b2c7d60d674a147a07c00174599b 100644 (file)
@@ -21,6 +21,7 @@
 extern crate libc;
 extern crate test;
 extern crate getopts;
+extern crate serialize as rustc_serialize;
 
 #[macro_use]
 extern crate log;
@@ -40,6 +41,7 @@
 
 pub mod procsrv;
 pub mod util;
+mod json;
 pub mod header;
 pub mod runtest;
 pub mod common;
index 6358d19ff0906da57d4bfbd586eeefebec17df2b..8770d4b1f4255ccea83114fb63265e701ca857c0 100644 (file)
@@ -12,7 +12,8 @@
 use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
 use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
 use common::{Incremental};
-use errors::{self, ErrorKind};
+use errors::{self, ErrorKind, Error};
+use json;
 use header::TestProps;
 use header;
 use procsrv;
@@ -26,7 +27,7 @@
 use std::io::BufReader;
 use std::io::prelude::*;
 use std::net::TcpStream;
-use std::path::{Path, PathBuf, Component};
+use std::path::{Path, PathBuf};
 use std::process::{Command, Output, ExitStatus};
 
 pub fn run(config: Config, testpaths: &TestPaths) {
@@ -944,7 +945,7 @@ fn check_error_patterns(revision: Option<&str>,
                        testpaths.file.display()));
     }
     let mut next_err_idx = 0;
-    let mut next_err_pat = &props.error_patterns[next_err_idx];
+    let mut next_err_pat = props.error_patterns[next_err_idx].trim();
     let mut done = false;
     for line in output_to_check.lines() {
         if line.contains(next_err_pat) {
@@ -955,7 +956,7 @@ fn check_error_patterns(revision: Option<&str>,
                 done = true;
                 break;
             }
-            next_err_pat = &props.error_patterns[next_err_idx];
+            next_err_pat = props.error_patterns[next_err_idx].trim();
         }
     }
     if done { return; }
@@ -998,208 +999,110 @@ fn check_forbid_output(revision: Option<&str>,
 }
 
 fn check_expected_errors(revision: Option<&str>,
-                         expected_errors: Vec<errors::ExpectedError>,
+                         expected_errors: Vec<errors::Error>,
                          testpaths: &TestPaths,
                          proc_res: &ProcRes) {
-    // true if we found the error in question
-    let mut found_flags = vec![false; expected_errors.len()];
-
     if proc_res.status.success() {
         fatal_proc_rec(revision, "process did not return an error status", proc_res);
     }
 
-    let prefixes = expected_errors.iter().map(|ee| {
-        let expected = format!("{}:{}:", testpaths.file.display(), ee.line_num);
-        // On windows just translate all '\' path separators to '/'
-        expected.replace(r"\", "/")
-    }).collect::<Vec<String>>();
+    let file_name =
+        format!("{}", testpaths.file.display())
+        .replace(r"\", "/"); // on windows, translate all '\' path separators to '/'
 
     // If the testcase being checked contains at least one expected "help"
     // message, then we'll ensure that all "help" messages are expected.
     // Otherwise, all "help" messages reported by the compiler will be ignored.
     // This logic also applies to "note" messages.
-    let (expect_help, expect_note) =
-        expected_errors.iter()
-                        .fold((false, false),
-                              |(acc_help, acc_note), ee|
-                                  (acc_help || ee.kind == Some(ErrorKind::Help),
-                                   acc_note || ee.kind == Some(ErrorKind::Note)));
-
-    // Scan and extract our error/warning messages,
-    // which look like:
-    //    filename:line1:col1: line2:col2: *error:* msg
-    //    filename:line1:col1: line2:col2: *warning:* msg
-    // where line1:col1: is the starting point, line2:col2:
-    // is the ending point, and * represents ANSI color codes.
-    //
-    // This pattern is ambiguous on windows, because filename may contain
-    // a colon, so any path prefix must be detected and removed first.
+    let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help));
+    let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
+
+    // Parse the JSON output from the compiler and extract out the messages.
+    let actual_errors = json::parse_output(&file_name, &proc_res.stderr);
     let mut unexpected = 0;
     let mut not_found = 0;
-    for line in proc_res.stderr.lines() {
-        let mut was_expected = false;
-        let mut prev = 0;
-        for (i, ee) in expected_errors.iter().enumerate() {
-            if !found_flags[i] {
-                debug!("prefix={} ee.kind={:?} ee.msg={} line={}",
-                       prefixes[i],
-                       ee.kind,
-                       ee.msg,
-                       line);
-                // Suggestions have no line number in their output, so take on the line number of
-                // the previous expected error
-                if ee.kind == Some(ErrorKind::Suggestion) {
-                    assert!(expected_errors[prev].kind == Some(ErrorKind::Help),
-                            "SUGGESTIONs must be preceded by a HELP");
-                    if line.contains(&ee.msg) {
-                        found_flags[i] = true;
-                        was_expected = true;
-                        break;
-                    }
-                }
-                if
-                    (prefix_matches(line, &prefixes[i]) || continuation(line)) &&
-                    (ee.kind.is_none() || line.contains(&ee.kind.as_ref().unwrap().to_string())) &&
-                    line.contains(&ee.msg)
-                {
-                    found_flags[i] = true;
-                    was_expected = true;
-                    break;
-                }
+    let mut found = vec![false; expected_errors.len()];
+    for actual_error in &actual_errors {
+        let opt_index =
+            expected_errors
+            .iter()
+            .enumerate()
+            .position(|(index, expected_error)| {
+                !found[index] &&
+                    actual_error.line_num == expected_error.line_num &&
+                    (expected_error.kind.is_none() ||
+                     actual_error.kind == expected_error.kind) &&
+                    actual_error.msg.contains(&expected_error.msg)
+            });
+
+        match opt_index {
+            Some(index) => {
+                // found a match, everybody is happy
+                assert!(!found[index]);
+                found[index] = true;
             }
-            prev = i;
-        }
-
-        // ignore this msg which gets printed at the end
-        if line.contains("aborting due to") {
-            was_expected = true;
-        }
 
-        if !was_expected && is_unexpected_compiler_message(line, expect_help, expect_note) {
-            error(revision, &format!("unexpected compiler message: '{}'", line));
-            unexpected += 1;
+            None => {
+                if is_unexpected_compiler_message(actual_error,
+                                                  expect_help,
+                                                  expect_note) {
+                    error(revision,
+                          &format!("{}:{}: unexpected {:?}: '{}'",
+                                   file_name,
+                                   actual_error.line_num,
+                                   actual_error.kind.as_ref()
+                                                    .map_or(String::from("message"),
+                                                            |k| k.to_string()),
+                                   actual_error.msg));
+                    unexpected += 1;
+                }
+            }
         }
     }
 
-    for (i, &flag) in found_flags.iter().enumerate() {
-        if !flag {
-            let ee = &expected_errors[i];
-            error(revision, &format!("expected {} on line {} not found: {}",
-                                     ee.kind.as_ref()
-                                            .map_or("message".into(),
-                                                    |k| k.to_string()),
-                                     ee.line_num, ee.msg));
+    // anything not yet found is a problem
+    for (index, expected_error) in expected_errors.iter().enumerate() {
+        if !found[index] {
+            error(revision,
+                  &format!("{}:{}: expected {} not found: {}",
+                           file_name,
+                           expected_error.line_num,
+                           expected_error.kind.as_ref()
+                                              .map_or("message".into(),
+                                                      |k| k.to_string()),
+                           expected_error.msg));
             not_found += 1;
         }
     }
 
     if unexpected > 0 || not_found > 0 {
-        fatal_proc_rec(
-            revision,
-            &format!("{} unexpected errors found, {} expected errors not found",
-                     unexpected, not_found),
-            proc_res);
-    }
-
-    fn prefix_matches(line: &str, prefix: &str) -> bool {
-        use std::ascii::AsciiExt;
-        // On windows just translate all '\' path separators to '/'
-        let line = line.replace(r"\", "/");
-        if cfg!(windows) {
-            line.to_ascii_lowercase().starts_with(&prefix.to_ascii_lowercase())
-        } else {
-            line.starts_with(prefix)
-        }
-    }
-
-    // A multi-line error will have followup lines which start with a space
-    // or open paren.
-    fn continuation( line: &str) -> bool {
-        line.starts_with(" ") || line.starts_with("(")
-    }
-}
-
-fn is_unexpected_compiler_message(line: &str, expect_help: bool, expect_note: bool) -> bool {
-    let mut c = Path::new(line).components();
-    let line = match c.next() {
-        Some(Component::Prefix(_)) => c.as_path().to_str().unwrap(),
-        _ => line,
-    };
-
-    let mut i = 0;
-    return scan_until_char(line, ':', &mut i) &&
-        scan_char(line, ':', &mut i) &&
-        scan_integer(line, &mut i) &&
-        scan_char(line, ':', &mut i) &&
-        scan_integer(line, &mut i) &&
-        scan_char(line, ':', &mut i) &&
-        scan_char(line, ' ', &mut i) &&
-        scan_integer(line, &mut i) &&
-        scan_char(line, ':', &mut i) &&
-        scan_integer(line, &mut i) &&
-        scan_char(line, ' ', &mut i) &&
-        (scan_string(line, "error", &mut i) ||
-         scan_string(line, "warning", &mut i) ||
-         (expect_help && scan_string(line, "help", &mut i)) ||
-         (expect_note && scan_string(line, "note", &mut i))
-        );
-}
-
-fn scan_until_char(haystack: &str, needle: char, idx: &mut usize) -> bool {
-    if *idx >= haystack.len() {
-        return false;
-    }
-    let opt = haystack[(*idx)..].find(needle);
-    if opt.is_none() {
-        return false;
-    }
-    *idx = opt.unwrap();
-    return true;
-}
-
-fn scan_char(haystack: &str, needle: char, idx: &mut usize) -> bool {
-    if *idx >= haystack.len() {
-        return false;
-    }
-    let ch = haystack[*idx..].chars().next().unwrap();
-    if ch != needle {
-        return false;
-    }
-    *idx += ch.len_utf8();
-    return true;
-}
-
-fn scan_integer(haystack: &str, idx: &mut usize) -> bool {
-    let mut i = *idx;
-    while i < haystack.len() {
-        let ch = haystack[i..].chars().next().unwrap();
-        if ch < '0' || '9' < ch {
-            break;
-        }
-        i += ch.len_utf8();
-    }
-    if i == *idx {
-        return false;
+        error(revision,
+              &format!("{} unexpected errors found, {} expected errors not found",
+                       unexpected, not_found));
+        print!("status: {}\ncommand: {}\n",
+               proc_res.status, proc_res.cmdline);
+        println!("actual errors (from JSON output): {:#?}\n", actual_errors);
+        println!("expected errors (from test file): {:#?}\n", expected_errors);
+        panic!();
     }
-    *idx = i;
-    return true;
 }
 
-fn scan_string(haystack: &str, needle: &str, idx: &mut usize) -> bool {
-    let mut haystack_i = *idx;
-    let mut needle_i = 0;
-    while needle_i < needle.len() {
-        if haystack_i >= haystack.len() {
-            return false;
-        }
-        let ch = haystack[haystack_i..].chars().next().unwrap();
-        haystack_i += ch.len_utf8();
-        if !scan_char(needle, ch, &mut needle_i) {
-            return false;
-        }
+/// Returns true if we should report an error about `actual_error`,
+/// which did not match any of the expected error. We always require
+/// errors/warnings to be explicitly listed, but only require
+/// helps/notes if there are explicit helps/notes given.
+fn is_unexpected_compiler_message(actual_error: &Error,
+                                  expect_help: bool,
+                                  expect_note: bool)
+                                  -> bool {
+    match actual_error.kind {
+        Some(ErrorKind::Help) => expect_help,
+        Some(ErrorKind::Note) => expect_note,
+        Some(ErrorKind::Error) => true,
+        Some(ErrorKind::Warning) => true,
+        Some(ErrorKind::Suggestion) => false,
+        None => false
     }
-    *idx = haystack_i;
-    return true;
 }
 
 struct ProcArgs {
@@ -1444,6 +1347,37 @@ fn make_compile_args<F>(config: &Config,
                         "-L".to_owned(),
                         config.build_base.to_str().unwrap().to_owned(),
                         format!("--target={}", target));
+
+    match config.mode {
+        CompileFail |
+        ParseFail |
+        Incremental => {
+            // If we are extracting and matching errors in the new
+            // fashion, then you want JSON mode. Old-skool error
+            // patterns still match the raw compiler output.
+            if props.error_patterns.is_empty() {
+                args.extend(["--error-format",
+                             "json",
+                             "-Z",
+                             "unstable-options"]
+                            .iter()
+                            .map(|s| s.to_string()));
+            }
+        }
+
+        RunFail |
+        RunPass |
+        RunPassValgrind |
+        Pretty |
+        DebugInfoGdb |
+        DebugInfoLldb |
+        Codegen |
+        Rustdoc |
+        CodegenUnits => {
+            // do not use JSON output
+        }
+    }
+
     args.extend_from_slice(&extras);
     if !props.no_prefer_dynamic {
         args.push("-C".to_owned());