about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-09 01:36:58 +0000
committerbors <bors@rust-lang.org>2018-09-09 01:36:58 +0000
commit004bc5a33cd44cd0896cae917a75488579126da5 (patch)
treed0da7000dd54a3a1bc3e9d24e902fbb99912d38f /src/libsyntax
parent0198a1ea45e29af00d92423aa6d2ac876410c3f9 (diff)
parent3192d3dc0c4417a6e360018b341c07d32f3f3d7f (diff)
downloadrust-004bc5a33cd44cd0896cae917a75488579126da5.tar.gz
rust-004bc5a33cd44cd0896cae917a75488579126da5.zip
Auto merge of #53949 - estebank:unclosed-delim, r=nikomatsakis
Improve messages for un-closed delimiter errors
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/parse/lexer/mod.rs5
-rw-r--r--src/libsyntax/parse/lexer/tokentrees.rs47
-rw-r--r--src/libsyntax/source_map.rs32
3 files changed, 69 insertions, 15 deletions
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index 96584a580f1..b7e8a880e7e 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -66,6 +66,10 @@ pub struct StringReader<'a> {
     /// The raw source span which *does not* take `override_span` into account
     span_src_raw: Span,
     open_braces: Vec<(token::DelimToken, Span)>,
+    /// The type and spans for all braces that have different indentation.
+    ///
+    /// Used only for error recovery when arriving to EOF with mismatched braces.
+    suspicious_open_spans: Vec<(token::DelimToken, Span, Span)>,
     crate override_span: Option<Span>,
     last_unclosed_found_span: Option<Span>,
 }
@@ -216,6 +220,7 @@ impl<'a> StringReader<'a> {
             span: syntax_pos::DUMMY_SP,
             span_src_raw: syntax_pos::DUMMY_SP,
             open_braces: Vec::new(),
+            suspicious_open_spans: Vec::new(),
             override_span,
             last_unclosed_found_span: None,
         }
diff --git a/src/libsyntax/parse/lexer/tokentrees.rs b/src/libsyntax/parse/lexer/tokentrees.rs
index e2fd7faf903..e6ad3b9203e 100644
--- a/src/libsyntax/parse/lexer/tokentrees.rs
+++ b/src/libsyntax/parse/lexer/tokentrees.rs
@@ -49,9 +49,26 @@ impl<'a> StringReader<'a> {
                 let msg = "this file contains an un-closed delimiter";
                 let mut err = self.sess.span_diagnostic.struct_span_err(self.span, msg);
                 for &(_, sp) in &self.open_braces {
-                    err.span_help(sp, "did you mean to close this delimiter?");
+                    err.span_label(sp, "un-closed delimiter");
                 }
 
+                if let Some((delim, _)) = self.open_braces.last() {
+                    if let Some((d, open_sp, close_sp)) = self.suspicious_open_spans.iter()
+                        .filter(|(d, _, _)| delim == d)
+                        .next()  // these are in reverse order as they get inserted on close, but
+                    {            // we want the last open/first close
+                        if d == delim {
+                            err.span_label(
+                                *open_sp,
+                                "this delimiter might not be properly closed...",
+                            );
+                            err.span_label(
+                                *close_sp,
+                                "...as it matches this but it has different indentation",
+                            );
+                        }
+                    }
+                }
                 Err(err)
             },
             token::OpenDelim(delim) => {
@@ -70,11 +87,20 @@ impl<'a> StringReader<'a> {
                 // Expand to cover the entire delimited token tree
                 let span = pre_span.with_hi(self.span.hi());
 
+                let sm = self.sess.source_map();
                 match self.token {
                     // Correct delimiter.
                     token::CloseDelim(d) if d == delim => {
-                        self.open_braces.pop().unwrap();
-
+                        let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
+                        if let Some(current_padding) = sm.span_to_margin(self.span) {
+                            if let Some(padding) = sm.span_to_margin(open_brace_span) {
+                                if current_padding != padding {
+                                    self.suspicious_open_spans.push(
+                                        (open_brace, open_brace_span, self.span),
+                                    );
+                                }
+                            }
+                        }
                         // Parse the close delimiter.
                         self.real_token();
                     }
@@ -94,8 +120,21 @@ impl<'a> StringReader<'a> {
                             // delimiter. The previous unclosed delimiters could actually be
                             // closed! The parser just hasn't gotten to them yet.
                             if let Some(&(_, sp)) = self.open_braces.last() {
-                                err.span_label(sp, "unclosed delimiter");
+                                err.span_label(sp, "un-closed delimiter");
                             };
+                            if let Some(current_padding) = sm.span_to_margin(self.span) {
+                                for (brace, brace_span) in &self.open_braces {
+                                    if let Some(padding) = sm.span_to_margin(*brace_span) {
+                                        // high likelihood of these two corresponding
+                                        if current_padding == padding && brace == &other {
+                                            err.span_label(
+                                                *brace_span,
+                                                "close delimiter possibly meant for this",
+                                            );
+                                        }
+                                    }
+                                }
+                            }
                             err.emit();
                         }
                         self.open_braces.pop().unwrap();
diff --git a/src/libsyntax/source_map.rs b/src/libsyntax/source_map.rs
index 95839f94b9e..7ee58350621 100644
--- a/src/libsyntax/source_map.rs
+++ b/src/libsyntax/source_map.rs
@@ -251,17 +251,18 @@ impl SourceMap {
     /// crate. The source code of such an "imported source_file" is not available,
     /// but we still know enough to generate accurate debuginfo location
     /// information for things inlined from other crates.
-    pub fn new_imported_source_file(&self,
-                                filename: FileName,
-                                name_was_remapped: bool,
-                                crate_of_origin: u32,
-                                src_hash: u128,
-                                name_hash: u128,
-                                source_len: usize,
-                                mut file_local_lines: Vec<BytePos>,
-                                mut file_local_multibyte_chars: Vec<MultiByteChar>,
-                                mut file_local_non_narrow_chars: Vec<NonNarrowChar>)
-                                -> Lrc<SourceFile> {
+    pub fn new_imported_source_file(
+        &self,
+        filename: FileName,
+        name_was_remapped: bool,
+        crate_of_origin: u32,
+        src_hash: u128,
+        name_hash: u128,
+        source_len: usize,
+        mut file_local_lines: Vec<BytePos>,
+        mut file_local_multibyte_chars: Vec<MultiByteChar>,
+        mut file_local_non_narrow_chars: Vec<NonNarrowChar>,
+    ) -> Lrc<SourceFile> {
         let start_pos = self.next_start_pos();
 
         let end_pos = Pos::from_usize(start_pos + source_len);
@@ -578,6 +579,15 @@ impl SourceMap {
                                                                 .to_string())
     }
 
+    pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
+        match self.span_to_prev_source(sp) {
+            Err(_) => None,
+            Ok(source) => source.split('\n').last().map(|last_line| {
+                last_line.len() - last_line.trim_left().len()
+            })
+        }
+    }
+
     /// Return the source snippet as `String` before the given `Span`
     pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
         self.span_to_source(sp, |src, start_index, _| src[..start_index].to_string())