about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-01-26 00:42:08 +0000
committerbors <bors@rust-lang.org>2016-01-26 00:42:08 +0000
commitfaf6d1e87391b25196b35909c3c95e5d873cacf0 (patch)
tree2bc19619fba5216c21015aee7cee4bec5b263b06
parenteceb96b40dedd903ddfca6df97bb1e5749b87787 (diff)
parent43b3681588ef40c78c794548d67a8f50101bc8ad (diff)
downloadrust-faf6d1e87391b25196b35909c3c95e5d873cacf0.tar.gz
rust-faf6d1e87391b25196b35909c3c95e5d873cacf0.zip
Auto merge of #31065 - nrc:ident-correct, r=pnkfelix
This PR adds some minor error correction to the parser - if there is a missing ident, we recover and carry on. It also makes compilation more robust so that non-fatal errors (which is still most of them, unfortunately) in parsing do not cause us to abort compilation. The effect is that a program with a missing or incorrect ident can get all the way to type checking.
-rw-r--r--src/librustc/middle/cstore.rs36
-rw-r--r--src/librustc/middle/lang_items.rs1
-rw-r--r--src/librustc/session/mod.rs7
-rw-r--r--src/librustc_driver/driver.rs97
-rw-r--r--src/librustc_metadata/creader.rs19
-rw-r--r--src/librustc_passes/const_fn.rs5
-rw-r--r--src/librustc_resolve/lib.rs6
-rw-r--r--src/librustc_typeck/check/mod.rs31
-rw-r--r--src/libsyntax/errors/mod.rs7
-rw-r--r--src/libsyntax/ext/expand.rs7
-rw-r--r--src/libsyntax/parse/mod.rs20
-rw-r--r--src/libsyntax/parse/parser.rs107
-rw-r--r--src/test/compile-fail/cfg-non-opt-expr.rs2
-rw-r--r--src/test/compile-fail/double-type-import.rs2
-rw-r--r--src/test/compile-fail/import-from-missing.rs1
-rw-r--r--src/test/compile-fail/import.rs2
-rw-r--r--src/test/compile-fail/import2.rs3
-rw-r--r--src/test/compile-fail/macro-reexport-malformed-1.rs3
-rw-r--r--src/test/compile-fail/macro-reexport-malformed-2.rs3
-rw-r--r--src/test/compile-fail/macro-reexport-malformed-3.rs3
-rw-r--r--src/test/compile-fail/macro-reexport-undef.rs2
-rw-r--r--src/test/compile-fail/macro-use-bad-args-1.rs5
-rw-r--r--src/test/compile-fail/macro-use-bad-args-2.rs5
-rw-r--r--src/test/compile-fail/parse-error-correct.rs19
-rw-r--r--src/test/compile-fail/privacy3.rs1
-rw-r--r--src/test/compile-fail/self_type_keyword.rs1
-rw-r--r--src/test/compile-fail/use-mod.rs1
27 files changed, 233 insertions, 163 deletions
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 27745a85935..973fd65beb3 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -267,24 +267,28 @@ impl InlinedItem {
 
 // FIXME: find a better place for this?
 pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
-    let say = |s: &str| {
-        match (sp, sess) {
-            (_, None) => panic!("{}", s),
-            (Some(sp), Some(sess)) => sess.span_err(sp, s),
-            (None, Some(sess)) => sess.err(s),
+    let mut err_count = 0;
+    {
+        let mut say = |s: &str| {
+            match (sp, sess) {
+                (_, None) => panic!("{}", s),
+                (Some(sp), Some(sess)) => sess.span_err(sp, s),
+                (None, Some(sess)) => sess.err(s),
+            }
+            err_count += 1;
+        };
+        if s.is_empty() {
+            say("crate name must not be empty");
+        }
+        for c in s.chars() {
+            if c.is_alphanumeric() { continue }
+            if c == '_'  { continue }
+            say(&format!("invalid character `{}` in crate name: `{}`", c, s));
         }
-    };
-    if s.is_empty() {
-        say("crate name must not be empty");
-    }
-    for c in s.chars() {
-        if c.is_alphanumeric() { continue }
-        if c == '_'  { continue }
-        say(&format!("invalid character `{}` in crate name: `{}`", c, s));
     }
-    match sess {
-        Some(sess) => sess.abort_if_errors(),
-        None => {}
+
+    if err_count > 0 {
+        sess.unwrap().abort_if_errors();
     }
 }
 
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index ec55daca9ec..6e57d5dd1ba 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -239,7 +239,6 @@ pub fn collect_language_items(session: &Session,
     collector.collect(krate);
     let LanguageItemCollector { mut items, .. } = collector;
     weak_lang_items::check_crate(krate, session, &mut items);
-    session.abort_if_errors();
     items
 }
 
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 2f3af1c0d09..975ec0e709b 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -176,14 +176,15 @@ impl Session {
     pub fn abort_if_errors(&self) {
         self.diagnostic().abort_if_errors();
     }
-    pub fn abort_if_new_errors<F>(&self, mut f: F)
-        where F: FnMut()
+    pub fn abort_if_new_errors<F, T>(&self, f: F) -> T
+        where F: FnOnce() -> T
     {
         let count = self.err_count();
-        f();
+        let result = f();
         if self.err_count() > count {
             self.abort_if_errors();
         }
+        result
     }
     pub fn span_warn(&self, sp: Span, msg: &str) {
         self.diagnostic().span_warn(sp, msg)
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index fd5f711c9d6..36074a7ae02 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -69,8 +69,8 @@ pub fn compile_input(sess: Session,
         let state = $make_state;
         (control.$point.callback)(state);
 
-        $tsess.abort_if_errors();
         if control.$point.stop == Compilation::Stop {
+            $tsess.abort_if_errors();
             return;
         }
     })}
@@ -470,7 +470,11 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
     let mut feature_gated_cfgs = vec![];
     krate = time(time_passes, "configuration 1", || {
-        syntax::config::strip_unconfigured_items(sess.diagnostic(), krate, &mut feature_gated_cfgs)
+        sess.abort_if_new_errors(|| {
+            syntax::config::strip_unconfigured_items(sess.diagnostic(),
+                                                     krate,
+                                                     &mut feature_gated_cfgs)
+        })
     });
 
     *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs);
@@ -481,13 +485,15 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     });
 
     time(time_passes, "gated macro checking", || {
-        let features = syntax::feature_gate::check_crate_macros(sess.codemap(),
-                                                                &sess.parse_sess.span_diagnostic,
-                                                                &krate);
-
-        // these need to be set "early" so that expansion sees `quote` if enabled.
-        *sess.features.borrow_mut() = features;
-        sess.abort_if_errors();
+        sess.abort_if_new_errors(|| {
+            let features =
+              syntax::feature_gate::check_crate_macros(sess.codemap(),
+                                                       &sess.parse_sess.span_diagnostic,
+                                                       &krate);
+
+            // these need to be set "early" so that expansion sees `quote` if enabled.
+            *sess.features.borrow_mut() = features;
+        });
     });
 
 
@@ -525,7 +531,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     let Registry { syntax_exts, early_lint_passes, late_lint_passes, lint_groups,
                    llvm_passes, attributes, .. } = registry;
 
-    {
+    sess.abort_if_new_errors(|| {
         let mut ls = sess.lint_store.borrow_mut();
         for pass in early_lint_passes {
             ls.register_early_pass(Some(sess), true, pass);
@@ -540,17 +546,14 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
         *sess.plugin_llvm_passes.borrow_mut() = llvm_passes;
         *sess.plugin_attributes.borrow_mut() = attributes.clone();
-    }
+    });
 
     // Lint plugins are registered; now we can process command line flags.
     if sess.opts.describe_lints {
         super::describe_lints(&*sess.lint_store.borrow(), true);
         return None;
     }
-    sess.lint_store.borrow_mut().process_command_line(sess);
-
-    // Abort if there are errors from lint processing or a plugin registrar.
-    sess.abort_if_errors();
+    sess.abort_if_new_errors(|| sess.lint_store.borrow_mut().process_command_line(sess));
 
     krate = time(time_passes, "expansion", || {
         // Windows dlls do not have rpaths, so they don't know how to find their
@@ -594,29 +597,36 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // much as possible (e.g. help the programmer avoid platform
     // specific differences)
     time(time_passes, "complete gated feature checking 1", || {
-        let features = syntax::feature_gate::check_crate(sess.codemap(),
-                                                         &sess.parse_sess.span_diagnostic,
-                                                         &krate,
-                                                         &attributes,
-                                                         sess.opts.unstable_features);
-        *sess.features.borrow_mut() = features;
-        sess.abort_if_errors();
+        sess.abort_if_new_errors(|| {
+            let features = syntax::feature_gate::check_crate(sess.codemap(),
+                                                             &sess.parse_sess.span_diagnostic,
+                                                             &krate,
+                                                             &attributes,
+                                                             sess.opts.unstable_features);
+            *sess.features.borrow_mut() = features;
+        });
     });
 
     // JBC: make CFG processing part of expansion to avoid this problem:
 
     // strip again, in case expansion added anything with a #[cfg].
-    krate = time(time_passes, "configuration 2", || {
-        syntax::config::strip_unconfigured_items(sess.diagnostic(), krate, &mut feature_gated_cfgs)
-    });
+    krate = sess.abort_if_new_errors(|| {
+        let krate = time(time_passes, "configuration 2", || {
+            syntax::config::strip_unconfigured_items(sess.diagnostic(),
+                                                     krate,
+                                                     &mut feature_gated_cfgs)
+        });
 
-    time(time_passes, "gated configuration checking", || {
-        let features = sess.features.borrow();
-        feature_gated_cfgs.sort();
-        feature_gated_cfgs.dedup();
-        for cfg in &feature_gated_cfgs {
-            cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap());
-        }
+        time(time_passes, "gated configuration checking", || {
+            let features = sess.features.borrow();
+            feature_gated_cfgs.sort();
+            feature_gated_cfgs.dedup();
+            for cfg in &feature_gated_cfgs {
+                cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap());
+            }
+        });
+
+        krate
     });
 
     krate = time(time_passes, "maybe building test harness", || {
@@ -639,13 +649,14 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // later, to make sure we've got everything (e.g. configuration
     // can insert new attributes via `cfg_attr`)
     time(time_passes, "complete gated feature checking 2", || {
-        let features = syntax::feature_gate::check_crate(sess.codemap(),
-                                                         &sess.parse_sess.span_diagnostic,
-                                                         &krate,
-                                                         &attributes,
-                                                         sess.opts.unstable_features);
-        *sess.features.borrow_mut() = features;
-        sess.abort_if_errors();
+        sess.abort_if_new_errors(|| {
+            let features = syntax::feature_gate::check_crate(sess.codemap(),
+                                                             &sess.parse_sess.span_diagnostic,
+                                                             &krate,
+                                                             &attributes,
+                                                             sess.opts.unstable_features);
+            *sess.features.borrow_mut() = features;
+        });
     });
 
     time(time_passes,
@@ -711,9 +722,11 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
          "external crate/lib resolution",
          || LocalCrateReader::new(sess, cstore, &hir_map).read_crates(krate));
 
-    let lang_items = time(time_passes,
-                          "language item collection",
-                          || middle::lang_items::collect_language_items(&sess, &hir_map));
+    let lang_items = time(time_passes, "language item collection", || {
+        sess.abort_if_new_errors(|| {
+            middle::lang_items::collect_language_items(&sess, &hir_map)
+        })
+    });
 
     let resolve::CrateMap {
         def_map,
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index 9122148a8cc..9c75007a8db 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -258,15 +258,14 @@ impl<'a> CrateReader<'a> {
                             metadata: &MetadataBlob) {
         let crate_rustc_version = decoder::crate_rustc_version(metadata.as_slice());
         if crate_rustc_version != Some(rustc_version()) {
-            span_err!(self.sess, span, E0514,
-                      "the crate `{}` has been compiled with {}, which is \
-                       incompatible with this version of rustc",
-                      name,
-                      crate_rustc_version
-                          .as_ref().map(|s|&**s)
-                          .unwrap_or("an old version of rustc")
+            span_fatal!(self.sess, span, E0514,
+                        "the crate `{}` has been compiled with {}, which is \
+                         incompatible with this version of rustc",
+                        name,
+                        crate_rustc_version
+                            .as_ref().map(|s|&**s)
+                            .unwrap_or("an old version of rustc")
             );
-            self.sess.abort_if_errors();
         }
     }
 
@@ -511,7 +510,6 @@ impl<'a> CrateReader<'a> {
                     }
                 };
                 let span = mk_sp(lo, p.last_span.hi);
-                p.abort_if_errors();
 
                 // Mark the attrs as used
                 for attr in &attrs {
@@ -554,8 +552,7 @@ impl<'a> CrateReader<'a> {
                                   name,
                                   config::host_triple(),
                                   self.sess.opts.target_triple);
-            span_err!(self.sess, span, E0456, "{}", &message[..]);
-            self.sess.abort_if_errors();
+            span_fatal!(self.sess, span, E0456, "{}", &message[..]);
         }
 
         let registrar =
diff --git a/src/librustc_passes/const_fn.rs b/src/librustc_passes/const_fn.rs
index cda5267f727..f422a47572b 100644
--- a/src/librustc_passes/const_fn.rs
+++ b/src/librustc_passes/const_fn.rs
@@ -18,8 +18,9 @@ use syntax::visit::{self, Visitor, FnKind};
 use syntax::codemap::Span;
 
 pub fn check_crate(sess: &Session, krate: &ast::Crate) {
-    visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
-    sess.abort_if_errors();
+    sess.abort_if_new_errors(|| {
+        visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
+    });
 }
 
 struct CheckConstFn<'a> {
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 444c43163e3..c698f729553 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -3037,6 +3037,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                           check_ribs: bool,
                           record_used: bool)
                           -> Option<LocalDef> {
+        if identifier.name == special_idents::invalid.name {
+            return Some(LocalDef::from_def(Def::Err));
+        }
+
         // First, check to see whether the name is a primitive type.
         if namespace == TypeNS {
             if let Some(&prim_ty) = self.primitive_type_table
@@ -4019,10 +4023,8 @@ pub fn create_resolver<'a, 'tcx>(session: &'a Session,
     resolver.callback = callback;
 
     build_reduced_graph::build_reduced_graph(&mut resolver, krate);
-    session.abort_if_errors();
 
     resolve_imports::resolve_imports(&mut resolver);
-    session.abort_if_errors();
 
     resolver
 }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index f49b25df66e..a8697f45d91 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -121,7 +121,7 @@ use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::{self, Span, Spanned};
 use syntax::errors::DiagnosticBuilder;
-use syntax::parse::token::{self, InternedString};
+use syntax::parse::token::{self, InternedString, special_idents};
 use syntax::ptr::P;
 use syntax::util::lev_distance::find_best_match_for_name;
 
@@ -2839,8 +2839,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                 method_ty
             }
             Err(error) => {
-                method::report_error(fcx, method_name.span, expr_t,
-                                     method_name.node, Some(rcvr), error);
+                if method_name.node != special_idents::invalid.name {
+                    method::report_error(fcx, method_name.span, expr_t,
+                                         method_name.node, Some(rcvr), error);
+                }
                 fcx.write_error(expr.id);
                 fcx.tcx().types.err
             }
@@ -2938,6 +2940,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             None => {}
         }
 
+        if field.node == special_idents::invalid.name {
+            fcx.write_error(expr.id);
+            return;
+        }
+
         if method::exists(fcx, field.span, field.node, expr_t, expr.id) {
             fcx.type_error_struct(field.span,
                                   |actual| {
@@ -3788,8 +3795,9 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
                 Some((Some(ty), slice::ref_slice(item_segment), def))
             }
             Err(error) => {
-                method::report_error(fcx, span, ty,
-                                     item_name, None, error);
+                if item_name != special_idents::invalid.name {
+                    method::report_error(fcx, span, ty, item_name, None, error);
+                }
                 fcx.write_error(node_id);
                 None
             }
@@ -4221,7 +4229,9 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
             }
             // Check for unrepresentable discriminant values
             match hint {
-                attr::ReprAny | attr::ReprExtern => (),
+                attr::ReprAny | attr::ReprExtern => {
+                    disr_vals.push(current_disr_val);
+                }
                 attr::ReprInt(sp, ity) => {
                     if !disr_in_range(ccx, ity, current_disr_val) {
                         let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0082,
@@ -4231,14 +4241,9 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
                         err.emit();
                     }
                 }
-                attr::ReprSimd => {
-                    ccx.tcx.sess.bug("range_to_inttype: found ReprSimd on an enum");
-                }
-                attr::ReprPacked => {
-                    ccx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
-                }
+                // Error reported elsewhere.
+                attr::ReprSimd | attr::ReprPacked => {}
             }
-            disr_vals.push(current_disr_val);
         }
     }
 
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
index 6983c74696a..a7a4ddc3b2a 100644
--- a/src/libsyntax/errors/mod.rs
+++ b/src/libsyntax/errors/mod.rs
@@ -555,6 +555,9 @@ impl Handler {
 pub enum Level {
     Bug,
     Fatal,
+    // An error which while not immediately fatal, should stop the compiler
+    // progressing beyond the current phase.
+    PhaseFatal,
     Error,
     Warning,
     Note,
@@ -573,7 +576,7 @@ impl fmt::Display for Level {
 impl Level {
     fn color(self) -> term::color::Color {
         match self {
-            Bug | Fatal | Error => term::color::BRIGHT_RED,
+            Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
             Warning => term::color::BRIGHT_YELLOW,
             Note => term::color::BRIGHT_GREEN,
             Help => term::color::BRIGHT_CYAN,
@@ -584,7 +587,7 @@ impl Level {
     fn to_str(self) -> &'static str {
         match self {
             Bug => "error: internal compiler error",
-            Fatal | Error => "error",
+            Fatal | PhaseFatal | Error => "error",
             Warning => "warning",
             Note => "note",
             Help => "help",
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 5f27bdfc98a..72537f6c7b2 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1304,9 +1304,14 @@ pub fn expand_crate(mut cx: ExtCtxt,
             expander.cx.syntax_env.insert(name, extension);
         }
 
+        let err_count = cx.parse_sess.span_diagnostic.err_count();
         let mut ret = expander.fold_crate(c);
         ret.exported_macros = expander.cx.exported_macros.clone();
-        cx.parse_sess.span_diagnostic.abort_if_errors();
+
+        if cx.parse_sess.span_diagnostic.err_count() > err_count {
+            cx.parse_sess.span_diagnostic.abort_if_errors();
+        }
+
         ret
     };
     return (ret, cx.syntax_env.names);
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 090b070433f..32372ccc13b 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -98,7 +98,7 @@ pub fn parse_crate_from_source_str(name: String,
                                            cfg,
                                            name,
                                            source);
-    maybe_aborted(panictry!(p.parse_crate_mod()),p)
+    panictry!(p.parse_crate_mod())
 }
 
 pub fn parse_crate_attrs_from_source_str(name: String,
@@ -110,7 +110,7 @@ pub fn parse_crate_attrs_from_source_str(name: String,
                                            cfg,
                                            name,
                                            source);
-    maybe_aborted(panictry!(p.parse_inner_attributes()), p)
+    panictry!(p.parse_inner_attributes())
 }
 
 pub fn parse_expr_from_source_str(name: String,
@@ -119,7 +119,7 @@ pub fn parse_expr_from_source_str(name: String,
                                   sess: &ParseSess)
                                   -> P<ast::Expr> {
     let mut p = new_parser_from_source_str(sess, cfg, name, source);
-    maybe_aborted(panictry!(p.parse_expr()), p)
+    panictry!(p.parse_expr())
 }
 
 pub fn parse_item_from_source_str(name: String,
@@ -128,7 +128,7 @@ pub fn parse_item_from_source_str(name: String,
                                   sess: &ParseSess)
                                   -> Option<P<ast::Item>> {
     let mut p = new_parser_from_source_str(sess, cfg, name, source);
-    maybe_aborted(panictry!(p.parse_item()), p)
+    panictry!(p.parse_item())
 }
 
 pub fn parse_meta_from_source_str(name: String,
@@ -137,7 +137,7 @@ pub fn parse_meta_from_source_str(name: String,
                                   sess: &ParseSess)
                                   -> P<ast::MetaItem> {
     let mut p = new_parser_from_source_str(sess, cfg, name, source);
-    maybe_aborted(panictry!(p.parse_meta_item()), p)
+    panictry!(p.parse_meta_item())
 }
 
 pub fn parse_stmt_from_source_str(name: String,
@@ -151,7 +151,7 @@ pub fn parse_stmt_from_source_str(name: String,
         name,
         source
     );
-    maybe_aborted(panictry!(p.parse_stmt()), p)
+    panictry!(p.parse_stmt())
 }
 
 // Warning: This parses with quote_depth > 0, which is not the default.
@@ -168,7 +168,7 @@ pub fn parse_tts_from_source_str(name: String,
     );
     p.quote_depth += 1;
     // right now this is re-creating the token trees from ... token trees.
-    maybe_aborted(panictry!(p.parse_all_token_trees()),p)
+    panictry!(p.parse_all_token_trees())
 }
 
 // Create a new parser from a source string
@@ -265,16 +265,10 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess,
     p
 }
 
-/// Abort if necessary
-pub fn maybe_aborted<T>(result: T, p: Parser) -> T {
-    p.abort_if_errors();
-    result
-}
 
 fn abort_if_errors<'a, T>(result: PResult<'a, T>, p: &Parser) -> T {
     match result {
         Ok(c) => {
-            p.abort_if_errors();
             c
         }
         Err(mut e) => {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index bfa42e76129..acce6ed87d0 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2355,6 +2355,59 @@ impl<'a> Parser<'a> {
         )
     }
 
+    // Assuming we have just parsed `.foo` (i.e., a dot and an ident), continue
+    // parsing into an expression.
+    fn parse_dot_suffix(&mut self,
+                        ident: Ident,
+                        ident_span: Span,
+                        self_value: P<Expr>)
+                        -> PResult<'a, P<Expr>> {
+        let (_, tys, bindings) = if self.eat(&token::ModSep) {
+            try!(self.expect_lt());
+            try!(self.parse_generic_values_after_lt())
+        } else {
+            (Vec::new(), Vec::new(), Vec::new())
+        };
+
+        if !bindings.is_empty() {
+            let last_span = self.last_span;
+            self.span_err(last_span, "type bindings are only permitted on trait paths");
+        }
+
+        let lo = self_value.span.lo;
+
+        Ok(match self.token {
+            // expr.f() method call.
+            token::OpenDelim(token::Paren) => {
+                let mut es = try!(self.parse_unspanned_seq(
+                    &token::OpenDelim(token::Paren),
+                    &token::CloseDelim(token::Paren),
+                    seq_sep_trailing_allowed(token::Comma),
+                    |p| Ok(try!(p.parse_expr()))
+                ));
+                let hi = self.last_span.hi;
+
+                es.insert(0, self_value);
+                let id = spanned(ident_span.lo, ident_span.hi, ident);
+                let nd = self.mk_method_call(id, tys, es);
+                self.mk_expr(lo, hi, nd, None)
+            }
+            // Field access.
+            _ => {
+                if !tys.is_empty() {
+                    let last_span = self.last_span;
+                    self.span_err(last_span,
+                                  "field expressions may not \
+                                   have type parameters");
+                }
+
+                let id = spanned(ident_span.lo, ident_span.hi, ident);
+                let field = self.mk_field(self_value, id);
+                self.mk_expr(lo, ident_span.hi, field, None)
+            }
+        })
+    }
+
     fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>> {
         let mut e = e0;
         let lo = e.span.lo;
@@ -2364,50 +2417,11 @@ impl<'a> Parser<'a> {
             if self.eat(&token::Dot) {
                 match self.token {
                   token::Ident(i, _) => {
-                    let dot = self.last_span.hi;
+                    let dot_pos = self.last_span.hi;
                     hi = self.span.hi;
                     self.bump();
-                    let (_, tys, bindings) = if self.eat(&token::ModSep) {
-                        try!(self.expect_lt());
-                        try!(self.parse_generic_values_after_lt())
-                    } else {
-                        (Vec::new(), Vec::new(), Vec::new())
-                    };
-
-                    if !bindings.is_empty() {
-                        let last_span = self.last_span;
-                        self.span_err(last_span, "type bindings are only permitted on trait paths");
-                    }
 
-                    // expr.f() method call
-                    match self.token {
-                        token::OpenDelim(token::Paren) => {
-                            let mut es = try!(self.parse_unspanned_seq(
-                                &token::OpenDelim(token::Paren),
-                                &token::CloseDelim(token::Paren),
-                                seq_sep_trailing_allowed(token::Comma),
-                                |p| Ok(try!(p.parse_expr()))
-                            ));
-                            hi = self.last_span.hi;
-
-                            es.insert(0, e);
-                            let id = spanned(dot, hi, i);
-                            let nd = self.mk_method_call(id, tys, es);
-                            e = self.mk_expr(lo, hi, nd, None);
-                        }
-                        _ => {
-                            if !tys.is_empty() {
-                                let last_span = self.last_span;
-                                self.span_err(last_span,
-                                              "field expressions may not \
-                                               have type parameters");
-                            }
-
-                            let id = spanned(dot, hi, i);
-                            let field = self.mk_field(e, id);
-                            e = self.mk_expr(lo, hi, field, None);
-                        }
-                    }
+                    e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e));
                   }
                   token::Literal(token::Integer(n), suf) => {
                     let sp = self.span;
@@ -2452,7 +2466,16 @@ impl<'a> Parser<'a> {
                     self.abort_if_errors();
 
                   }
-                  _ => return self.unexpected()
+                  _ => {
+                    // FIXME Could factor this out into non_fatal_unexpected or something.
+                    let actual = self.this_token_to_string();
+                    self.span_err(self.span, &format!("unexpected token: `{}`", actual));
+
+                    let dot_pos = self.last_span.hi;
+                    e = try!(self.parse_dot_suffix(special_idents::invalid,
+                                                   mk_sp(dot_pos, dot_pos),
+                                                   e));
+                  }
                 }
                 continue;
             }
diff --git a/src/test/compile-fail/cfg-non-opt-expr.rs b/src/test/compile-fail/cfg-non-opt-expr.rs
index d9d379ddc7d..b3ef3d72ca3 100644
--- a/src/test/compile-fail/cfg-non-opt-expr.rs
+++ b/src/test/compile-fail/cfg-non-opt-expr.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(stmt_expr_attributes)]
+
 fn main() {
     let _ = #[cfg(unset)] ();
     //~^ ERROR removing an expression is not supported in this position
diff --git a/src/test/compile-fail/double-type-import.rs b/src/test/compile-fail/double-type-import.rs
index 923f95e69d1..d6d7dbb4aec 100644
--- a/src/test/compile-fail/double-type-import.rs
+++ b/src/test/compile-fail/double-type-import.rs
@@ -20,5 +20,5 @@ mod foo {
 }
 
 fn main() {
-    let _ = foo::X;
+    let _ = foo::X; //~ ERROR unresolved name `foo::X`
 }
diff --git a/src/test/compile-fail/import-from-missing.rs b/src/test/compile-fail/import-from-missing.rs
index f393442de10..489bcfbdefd 100644
--- a/src/test/compile-fail/import-from-missing.rs
+++ b/src/test/compile-fail/import-from-missing.rs
@@ -16,3 +16,4 @@ mod spam {
 }
 
 fn main() { ham(); eggs(); }
+//~^ ERROR unresolved name `eggs`
diff --git a/src/test/compile-fail/import.rs b/src/test/compile-fail/import.rs
index 844d527a546..86c4ce8b038 100644
--- a/src/test/compile-fail/import.rs
+++ b/src/test/compile-fail/import.rs
@@ -16,4 +16,4 @@ use zed::baz;
 mod zed {
     pub fn bar() { println!("bar"); }
 }
-fn main(args: Vec<String>) { bar(); }
+fn main() { bar(); }
diff --git a/src/test/compile-fail/import2.rs b/src/test/compile-fail/import2.rs
index 6533bd5ddc6..1d2aecd4e3b 100644
--- a/src/test/compile-fail/import2.rs
+++ b/src/test/compile-fail/import2.rs
@@ -16,4 +16,5 @@ mod baz {}
 mod zed {
     pub fn bar() { println!("bar3"); }
 }
-fn main(args: Vec<String>) { bar(); }
+fn main() { bar(); }
+//~^ ERROR unresolved name `bar`
diff --git a/src/test/compile-fail/macro-reexport-malformed-1.rs b/src/test/compile-fail/macro-reexport-malformed-1.rs
index 6c85cf5c7f5..ea2dfca0714 100644
--- a/src/test/compile-fail/macro-reexport-malformed-1.rs
+++ b/src/test/compile-fail/macro-reexport-malformed-1.rs
@@ -8,9 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![no_std]
 #![feature(macro_reexport)]
 
 #[macro_reexport]  //~ ERROR bad macro reexport
 extern crate std;
-
-fn main() { }
diff --git a/src/test/compile-fail/macro-reexport-malformed-2.rs b/src/test/compile-fail/macro-reexport-malformed-2.rs
index 1dd0168181f..844955fb7e6 100644
--- a/src/test/compile-fail/macro-reexport-malformed-2.rs
+++ b/src/test/compile-fail/macro-reexport-malformed-2.rs
@@ -8,9 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![no_std]
 #![feature(macro_reexport)]
 
 #[macro_reexport="foo"]  //~ ERROR bad macro reexport
 extern crate std;
-
-fn main() { }
diff --git a/src/test/compile-fail/macro-reexport-malformed-3.rs b/src/test/compile-fail/macro-reexport-malformed-3.rs
index 7ae045f6e4f..381c22854e6 100644
--- a/src/test/compile-fail/macro-reexport-malformed-3.rs
+++ b/src/test/compile-fail/macro-reexport-malformed-3.rs
@@ -8,9 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![no_std]
 #![feature(macro_reexport)]
 
 #[macro_reexport(foo="bar")]  //~ ERROR bad macro reexport
 extern crate std;
-
-fn main() { }
diff --git a/src/test/compile-fail/macro-reexport-undef.rs b/src/test/compile-fail/macro-reexport-undef.rs
index 8fa6b32905c..5bb0b8759f4 100644
--- a/src/test/compile-fail/macro-reexport-undef.rs
+++ b/src/test/compile-fail/macro-reexport-undef.rs
@@ -10,6 +10,8 @@
 
 // aux-build:two_macros.rs
 
+#![feature(macro_reexport)]
+
 #[macro_use(macro_two)]
 #[macro_reexport(no_way)] //~ ERROR reexported macro not found
 extern crate two_macros;
diff --git a/src/test/compile-fail/macro-use-bad-args-1.rs b/src/test/compile-fail/macro-use-bad-args-1.rs
index a73c4adb71f..39c09c69779 100644
--- a/src/test/compile-fail/macro-use-bad-args-1.rs
+++ b/src/test/compile-fail/macro-use-bad-args-1.rs
@@ -8,8 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![no_std]
+
 #[macro_use(foo(bar))]  //~ ERROR bad macro import
 extern crate std;
-
-fn main() {
-}
diff --git a/src/test/compile-fail/macro-use-bad-args-2.rs b/src/test/compile-fail/macro-use-bad-args-2.rs
index 31efe857605..11a0108b99b 100644
--- a/src/test/compile-fail/macro-use-bad-args-2.rs
+++ b/src/test/compile-fail/macro-use-bad-args-2.rs
@@ -8,8 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![no_std]
+
 #[macro_use(foo="bar")]  //~ ERROR bad macro import
 extern crate std;
-
-fn main() {
-}
diff --git a/src/test/compile-fail/parse-error-correct.rs b/src/test/compile-fail/parse-error-correct.rs
new file mode 100644
index 00000000000..7715ed41841
--- /dev/null
+++ b/src/test/compile-fail/parse-error-correct.rs
@@ -0,0 +1,19 @@
+// 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.
+
+// Test that the parser is error correcting missing idents. Despite a parsing
+// error (or two), we still run type checking (and don't get extra errors there).
+
+fn main() {
+    let y = 42;
+    let x = y.;  //~ ERROR unexpected token
+    let x = y.();  //~ ERROR unexpected token
+    let x = y.foo; //~ ERROR no field
+}
diff --git a/src/test/compile-fail/privacy3.rs b/src/test/compile-fail/privacy3.rs
index da6266bc7ee..6a203993ccf 100644
--- a/src/test/compile-fail/privacy3.rs
+++ b/src/test/compile-fail/privacy3.rs
@@ -28,6 +28,7 @@ fn test1() {
     use bar::gpriv;
     //~^ ERROR unresolved import `bar::gpriv`. There is no `gpriv` in `bar`
     gpriv();
+    //~^ ERROR unresolved name `gpriv`
 }
 
 #[start] fn main(_: isize, _: *const *const u8) -> isize { 3 }
diff --git a/src/test/compile-fail/self_type_keyword.rs b/src/test/compile-fail/self_type_keyword.rs
index 6f5aeead57e..e28197e81fa 100644
--- a/src/test/compile-fail/self_type_keyword.rs
+++ b/src/test/compile-fail/self_type_keyword.rs
@@ -29,6 +29,7 @@ pub fn main() {
         //~^ ERROR expected identifier, found keyword `Self`
         Self!() => (),
         //~^ ERROR expected identifier, found keyword `Self`
+        //~^^ ERROR macro undefined: 'Self!'
         Foo { x: Self } => (),
         //~^ ERROR expected identifier, found keyword `Self`
         Foo { Self } => (),
diff --git a/src/test/compile-fail/use-mod.rs b/src/test/compile-fail/use-mod.rs
index 15640e386df..9cc3c92e2e3 100644
--- a/src/test/compile-fail/use-mod.rs
+++ b/src/test/compile-fail/use-mod.rs
@@ -14,6 +14,7 @@ use foo::bar::{
     Bar,
     self
 //~^ NOTE another `self` import appears here
+//~^^ ERROR a module named `bar` has already been imported in this module
 };
 
 use {self};