Repository: rust-analyzer/rust-analyzer
Pull request #3218 (2020-02-18 13:41:00)

Cut some deps

matklad: bors r+
🤖

Patch:
diff --git a/.gitignore b/.gitignore
index 99e0c50039..dc5ceca7fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ crates/*/target
 *.log
 *.iml
 .vscode/settings.json
+cargo-timing*.html
diff --git a/Cargo.lock b/Cargo.lock
index c10d72aa4c..e7b8730769 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1043,7 +1043,6 @@ version = "0.1.0"
 dependencies = [
  "either",
  "format-buf",
- "fst",
  "indexmap",
  "insta",
  "itertools",
@@ -1060,11 +1059,8 @@ dependencies = [
  "ra_syntax",
  "ra_text_edit",
  "rand",
- "rayon",
  "rustc-hash",
- "superslice",
  "test_utils",
- "unicase",
 ]
 
 [[package]]
@@ -1091,7 +1087,6 @@ dependencies = [
  "rustc-hash",
  "superslice",
  "test_utils",
- "unicase",
 ]
 
 [[package]]
@@ -1602,15 +1597,6 @@ dependencies = [
  "num_cpus",
 ]
 
-[[package]]
-name = "unicase"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
-dependencies = [
- "version_check",
-]
-
 [[package]]
 name = "unicode-bidi"
 version = "0.3.4"
@@ -1653,12 +1639,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "version_check"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
-
 [[package]]
 name = "walkdir"
 version = "2.3.1"
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index d50cf1d20a..97dea5ffde 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -17,11 +17,7 @@ indexmap = "1.3.0"
 itertools = "0.8.0"
 join_to_string = "0.1.3"
 log = "0.4.5"
-rayon = "1.0.2"
-fst = { version = "0.3.1", default-features = false }
 rustc-hash = "1.0"
-unicase = "2.2.0"
-superslice = "1.0.0"
 rand = { version = "0.7.0", features = ["small_rng"] }
 once_cell = "1.2.0"
 
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 82596c665c..22bd497231 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -10,7 +10,7 @@ use ra_prof::profile;
 use ra_syntax::{
     algo,
     ast::{self, make, AstNode},
-    SyntaxNode, TextRange, T,
+    Location, SyntaxNode, TextRange, T,
 };
 use ra_text_edit::{TextEdit, TextEditBuilder};
 
@@ -29,7 +29,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
     let mut res = Vec::new();
 
     res.extend(parse.errors().iter().map(|err| Diagnostic {
-        range: err.range(),
+        range: location_to_range(err.location()),
         message: format!("Syntax Error: {}", err),
         severity: Severity::Error,
         fix: None,
@@ -116,6 +116,12 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
     drop(sink);
     res.into_inner()
 }
+fn location_to_range(location: Location) -> TextRange {
+    match location {
+        Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
+        Location::Range(range) => range,
+    }
+}
 
 fn check_unnecessary_braces_in_use_statement(
     acc: &mut Vec<Diagnostic>,
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index ad3acce595..dbe98f3a0d 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -20,7 +20,6 @@ log = "0.4.5"
 rayon = "1.0.2"
 fst = { version = "0.3.1", default-features = false }
 rustc-hash = "1.0"
-unicase = "2.2.0"
 superslice = "1.0.0"
 once_cell = "1.2.0"
 
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index 64ddf2f95e..e6b3126b64 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -21,6 +21,7 @@
 //! those FSTs.
 
 use std::{
+    cmp::Ordering,
     fmt,
     hash::{Hash, Hasher},
     mem,
@@ -187,29 +188,34 @@ impl Hash for SymbolIndex {
 
 impl SymbolIndex {
     fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
-        fn cmp_key<'a>(s1: &'a FileSymbol) -> impl Ord + 'a {
-            unicase::Ascii::new(s1.name.as_str())
+        fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
+            let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase());
+            let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase());
+            lhs_chars.cmp(rhs_chars)
         }
+
         #[cfg(not(feature = "wasm"))]
-        symbols.par_sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2)));
+        symbols.par_sort_by(cmp);
 
         #[cfg(feature = "wasm")]
-        symbols.sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2)));
+        symbols.sort_by(cmp);
 
         let mut builder = fst::MapBuilder::memory();
 
         let mut last_batch_start = 0;
 
         for idx in 0..symbols.len() {
-            if symbols.get(last_batch_start).map(cmp_key) == symbols.get(idx + 1).map(cmp_key) {
-                continue;
+            if let Some(next_symbol) = symbols.get(idx + 1) {
+                if cmp(&symbols[last_batch_start], next_symbol) == Ordering::Equal {
+                    continue;
+                }
             }
 
             let start = last_batch_start;
             let end = idx + 1;
             last_batch_start = end;
 
-            let key = symbols[start].name.as_str().to_lowercase();
+            let key = symbols[start].name.as_str().to_ascii_lowercase();
             let value = SymbolIndex::range_to_map_value(start, end);
 
             builder.insert(key, value).unwrap();
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index e3f74da6de..f8f4b64c1e 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -41,9 +41,11 @@ use crate::syntax_node::GreenNode;
 pub use crate::{
     algo::InsertPosition,
     ast::{AstNode, AstToken},
-    parsing::{lex_single_syntax_kind, lex_single_valid_syntax_kind, tokenize, Token},
+    parsing::{
+        lex_single_syntax_kind, lex_single_valid_syntax_kind, tokenize, Token, TokenizeError,
+    },
     ptr::{AstPtr, SyntaxNodePtr},
-    syntax_error::SyntaxError,
+    syntax_error::{Location, SyntaxError, SyntaxErrorKind},
     syntax_node::{
         Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder,
     },
@@ -115,7 +117,7 @@ impl Parse<SourceFile> {
     pub fn debug_dump(&self) -> String {
         let mut buf = format!("{:#?}", self.tree().syntax());
         for err in self.errors.iter() {
-            writeln!(buf, "error {:?}: {}", err.range(), err).unwrap();
+            writeln!(buf, "error {:?}: {}", err.location(), err.kind()).unwrap();
         }
         buf
     }
diff --git a/crates/ra_syntax/src/parsing/lexer.rs b/crates/ra_syntax/src/parsing/lexer.rs
index f2684c852a..f889e6a1d6 100644
--- a/crates/ra_syntax/src/parsing/lexer.rs
+++ b/crates/ra_syntax/src/parsing/lexer.rs
@@ -2,7 +2,7 @@
 //! It is just a bridge to `rustc_lexer`.
 
 use crate::{
-    SyntaxError,
+    SyntaxError, SyntaxErrorKind,
     SyntaxKind::{self, *},
     TextRange, TextUnit,
 };
@@ -41,13 +41,13 @@ pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) {
         let token_len = TextUnit::from_usize(rustc_token.len);
         let token_range = TextRange::offset_len(TextUnit::from_usize(offset), token_len);
 
-        let (syntax_kind, err_message) =
+        let (syntax_kind, error) =
             rustc_token_kind_to_syntax_kind(&rustc_token.kind, &text[token_range]);
 
         tokens.push(Token { kind: syntax_kind, len: token_len });
 
-        if let Some(err_message) = err_message {
-            errors.push(SyntaxError::new(err_message, token_range));
+        if let Some(error) = error {
+            errors.push(SyntaxError::new(SyntaxErrorKind::TokenizeError(error), token_range));
         }
 
         offset += rustc_token.len;
@@ -94,21 +94,61 @@ fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> {
     }
 
     let rustc_token = rustc_lexer::first_token(text);
-    let (syntax_kind, err_message) = rustc_token_kind_to_syntax_kind(&rustc_token.kind, text);
+    let (syntax_kind, error) = rustc_token_kind_to_syntax_kind(&rustc_token.kind, text);
 
     let token = Token { kind: syntax_kind, len: TextUnit::from_usize(rustc_token.len) };
-    let optional_error = err_message.map(|err_message| {
-        SyntaxError::new(err_message, TextRange::from_to(0.into(), TextUnit::of_str(text)))
+    let error = error.map(|error| {
+        SyntaxError::new(
+            SyntaxErrorKind::TokenizeError(error),
+            TextRange::from_to(TextUnit::from(0), TextUnit::of_str(text)),
+        )
     });
 
-    Some((token, optional_error))
+    Some((token, error))
+}
+
+// FIXME: simplify TokenizeError to `SyntaxError(String, TextRange)` as per @matklad advice:
+// https://github.com/rust-analyzer/rust-analyzer/pull/2911/files#r371175067
+
+/// Describes the values of `SyntaxErrorKind::TokenizeError` enum variant.
+/// It describes all the types of errors that may happen during the tokenization
+/// of Rust source.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum TokenizeError {
+    /// Base prefix was provided, but there were no digits
+    /// after it, e.g. `0x`, `0b`.
+    EmptyInt,
+    /// Float exponent lacks digits e.g. `12.34e+`, `12.3E+`, `12e-`, `1_E-`,
+    EmptyExponent,
+
+    /// Block comment lacks trailing delimiter `*/`
+    UnterminatedBlockComment,
+    /// Character literal lacks trailing delimiter `'`
+    UnterminatedChar,
+    /// Characterish byte literal lacks trailing delimiter `'`
+    UnterminatedByte,
+    /// String literal lacks trailing delimiter `"`
+    UnterminatedString,
+    /// Byte string literal lacks trailing delimiter `"`
+    UnterminatedByteString,
+    /// Raw literal lacks trailing delimiter e.g. `"##`
+    UnterminatedRawString,
+    /// Raw byte string literal lacks trailing delimiter e.g. `"##`
+    UnterminatedRawByteString,
+
+    /// Raw string lacks a quote after the pound characters e.g. `r###`
+    UnstartedRawString,
+    /// Raw byte string lacks a quote after the pound characters e.g. `br###`
+    UnstartedRawByteString,
+
+    /// Lifetime starts with a number e.g. `'4ever`
+    LifetimeStartsWithNumber,
 }
 
-/// Returns `SyntaxKind` and an optional tokenize error message.
 fn rustc_token_kind_to_syntax_kind(
     rustc_token_kind: &rustc_lexer::TokenKind,
     token_text: &str,
-) -> (SyntaxKind, Option<&'static str>) {
+) -> (SyntaxKind, Option<TokenizeError>) {
     // A note on an intended tradeoff:
     // We drop some useful infromation here (see patterns with double dots `..`)
     // Storing that info in `SyntaxKind` is not possible due to its layout requirements of
@@ -116,15 +156,14 @@ fn rustc_token_kind_to_syntax_kind(
 
     let syntax_kind = {
         use rustc_lexer::TokenKind as TK;
+        use TokenizeError as TE;
+
         match rustc_token_kind {
             TK::LineComment => COMMENT,
 
             TK::BlockComment { terminated: true } => COMMENT,
             TK::BlockComment { terminated: false } => {
-                return (
-                    COMMENT,
-                    Some("Missing trailing `*/` symbols to terminate the block comment"),
-                );
+                return (COMMENT, Some(TE::UnterminatedBlockComment));
             }
 
             TK::Whitespace => WHITESPACE,
@@ -142,7 +181,7 @@ fn rustc_token_kind_to_syntax_kind(
 
             TK::Lifetime { starts_with_number: false } => LIFETIME,
             TK::Lifetime { starts_with_number: true } => {
-                return (LIFETIME, Some("Lifetime name cannot start with a number"))
+                return (LIFETIME, Some(TE::LifetimeStartsWithNumber))
             }
 
             TK::Semi => SEMI,
@@ -178,56 +217,57 @@ fn rustc_token_kind_to_syntax_kind(
 
     return (syntax_kind, None);
 
-    fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) {
+    fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<TokenizeError>) {
         use rustc_lexer::LiteralKind as LK;
+        use TokenizeError as TE;
 
         #[rustfmt::skip]
         let syntax_kind = match *kind {
             LK::Int { empty_int: false, .. } => INT_NUMBER,
             LK::Int { empty_int: true, .. } => {
-                return (INT_NUMBER, Some("Missing digits after the integer base prefix"))
+                return (INT_NUMBER, Some(TE::EmptyInt))
             }
 
             LK::Float { empty_exponent: false, .. } => FLOAT_NUMBER,
             LK::Float { empty_exponent: true, .. } => {
-                return (FLOAT_NUMBER, Some("Missing digits after the exponent symbol"))
+                return (FLOAT_NUMBER, Some(TE::EmptyExponent))
             }
 
             LK::Char { terminated: true } => CHAR,
             LK::Char { terminated: false } => {
-                return (CHAR, Some("Missing trailing `'` symbol to terminate the character literal"))
+                return (CHAR, Some(TE::UnterminatedChar))
             }
 
             LK::Byte { terminated: true } => BYTE,
             LK::Byte { terminated: false } => {
-                return (BYTE, Some("Missing trailing `'` symbol to terminate the byte literal"))
+                return (BYTE, Some(TE::UnterminatedByte))
             }
 
             LK::Str { terminated: true } => STRING,
             LK::Str { terminated: false } => {
-                return (STRING, Some("Missing trailing `\"` symbol to terminate the string literal"))
+                return (STRING, Some(TE::UnterminatedString))
             }
 
 
             LK::ByteStr { terminated: true } => BYTE_STRING,
             LK::ByteStr { terminated: false } => {
-                return (BYTE_STRING, Some("Missing trailing `\"` symbol to terminate the byte string literal"))
+                return (BYTE_STRING, Some(TE::UnterminatedByteString))
             }
 
             LK::RawStr { started: true, terminated: true, .. } => RAW_STRING,
             LK::RawStr { started: true, terminated: false, .. } => {
-                return (RAW_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw string literal"))
+                return (RAW_STRING, Some(TE::UnterminatedRawString))
             }
             LK::RawStr { started: false, .. } => {
-                return (RAW_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw string literal"))
+                return (RAW_STRING, Some(TE::UnstartedRawString))
             }
 
             LK::RawByteStr { started: true, terminated: true, .. } => RAW_BYTE_STRING,
             LK::RawByteStr { started: true, terminated: false, .. } => {
-                return (RAW_BYTE_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"))
+                return (RAW_BYTE_STRING, Some(TE::UnterminatedRawByteString))
             }
             LK::RawByteStr { started: false, .. } => {
-                return (RAW_BYTE_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw byte string literal"))
+                return (RAW_BYTE_STRING, Some(TE::UnstartedRawByteString))
             }
         };
 
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index 2d65b91f13..a86da06754 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -27,8 +27,8 @@ pub(crate) fn incremental_reparse(
     edit: &AtomTextEdit,
     errors: Vec<SyntaxError>,
 ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
-    if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) {
-        return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
+    if let Some((green, old_range)) = reparse_token(node, &edit) {
+        return Some((green, merge_errors(errors, Vec::new(), old_range, edit), old_range));
     }
 
     if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) {
@@ -40,7 +40,7 @@ pub(crate) fn incremental_reparse(
 fn reparse_token<'node>(
     root: &'node SyntaxNode,
     edit: &AtomTextEdit,
-) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
+) -> Option<(GreenNode, TextRange)> {
     let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone();
     let prev_token_kind = prev_token.kind();
     match prev_token_kind {
@@ -54,7 +54,7 @@ fn reparse_token<'node>(
             }
 
             let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit);
-            let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?;
+            let (new_token_kind, _error) = lex_single_syntax_kind(&new_text)?;
 
             if new_token_kind != prev_token_kind
                 || (new_token_kind == IDENT && is_contextual_kw(&new_text))
@@ -76,11 +76,7 @@ fn reparse_token<'node>(
 
             let new_token =
                 GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), new_text.into());
-            Some((
-                prev_token.replace_with(new_token),
-                new_err.into_iter().collect(),
-                prev_token.text_range(),
-            ))
+            Some((prev_token.replace_with(new_token), prev_token.text_range()))
         }
         _ => None,
     }
@@ -91,7 +87,7 @@ fn reparse_block<'node>(
     edit: &AtomTextEdit,
 ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
     let (node, reparser) = find_reparsable_node(root, edit.delete)?;
-    let text = get_text_after_edit(node.clone().into(), edit);
+    let text = get_text_after_edit(node.clone().into(), &edit);
 
     let (tokens, new_lexer_errors) = tokenize(&text);
     if !is_balanced(&tokens) {
@@ -166,26 +162,20 @@ fn is_balanced(tokens: &[Token]) -> bool {
 fn merge_errors(
     old_errors: Vec<SyntaxError>,
     new_errors: Vec<SyntaxError>,
-    range_before_reparse: TextRange,
+    old_range: TextRange,
     edit: &AtomTextEdit,
 ) -> Vec<SyntaxError> {
     let mut res = Vec::new();
-
-    for old_err in old_errors {
-        let old_err_range = old_err.range();
-        if old_err_range.end() <= range_before_reparse.start() {
-            res.push(old_err);
-        } else if old_err_range.start() >= range_before_reparse.end() {
-            let inserted_len = TextUnit::of_str(&edit.insert);
-            res.push(old_err.with_range((old_err_range + inserted_len) - edit.delete.len()));
-            // Note: extra parens are intentional to prevent uint underflow, HWAB (here was a bug)
+    for e in old_errors {
+        if e.offset() <= old_range.start() {
+            res.push(e)
+        } else if e.offset() >= old_range.end() {
+            res.push(e.add_offset(TextUnit::of_str(&edit.insert), edit.delete.len()));
         }
     }
-    res.extend(new_errors.into_iter().map(|new_err| {
-        // fighting borrow checker with a variable ;)
-        let offseted_range = new_err.range() + range_before_reparse.start();
-        new_err.with_range(offseted_range)
-    }));
+    for e in new_errors {
+        res.push(e.add_offset(old_range.start(), 0.into()));
+    }
     res
 }
 
@@ -203,9 +193,9 @@ mod tests {
 
         let fully_reparsed = SourceFile::parse(&after);
         let incrementally_reparsed: Parse<SourceFile> = {
-            let before = SourceFile::parse(&before);
+            let f = SourceFile::parse(&before);
             let (green, new_errors, range) =
-                incremental_reparse(before.tree().syntax(), &edit, before.errors.to_vec()).unwrap();
+                incremental_reparse(f.tree().syntax(), &edit, f.errors.to_vec()).unwrap();
             assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
             Parse::new(green, new_errors)
         };
@@ -214,7 +204,6 @@ mod tests {
             &format!("{:#?}", fully_reparsed.tree().syntax()),
             &format!("{:#?}", incrementally_reparsed.tree().syntax()),
         );
-        assert_eq!(fully_reparsed.errors(), incrementally_reparsed.errors());
     }
 
     #[test] // FIXME: some test here actually test token reparsing
@@ -413,42 +402,4 @@ enum Foo {
             4,
         );
     }
-
-    #[test]
-    fn reparse_str_token_with_error_unchanged() {
-        do_check(r#""<|>Unclosed<|> string literal"#, "Still unclosed", 24);
-    }
-
-    #[test]
-    fn reparse_str_token_with_error_fixed() {
-        do_check(r#""unterinated<|><|>"#, "\"", 12);
-    }
-
-    #[test]
-    fn reparse_block_with_error_in_middle_unchanged() {
-        do_check(
-            r#"fn main() {
-                if {}
-                32 + 4<|><|>
-                return
-                if {}
-            }"#,
-            "23",
-            105,
-        )
-    }
-
-    #[test]
-    fn reparse_block_with_error_in_middle_fixed() {
-        do_check(
-            r#"fn main() {
-                if {}
-                32 + 4<|><|>
-                return
-                if {}
-            }"#,
-            ";",
-            105,
-        )
-    }
 }
diff --git a/crates/ra_syntax/src/syntax_error.rs b/crates/ra_syntax/src/syntax_error.rs
index 54acf7847b..7f9d366184 100644
--- a/crates/ra_syntax/src/syntax_error.rs
+++ b/crates/ra_syntax/src/syntax_error.rs
@@ -1,44 +1,209 @@
-//! See docs for `SyntaxError`.
+//! FIXME: write short doc here
 
 use std::fmt;
 
-use crate::{TextRange, TextUnit};
+use ra_parser::ParseError;
+
+use crate::{validation::EscapeError, TextRange, TextUnit, TokenizeError};
 
-/// Represents the result of unsuccessful tokenization, parsing
-/// or tree validation.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct SyntaxError(String, TextRange);
-
-// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed)
-// It was introduced in this PR: https://github.com/rust-analyzer/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95
-// but it was not removed by a mistake.
-//
-// So, we need to find a place where to stick validation for attributes in match clauses.
-// Code before refactor:
-// InvalidMatchInnerAttr => {
-//    write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
-// }
+pub struct SyntaxError {
+    kind: SyntaxErrorKind,
+    location: Location,
+}
+
+// FIXME: Location should be just `Location(TextRange)`
+// TextUnit enum member just unnecessarily compicates things,
+// we should'n treat it specially, it just as a `TextRange { start: x, end: x + 1 }`
+// see `location_to_range()` in ra_ide/src/diagnostics
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub enum Location {
+    Offset(TextUnit),
+    Range(TextRange),
+}
+
+impl From<TextUnit> for Location {
+    fn from(offset: TextUnit) -> Location {
+        Location::Offset(offset)
+    }
+}
+
+impl From<TextRange> for Location {
+    fn from(range: TextRange) -> Location {
+        Location::Range(range)
+    }
+}
+
+impl fmt::Debug for Location {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Location::Offset(it) => fmt::Debug::fmt(it, f),
+            Location::Range(it) => fmt::Debug::fmt(it, f),
+        }
+    }
+}
 
 impl SyntaxError {
-    pub fn new(message: impl Into<String>, range: TextRange) -> Self {
-        Self(message.into(), range)
+    pub fn new<L: Into<Location>>(kind: SyntaxErrorKind, loc: L) -> SyntaxError {
+        SyntaxError { kind, location: loc.into() }
     }
-    pub fn new_at_offset(message: impl Into<String>, offset: TextUnit) -> Self {
-        Self(message.into(), TextRange::offset_len(offset, 0.into()))
+
+    pub fn kind(&self) -> SyntaxErrorKind {
+        self.kind.clone()
     }
 
-    pub fn range(&self) -> TextRange {
-        self.1
+    pub fn location(&self) -> Location {
+        self.location.clone()
     }
 
-    pub fn with_range(mut self, range: TextRange) -> Self {
-        self.1 = range;
+    pub fn offset(&self) -> TextUnit {
+        match self.location {
+            Location::Offset(offset) => offset,
+            Location::Range(range) => range.start(),
+        }
+    }
+
+    pub fn add_offset(mut self, plus_offset: TextUnit, minus_offset: TextUnit) -> SyntaxError {
+        self.location = match self.location {
+            Location::Range(range) => Location::Range(range + plus_offset - minus_offset),
+            Location::Offset(offset) => Location::Offset(offset + plus_offset - minus_offset),
+        };
+
         self
     }
+
+    pub fn debug_dump(&self, acc: &mut impl fmt::Write) {
+        writeln!(acc, "error {:?}: {}", self.location(), self.kind()).unwrap();
+    }
 }
 
 impl fmt::Display for SyntaxError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self.0.fmt(f)
+        self.kind.fmt(f)
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum SyntaxErrorKind {
+    ParseError(ParseError),
+    EscapeError(EscapeError),
+    TokenizeError(TokenizeError),
+    // FIXME: the obvious pattern of this enum dictates that the following enum variants
+    // should be wrapped into something like `SemmanticError(SemmanticError)`
+    // or `ValidateError(ValidateError)` or `SemmanticValidateError(...)`
+    InvalidBlockAttr,
+    InvalidMatchInnerAttr,
+    InvalidTupleIndexFormat,
+    VisibilityNotAllowed,
+    InclusiveRangeMissingEnd,
+}
+
+impl fmt::Display for SyntaxErrorKind {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::SyntaxErrorKind::*;
+        match self {
+            InvalidBlockAttr => {
+                write!(f, "A block in this position cannot accept inner attributes")
+            }
+            InvalidMatchInnerAttr => {
+                write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
+            }
+            InvalidTupleIndexFormat => {
+                write!(f, "Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix")
+            }
+            ParseError(msg) => write!(f, "{}", msg.0),
+            EscapeError(err) => write!(f, "{}", err),
+            TokenizeError(err) => write!(f, "{}", err),
+            VisibilityNotAllowed => {
+                write!(f, "unnecessary visibility qualifier")
+            }
+            InclusiveRangeMissingEnd => {
+                write!(f, "An inclusive range must have an end expression")
+            }
+        }
+    }
+}
+
+impl fmt::Display for TokenizeError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        #[rustfmt::skip]
+        let msg = match self {
+            TokenizeError::EmptyInt => {
+                "Missing digits after the integer base prefix"
+            }
+            TokenizeError::EmptyExponent => {
+                "Missing digits after the exponent symbol"
+            }
+            TokenizeError::UnterminatedBlockComment => {
+                "Missing trailing `*/` symbols to terminate the block comment"
+            }
+            TokenizeError::UnterminatedChar => {
+                "Missing trailing `'` symbol to terminate the character literal"
+            }
+            TokenizeError::UnterminatedByte => {
+                "Missing trailing `'` symbol to terminate the byte literal"
+            }
+            TokenizeError::UnterminatedString => {
+                "Missing trailing `\"` symbol to terminate the string literal"
+            }
+            TokenizeError::UnterminatedByteString => {
+                "Missing trailing `\"` symbol to terminate the byte string literal"
+            }
+            TokenizeError::UnterminatedRawString => {
+                "Missing trailing `\"` with `#` symbols to terminate the raw string literal"
+            }
+            TokenizeError::UnterminatedRawByteString => {
+                "Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"
+            }
+            TokenizeError::UnstartedRawString => {
+                "Missing `\"` symbol after `#` symbols to begin the raw string literal"
+            }
+            TokenizeError::UnstartedRawByteString => {
+                "Missing `\"` symbol after `#` symbols to begin the raw byte string literal"
+            }
+            TokenizeError::LifetimeStartsWithNumber => {
+                "Lifetime name cannot start with a number"
+            }
+        };
+        write!(f, "{}", msg)
+    }
+}
+
+impl fmt::Display for EscapeError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let msg = match self {
+            EscapeError::ZeroChars => "Empty literal",
+            EscapeError::MoreThanOneChar => "Literal should be one character long",
+            EscapeError::LoneSlash => "Character must be escaped: '\\'",
+            EscapeError::InvalidEscape => "Invalid escape sequence",
+            EscapeError::BareCarriageReturn => "Character must be escaped: '\r'",
+            EscapeError::EscapeOnlyChar => "Character must be escaped",
+            EscapeError::TooShortHexEscape => "Escape sequence should have two digits",
+            EscapeError::InvalidCharInHexEscape => "Escape sequence should be a hexadecimal number",
+            EscapeError::OutOfRangeHexEscape => "Escape sequence should be ASCII",
+            EscapeError::NoBraceInUnicodeEscape => "Invalid escape sequence",
+            EscapeError::InvalidCharInUnicodeEscape => "Invalid escape sequence",
+            EscapeError::EmptyUnicodeEscape => "Invalid escape sequence",
+            EscapeError::UnclosedUnicodeEscape => "Missing '}'",
+            EscapeError::LeadingUnderscoreUnicodeEscape => "Invalid escape sequence",
+            EscapeError::OverlongUnicodeEscape => {
+                "Unicode escape sequence should have at most 6 digits"
+            }
+            EscapeError::LoneSurrogateUnicodeEscape => {
+                "Unicode escape code should not be a surrogate"
+            }
+            EscapeError::OutOfRangeUnicodeEscape => {
+                "Unicode escape code should be at most 0x10FFFF"
+            }
+            EscapeError::UnicodeEscapeInByte => "Unicode escapes are not allowed in bytes",
+            EscapeError::NonAsciiCharInByte => "Non ASCII characters are not allowed in bytes",
+        };
+        write!(f, "{}", msg)
+    }
+}
+
+impl From<EscapeError> for SyntaxErrorKind {
+    fn from(err: EscapeError) -> Self {
+        SyntaxErrorKind::EscapeError(err)
     }
 }
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index 4e3a1460d5..7c2b18af34 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -6,9 +6,13 @@
 //! The *real* implementation is in the (language-agnostic) `rowan` crate, this
 //! module just wraps its API.
 
+use ra_parser::ParseError;
 use rowan::{GreenNodeBuilder, Language};
 
-use crate::{Parse, SmolStr, SyntaxError, SyntaxKind, TextUnit};
+use crate::{
+    syntax_error::{SyntaxError, SyntaxErrorKind},
+    Parse, SmolStr, SyntaxKind, TextUnit,
+};
 
 pub(crate) use rowan::{GreenNode, GreenToken};
 
@@ -69,7 +73,8 @@ impl SyntaxTreeBuilder {
         self.inner.finish_node()
     }
 
-    pub fn error(&mut self, error: ra_parser::ParseError, text_pos: TextUnit) {
-        self.errors.push(SyntaxError::new_at_offset(error.0, text_pos))
+    pub fn error(&mut self, error: ParseError, text_pos: TextUnit) {
+        let error = SyntaxError::new(SyntaxErrorKind::ParseError(error), text_pos);
+        self.errors.push(error)
     }
 }
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 863859dcaa..8a5f0e4b75 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -5,76 +5,92 @@ mod block;
 use rustc_lexer::unescape;
 
 use crate::{
-    ast, match_ast, AstNode, SyntaxError,
+    ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind,
     SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF},
     SyntaxNode, SyntaxToken, TextUnit, T,
 };
 
-fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
-    use unescape::EscapeError as EE;
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum EscapeError {
+    ZeroChars,
+    MoreThanOneChar,
+    LoneSlash,
+    InvalidEscape,
+    BareCarriageReturn,
+    EscapeOnlyChar,
+    TooShortHexEscape,
+    InvalidCharInHexEscape,
+    OutOfRangeHexEscape,
+    NoBraceInUnicodeEscape,
+    InvalidCharInUnicodeEscape,
+    EmptyUnicodeEscape,
+    UnclosedUnicodeEscape,
+    LeadingUnderscoreUnicodeEscape,
+    OverlongUnicodeEscape,
+    LoneSurrogateUnicodeEscape,
+    OutOfRangeUnicodeEscape,
+    UnicodeEscapeInByte,
+    NonAsciiCharInByte,
+}
 
-    #[rustfmt::skip]
-    let err_message = match err {
-        EE::ZeroChars => {
-            "Literal must not be empty"
-        }
-        EE::MoreThanOneChar => {
-            "Literal must be one character long"
-        }
-        EE::LoneSlash => {
-            "Character must be escaped: `\\`"
-        }
-        EE::InvalidEscape => {
-            "Invalid escape"
-        }
-        EE::BareCarriageReturn | EE::BareCarriageReturnInRawString => {
-            "Character must be escaped: `\r`"
-        }
-        EE::EscapeOnlyChar => {
-            "Escape character `\\` must be escaped itself"
-        }
-        EE::TooShortHexEscape => {
-            "ASCII hex escape code must have exactly two digits"
-        }
-        EE::InvalidCharInHexEscape => {
-            "ASCII hex escape code must contain only hex characters"
-        }
-        EE::OutOfRangeHexEscape => {
-            "ASCII hex escape code must be at most 0x7F"
-        }
-        EE::NoBraceInUnicodeEscape => {
-            "Missing `{` to begin the unicode escape"
-        }
-        EE::InvalidCharInUnicodeEscape => {
-            "Unicode escape must contain only hex characters and underscores"
-        }
-        EE::EmptyUnicodeEscape => {
-            "Unicode escape must not be empty"
-        }
-        EE::UnclosedUnicodeEscape => {
-            "Missing '}' to terminate the unicode escape"
-        }
-        EE::LeadingUnderscoreUnicodeEscape => {
-            "Unicode escape code must not begin with an underscore"
-        }
-        EE::OverlongUnicodeEscape => {
-            "Unicode escape code must have at most 6 digits"
-        }
-        EE::LoneSurrogateUnicodeEscape => {
-            "Unicode escape code must not be a surrogate"
-        }
-        EE::OutOfRangeUnicodeEscape => {
-            "Unicode escape code must be at most 0x10FFFF"
-        }
-        EE::UnicodeEscapeInByte => {
-            "Byte literals must not contain unicode escapes"
-        }
-        EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => {
-            "Byte literals must not contain non-ASCII characters"
+impl From<rustc_lexer::unescape::EscapeError> for EscapeError {
+    fn from(err: rustc_lexer::unescape::EscapeError) -> Self {
+        match err {
+            rustc_lexer::unescape::EscapeError::ZeroChars => EscapeError::ZeroChars,
+            rustc_lexer::unescape::EscapeError::MoreThanOneChar => EscapeError::MoreThanOneChar,
+            rustc_lexer::unescape::EscapeError::LoneSlash => EscapeError::LoneSlash,
+            rustc_lexer::unescape::EscapeError::InvalidEscape => EscapeError::InvalidEscape,
+            rustc_lexer::unescape::EscapeError::BareCarriageReturn
+            | rustc_lexer::unescape::EscapeError::BareCarriageReturnInRawString => {
+                EscapeError::BareCarriageReturn
+            }
+            rustc_lexer::unescape::EscapeError::EscapeOnlyChar => EscapeError::EscapeOnlyChar,
+            rustc_lexer::unescape::EscapeError::TooShortHexEscape => EscapeError::TooShortHexEscape,
+            rustc_lexer::unescape::EscapeError::InvalidCharInHexEscape => {
+                EscapeError::InvalidCharInHexEscape
+            }
+            rustc_lexer::unescape::EscapeError::OutOfRangeHexEscape => {
+                EscapeError::OutOfRangeHexEscape
+            }
+            rustc_lexer::unescape::EscapeError::NoBraceInUnicodeEscape => {
+                EscapeError::NoBraceInUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::InvalidCharInUnicodeEscape => {
+                EscapeError::InvalidCharInUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::EmptyUnicodeEscape => {
+                EscapeError::EmptyUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::UnclosedUnicodeEscape => {
+                EscapeError::UnclosedUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::LeadingUnderscoreUnicodeEscape => {
+                EscapeError::LeadingUnderscoreUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::OverlongUnicodeEscape => {
+                EscapeError::OverlongUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::LoneSurrogateUnicodeEscape => {
+                EscapeError::LoneSurrogateUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::OutOfRangeUnicodeEscape => {
+                EscapeError::OutOfRangeUnicodeEscape
+            }
+            rustc_lexer::unescape::EscapeError::UnicodeEscapeInByte => {
+                EscapeError::UnicodeEscapeInByte
+            }
+            rustc_lexer::unescape::EscapeError::NonAsciiCharInByte
+            | rustc_lexer::unescape::EscapeError::NonAsciiCharInByteString => {
+                EscapeError::NonAsciiCharInByte
+            }
         }
-    };
+    }
+}
 
-    err_message
+impl From<rustc_lexer::unescape::EscapeError> for SyntaxErrorKind {
+    fn from(err: rustc_lexer::unescape::EscapeError) -> Self {
+        SyntaxErrorKind::EscapeError(err.into())
+    }
 }
 
 pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
@@ -102,7 +118,6 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
 }
 
 fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
-    // FIXME: move this function to outer scope (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366196658)
     fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> {
         text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end))
     }
@@ -110,10 +125,9 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
     let token = literal.token();
     let text = token.text().as_str();
 
-    // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
     let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
         let off = token.text_range().start() + TextUnit::from_usize(off + prefix_len);
-        acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
+        acc.push(SyntaxError::new(err.into(), off));
     };
 
     match token.kind() {
@@ -181,8 +195,7 @@ fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<Syntax
     if let Some(int_token) = int_token(name_ref) {
         if int_token.text().chars().any(|c| !c.is_digit(10)) {
             errors.push(SyntaxError::new(
-                "Tuple (struct) field access is only allowed through \
-                decimal integers with no underscores or suffix",
+                SyntaxErrorKind::InvalidTupleIndexFormat,
                 int_token.text_range(),
             ));
         }
@@ -202,21 +215,21 @@ fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
         FN_DEF | CONST_DEF | TYPE_ALIAS_DEF => (),
         _ => return,
     }
-
     let impl_block = match parent.parent().and_then(|it| it.parent()).and_then(ast::ImplBlock::cast)
     {
         Some(it) => it,
         None => return,
     };
     if impl_block.target_trait().is_some() {
-        errors.push(SyntaxError::new("Unnecessary visibility qualifier", vis.syntax.text_range()));
+        errors
+            .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range()))
     }
 }
 
 fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
     if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() {
         errors.push(SyntaxError::new(
-            "An inclusive range must have an end expression",
+            SyntaxErrorKind::InclusiveRangeMissingEnd,
             expr.syntax().text_range(),
         ));
     }
diff --git a/crates/ra_syntax/src/validation/block.rs b/crates/ra_syntax/src/validation/block.rs
index 8e962ab5b7..c85bbc1f4f 100644
--- a/crates/ra_syntax/src/validation/block.rs
+++ b/crates/ra_syntax/src/validation/block.rs
@@ -1,8 +1,9 @@
-//! Logic for validating block expressions i.e. `ast::BlockExpr`.
+//! FIXME: write short doc here
 
 use crate::{
     ast::{self, AstNode, AttrsOwner},
     SyntaxError,
+    SyntaxErrorKind::*,
     SyntaxKind::*,
 };
 
@@ -14,11 +15,10 @@ pub(crate) fn validate_block_expr(expr: ast::BlockExpr, errors: &mut Vec<SyntaxE
         }
     }
     if let Some(block) = expr.block() {
-        errors.extend(block.attrs().map(|attr| {
-            SyntaxError::new(
-                "A block in this position cannot accept inner attributes",
-                attr.syntax().text_range(),
-            )
-        }))
+        errors.extend(
+            block
+                .attrs()
+                .map(|attr| SyntaxError::new(InvalidBlockAttr, attr.syntax().text_range())),
+        )
     }
 }


Test patch:
diff --git a/crates/ra_syntax/src/tests.rs b/crates/ra_syntax/src/tests.rs
index 912e6aec0e..fb22b9e54c 100644
--- a/crates/ra_syntax/src/tests.rs
+++ b/crates/ra_syntax/src/tests.rs
@@ -5,7 +5,7 @@ use std::{
 
 use test_utils::{collect_tests, dir_tests, project_dir, read_text};
 
-use crate::{fuzz, tokenize, SourceFile, SyntaxError, Token};
+use crate::{fuzz, tokenize, Location, SourceFile, SyntaxError, TextRange, Token};
 
 #[test]
 fn lexer_tests() {
@@ -128,8 +128,25 @@ fn dump_tokens_and_errors(tokens: &[Token], errors: &[SyntaxError], text: &str)
         writeln!(acc, "{:?} {} {:?}", token.kind, token_len, token_text).unwrap();
     }
     for err in errors {
-        writeln!(acc, "> error{:?} token({:?}) msg({})", err.range(), &text[err.range()], err)
-            .unwrap();
+        let err_range = location_to_range(err.location());
+        writeln!(
+            acc,
+            "> error{:?} token({:?}) msg({})",
+            err.location(),
+            &text[err_range],
+            err.kind()
+        )
+        .unwrap();
+    }
+    return acc;
+
+    // FIXME: copy-pasted this from `ra_ide/src/diagnostics.rs`
+    // `Location` will be refactored soon in new PR, see todos here:
+    // https://github.com/rust-analyzer/rust-analyzer/issues/223
+    fn location_to_range(location: Location) -> TextRange {
+        match location {
+            Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
+            Location::Range(range) => range,
+        }
     }
-    acc
 }
diff --git a/crates/ra_syntax/test_data/parser/err/0000_struct_field_missing_comma.txt b/crates/ra_syntax/test_data/parser/err/0000_struct_field_missing_comma.txt
index edcd936b07..dc143945ad 100644
--- a/crates/ra_syntax/test_data/parser/err/0000_struct_field_missing_comma.txt
+++ b/crates/ra_syntax/test_data/parser/err/0000_struct_field_missing_comma.txt
@@ -31,4 +31,4 @@ SOURCE_FILE@[0; 34)
                 IDENT@[29; 32) "u32"
       WHITESPACE@[32; 33) "\n"
       R_CURLY@[33; 34) "}"
-error [21; 21): expected COMMA
+error 21: expected COMMA
diff --git a/crates/ra_syntax/test_data/parser/err/0001_item_recovery_in_file.txt b/crates/ra_syntax/test_data/parser/err/0001_item_recovery_in_file.txt
index 2d653715ea..181bcdb9ec 100644
--- a/crates/ra_syntax/test_data/parser/err/0001_item_recovery_in_file.txt
+++ b/crates/ra_syntax/test_data/parser/err/0001_item_recovery_in_file.txt
@@ -14,5 +14,5 @@ SOURCE_FILE@[0; 21)
     RECORD_FIELD_DEF_LIST@[19; 21)
       L_CURLY@[19; 20) "{"
       R_CURLY@[20; 21) "}"
-error [0; 0): expected an item
-error [3; 3): expected an item
+error 0: expected an item
+error 3: expected an item
diff --git a/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt b/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
index 0026805838..bdb5fa6c5a 100644
--- a/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
+++ b/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
@@ -26,14 +26,14 @@ SOURCE_FILE@[0; 42)
     NAME@[36; 41)
       IDENT@[36; 41) "rusti"
   WHITESPACE@[41; 42) "\n"
-error [23; 23): expected `[`
-error [23; 23): expected an item
-error [27; 27): expected one of `*`, `::`, `{`, `self`, `super` or an identifier
-error [28; 28): expected SEMI
-error [31; 31): expected EXCL
-error [31; 31): expected `{`, `[`, `(`
-error [31; 31): expected SEMI
-error [31; 31): expected an item
-error [35; 35): expected EXCL
-error [41; 41): expected `{`, `[`, `(`
-error [41; 41): expected SEMI
+error 23: expected `[`
+error 23: expected an item
+error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
+error 28: expected SEMI
+error 31: expected EXCL
+error 31: expected `{`, `[`, `(`
+error 31: expected SEMI
+error 31: expected an item
+error 35: expected EXCL
+error 41: expected `{`, `[`, `(`
+error 41: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/err/0003_C++_semicolon.txt b/crates/ra_syntax/test_data/parser/err/0003_C++_semicolon.txt
index 8039a89137..58d005902b 100644
--- a/crates/ra_syntax/test_data/parser/err/0003_C++_semicolon.txt
+++ b/crates/ra_syntax/test_data/parser/err/0003_C++_semicolon.txt
@@ -35,5 +35,5 @@ SOURCE_FILE@[0; 40)
       R_CURLY@[38; 39) "}"
   ERROR@[39; 40)
     SEMI@[39; 40) ";"
-error [39; 39): expected item, found `;`
+error 39: expected item, found `;`
 consider removing this semicolon
diff --git a/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.txt b/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.txt
index 5f6e109869..5210a884d9 100644
--- a/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.txt
+++ b/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.txt
@@ -13,4 +13,4 @@ SOURCE_FILE@[0; 12)
           ERROR@[9; 11)
             INT_NUMBER@[9; 11) "92"
     SEMI@[11; 12) ";"
-error [9; 9): expected identifier
+error 9: expected identifier
diff --git a/crates/ra_syntax/test_data/parser/err/0005_attribute_recover.txt b/crates/ra_syntax/test_data/parser/err/0005_attribute_recover.txt
index cc11421a9a..7c1a231701 100644
--- a/crates/ra_syntax/test_data/parser/err/0005_attribute_recover.txt
+++ b/crates/ra_syntax/test_data/parser/err/0005_attribute_recover.txt
@@ -55,6 +55,6 @@ SOURCE_FILE@[0; 54)
         WHITESPACE@[51; 52) "\n"
         R_CURLY@[52; 53) "}"
   WHITESPACE@[53; 54) "\n"
-error [53; 53): expected R_PAREN
-error [53; 53): expected `]`
-error [53; 53): expected an item
+error 53: expected R_PAREN
+error 53: expected `]`
+error 53: expected an item
diff --git a/crates/ra_syntax/test_data/parser/err/0006_named_field_recovery.txt b/crates/ra_syntax/test_data/parser/err/0006_named_field_recovery.txt
index 84fd928625..5f5198b31e 100644
--- a/crates/ra_syntax/test_data/parser/err/0006_named_field_recovery.txt
+++ b/crates/ra_syntax/test_data/parser/err/0006_named_field_recovery.txt
@@ -64,11 +64,11 @@ SOURCE_FILE@[0; 74)
       WHITESPACE@[71; 72) "\n"
       R_CURLY@[72; 73) "}"
   WHITESPACE@[73; 74) "\n"
-error [31; 31): expected field declaration
-error [33; 33): expected COMMA
-error [38; 38): expected field declaration
-error [39; 39): expected COMMA
-error [40; 40): expected field declaration
-error [41; 41): expected COMMA
-error [42; 42): expected field declaration
-error [43; 43): expected COMMA
+error 31: expected field declaration
+error 33: expected COMMA
+error 38: expected field declaration
+error 39: expected COMMA
+error 40: expected field declaration
+error 41: expected COMMA
+error 42: expected field declaration
+error 43: expected COMMA
diff --git a/crates/ra_syntax/test_data/parser/err/0007_stray_curly_in_file.txt b/crates/ra_syntax/test_data/parser/err/0007_stray_curly_in_file.txt
index 1978f30fa4..7ae4174410 100644
--- a/crates/ra_syntax/test_data/parser/err/0007_stray_curly_in_file.txt
+++ b/crates/ra_syntax/test_data/parser/err/0007_stray_curly_in_file.txt
@@ -28,6 +28,6 @@ SOURCE_FILE@[0; 31)
   ERROR@[29; 30)
     R_CURLY@[29; 30) "}"
   WHITESPACE@[30; 31) "\n"
-error [0; 0): unmatched `}`
-error [14; 14): unmatched `}`
-error [29; 29): unmatched `}`
+error 0: unmatched `}`
+error 14: unmatched `}`
+error 29: unmatched `}`
diff --git a/crates/ra_syntax/test_data/parser/err/0008_item_block_recovery.txt b/crates/ra_syntax/test_data/parser/err/0008_item_block_recovery.txt
index 98248227d8..1a4b40a2b0 100644
--- a/crates/ra_syntax/test_data/parser/err/0008_item_block_recovery.txt
+++ b/crates/ra_syntax/test_data/parser/err/0008_item_block_recovery.txt
@@ -76,6 +76,6 @@ SOURCE_FILE@[0; 95)
         WHITESPACE@[92; 93) "\n"
         R_CURLY@[93; 94) "}"
   WHITESPACE@[94; 95) "\n"
-error [17; 17): expected EXCL
-error [19; 19): expected SEMI
-error [20; 20): expected an item
+error 17: expected EXCL
+error 19: expected SEMI
+error 20: expected an item
diff --git a/crates/ra_syntax/test_data/parser/err/0009_broken_struct_type_parameter.txt b/crates/ra_syntax/test_data/parser/err/0009_broken_struct_type_parameter.txt
index ca508ac7c7..e147d6424b 100644
--- a/crates/ra_syntax/test_data/parser/err/0009_broken_struct_type_parameter.txt
+++ b/crates/ra_syntax/test_data/parser/err/0009_broken_struct_type_parameter.txt
@@ -45,14 +45,14 @@ SOURCE_FILE@[0; 43)
       IDENT@[40; 41) "T"
     SEMI@[41; 42) ";"
   WHITESPACE@[42; 43) "\n"
-error [9; 9): expected type parameter
-error [11; 11): expected COMMA
-error [11; 11): expected R_ANGLE
-error [11; 11): expected `;`, `{`, or `(`
-error [12; 12): expected an item
-error [14; 14): expected an item
-error [15; 15): expected an item
-error [17; 17): expected an item
-error [24; 24): expected SEMI
-error [24; 24): expected expression
-error [25; 25): expected SEMI
+error 9: expected type parameter
+error 11: expected COMMA
+error 11: expected R_ANGLE
+error 11: expected `;`, `{`, or `(`
+error 12: expected an item
+error 14: expected an item
+error 15: expected an item
+error 17: expected an item
+error 24: expected SEMI
+error 24: expected expression
+error 25: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt b/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt
index e0edf6a2d8..b73dda5ad8 100644
--- a/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt
+++ b/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt
@@ -40,4 +40,4 @@ SOURCE_FILE@[0; 42)
         WHITESPACE@[39; 40) "\n"
         R_CURLY@[40; 41) "}"
   WHITESPACE@[41; 42) "\n"
-error [24; 24): expected `{`
+error 24: expected `{`
diff --git a/crates/ra_syntax/test_data/parser/err/0011_extern_struct.txt b/crates/ra_syntax/test_data/parser/err/0011_extern_struct.txt
index 900894dcf9..381147dc0a 100644
--- a/crates/ra_syntax/test_data/parser/err/0011_extern_struct.txt
+++ b/crates/ra_syntax/test_data/parser/err/0011_extern_struct.txt
@@ -10,4 +10,4 @@ SOURCE_FILE@[0; 19)
       IDENT@[14; 17) "Foo"
     SEMI@[17; 18) ";"
   WHITESPACE@[18; 19) "\n"
-error [6; 6): expected existential, fn, trait or impl
+error 6: expected existential, fn, trait or impl
diff --git a/crates/ra_syntax/test_data/parser/err/0013_invalid_type.txt b/crates/ra_syntax/test_data/parser/err/0013_invalid_type.txt
index 7a934cf663..4907bac6da 100644
--- a/crates/ra_syntax/test_data/parser/err/0013_invalid_type.txt
+++ b/crates/ra_syntax/test_data/parser/err/0013_invalid_type.txt
@@ -69,21 +69,21 @@ SOURCE_FILE@[0; 86)
   ERROR@[83; 84)
     SEMI@[83; 84) ";"
   WHITESPACE@[84; 86) "\n\n"
-error [67; 67): expected type
-error [68; 68): expected COMMA
-error [68; 68): expected R_ANGLE
-error [68; 68): expected COMMA
-error [68; 68): expected R_ANGLE
-error [68; 68): expected COMMA
-error [68; 68): expected R_ANGLE
-error [68; 68): expected COMMA
-error [72; 72): expected COMMA
-error [72; 72): expected a type
-error [72; 72): expected R_PAREN
-error [72; 72): expected SEMI
-error [72; 72): expected an item
-error [73; 73): expected an item
-error [79; 79): expected an item
-error [80; 80): expected an item
-error [82; 82): expected an item
-error [83; 83): expected an item
+error 67: expected type
+error 68: expected COMMA
+error 68: expected R_ANGLE
+error 68: expected COMMA
+error 68: expected R_ANGLE
+error 68: expected COMMA
+error 68: expected R_ANGLE
+error 68: expected COMMA
+error 72: expected COMMA
+error 72: expected a type
+error 72: expected R_PAREN
+error 72: expected SEMI
+error 72: expected an item
+error 73: expected an item
+error 79: expected an item
+error 80: expected an item
+error 82: expected an item
+error 83: expected an item
diff --git a/crates/ra_syntax/test_data/parser/err/0014_where_no_bounds.txt b/crates/ra_syntax/test_data/parser/err/0014_where_no_bounds.txt
index a25d641b82..0ba17cf8df 100644
--- a/crates/ra_syntax/test_data/parser/err/0014_where_no_bounds.txt
+++ b/crates/ra_syntax/test_data/parser/err/0014_where_no_bounds.txt
@@ -29,4 +29,4 @@ SOURCE_FILE@[0; 23)
         L_CURLY@[20; 21) "{"
         R_CURLY@[21; 22) "}"
   WHITESPACE@[22; 23) "\n"
-error [19; 19): expected colon
+error 19: expected colon
diff --git a/crates/ra_syntax/test_data/parser/err/0015_curly_in_params.txt b/crates/ra_syntax/test_data/parser/err/0015_curly_in_params.txt
index 36b848be33..23ca0c4467 100644
--- a/crates/ra_syntax/test_data/parser/err/0015_curly_in_params.txt
+++ b/crates/ra_syntax/test_data/parser/err/0015_curly_in_params.txt
@@ -16,9 +16,9 @@ SOURCE_FILE@[0; 14)
     WHITESPACE@[11; 12) "\n"
     R_CURLY@[12; 13) "}"
   WHITESPACE@[13; 14) "\n"
-error [7; 7): expected value parameter
-error [7; 7): expected R_PAREN
-error [7; 7): expected a block
-error [7; 7): unmatched `}`
-error [8; 8): expected an item
-error [10; 10): expected an item
+error 7: expected value parameter
+error 7: expected R_PAREN
+error 7: expected a block
+error 7: unmatched `}`
+error 8: expected an item
+error 10: expected an item
diff --git a/crates/ra_syntax/test_data/parser/err/0016_missing_semi.txt b/crates/ra_syntax/test_data/parser/err/0016_missing_semi.txt
index 6343580e00..bc5be6a668 100644
--- a/crates/ra_syntax/test_data/parser/err/0016_missing_semi.txt
+++ b/crates/ra_syntax/test_data/parser/err/0016_missing_semi.txt
@@ -41,4 +41,4 @@ SOURCE_FILE@[0; 56)
         WHITESPACE@[53; 54) "\n"
         R_CURLY@[54; 55) "}"
   WHITESPACE@[55; 56) "\n"
-error [38; 38): expected SEMI
+error 38: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/err/0017_incomplete_binexpr.txt b/crates/ra_syntax/test_data/parser/err/0017_incomplete_binexpr.txt
index 59480e9994..9033a5b383 100644
--- a/crates/ra_syntax/test_data/parser/err/0017_incomplete_binexpr.txt
+++ b/crates/ra_syntax/test_data/parser/err/0017_incomplete_binexpr.txt
@@ -44,4 +44,4 @@ SOURCE_FILE@[0; 47)
         WHITESPACE@[44; 45) "\n"
         R_CURLY@[45; 46) "}"
   WHITESPACE@[46; 47) "\n"
-error [44; 44): expected expression
+error 44: expected expression
diff --git a/crates/ra_syntax/test_data/parser/err/0018_incomplete_fn.txt b/crates/ra_syntax/test_data/parser/err/0018_incomplete_fn.txt
index b9e60f6c13..d12f4c99be 100644
--- a/crates/ra_syntax/test_data/parser/err/0018_incomplete_fn.txt
+++ b/crates/ra_syntax/test_data/parser/err/0018_incomplete_fn.txt
@@ -125,8 +125,8 @@ SOURCE_FILE@[0; 183)
       WHITESPACE@[180; 181) "\n"
       R_CURLY@[181; 182) "}"
   WHITESPACE@[182; 183) "\n"
-error [34; 34): expected pattern
-error [34; 34): expected COLON
-error [34; 34): expected type
-error [180; 180): expected function arguments
-error [180; 180): expected a block
+error 34: expected pattern
+error 34: expected COLON
+error 34: expected type
+error 180: expected function arguments
+error 180: expected a block
diff --git a/crates/ra_syntax/test_data/parser/err/0019_let_recover.txt b/crates/ra_syntax/test_data/parser/err/0019_let_recover.txt
index 97e91a94f5..cc3578e54f 100644
--- a/crates/ra_syntax/test_data/parser/err/0019_let_recover.txt
+++ b/crates/ra_syntax/test_data/parser/err/0019_let_recover.txt
@@ -95,13 +95,13 @@ SOURCE_FILE@[0; 139)
         WHITESPACE@[136; 137) "\n"
         R_CURLY@[137; 138) "}"
   WHITESPACE@[138; 139) "\n"
-error [24; 24): expected expression
-error [24; 24): expected SEMI
-error [49; 49): expected pattern
-error [49; 49): expected SEMI
-error [75; 75): expected pattern
-error [75; 75): expected SEMI
-error [98; 98): expected pattern
-error [98; 98): expected SEMI
-error [124; 124): expected pattern
-error [124; 124): expected SEMI
+error 24: expected expression
+error 24: expected SEMI
+error 49: expected pattern
+error 49: expected SEMI
+error 75: expected pattern
+error 75: expected SEMI
+error 98: expected pattern
+error 98: expected SEMI
+error 124: expected pattern
+error 124: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/err/0020_fn_recover.txt b/crates/ra_syntax/test_data/parser/err/0020_fn_recover.txt
index c11dc23f53..70727d0595 100644
--- a/crates/ra_syntax/test_data/parser/err/0020_fn_recover.txt
+++ b/crates/ra_syntax/test_data/parser/err/0020_fn_recover.txt
@@ -16,6 +16,6 @@ SOURCE_FILE@[0; 16)
         L_CURLY@[13; 14) "{"
         R_CURLY@[14; 15) "}"
   WHITESPACE@[15; 16) "\n"
-error [2; 2): expected a name
-error [2; 2): expected function arguments
-error [2; 2): expected a block
+error 2: expected a name
+error 2: expected function arguments
+error 2: expected a block
diff --git a/crates/ra_syntax/test_data/parser/err/0021_incomplete_param.txt b/crates/ra_syntax/test_data/parser/err/0021_incomplete_param.txt
index ae04122d88..3337824800 100644
--- a/crates/ra_syntax/test_data/parser/err/0021_incomplete_param.txt
+++ b/crates/ra_syntax/test_data/parser/err/0021_incomplete_param.txt
@@ -31,5 +31,5 @@ SOURCE_FILE@[0; 22)
         WHITESPACE@[19; 20) "\n"
         R_CURLY@[20; 21) "}"
   WHITESPACE@[21; 22) "\n"
-error [16; 16): expected COLON
-error [16; 16): expected type
+error 16: expected COLON
+error 16: expected type
diff --git a/crates/ra_syntax/test_data/parser/err/0022_bad_exprs.txt b/crates/ra_syntax/test_data/parser/err/0022_bad_exprs.txt
index bb87022b08..cb45eb2fc2 100644
--- a/crates/ra_syntax/test_data/parser/err/0022_bad_exprs.txt
+++ b/crates/ra_syntax/test_data/parser/err/0022_bad_exprs.txt
@@ -148,36 +148,36 @@ SOURCE_FILE@[0; 112)
         WHITESPACE@[109; 110) " "
         R_CURLY@[110; 111) "}"
   WHITESPACE@[111; 112) "\n"
-error [16; 16): expected expression
-error [17; 17): expected R_BRACK
-error [17; 17): expected SEMI
-error [17; 17): expected expression
-error [18; 18): expected SEMI
-error [25; 25): expected a name
-error [26; 26): expected `;`, `{`, or `(`
-error [30; 30): expected pattern
-error [31; 31): expected SEMI
-error [53; 53): expected expression
-error [54; 54): expected SEMI
-error [54; 54): expected expression
-error [55; 55): expected SEMI
-error [60; 60): expected type
-error [60; 60): expected `{`
-error [60; 60): expected expression
-error [61; 61): expected SEMI
-error [65; 65): expected pattern
-error [65; 65): expected SEMI
-error [65; 65): expected expression
-error [92; 92): expected expression
-error [93; 93): expected SEMI
-error [93; 93): expected expression
-error [94; 94): expected SEMI
-error [95; 95): expected expression
-error [96; 96): expected SEMI
-error [96; 96): expected expression
-error [97; 97): expected SEMI
-error [103; 103): expected a name
-error [104; 104): expected `{`
-error [108; 108): expected pattern
-error [108; 108): expected SEMI
-error [108; 108): expected expression
+error 16: expected expression
+error 17: expected R_BRACK
+error 17: expected SEMI
+error 17: expected expression
+error 18: expected SEMI
+error 25: expected a name
+error 26: expected `;`, `{`, or `(`
+error 30: expected pattern
+error 31: expected SEMI
+error 53: expected expression
+error 54: expected SEMI
+error 54: expected expression
+error 55: expected SEMI
+error 60: expected type
+error 60: expected `{`
+error 60: expected expression
+error 61: expected SEMI
+error 65: expected pattern
+error 65: expected SEMI
+error 65: expected expression
+error 92: expected expression
+error 93: expected SEMI
+error 93: expected expression
+error 94: expected SEMI
+error 95: expected expression
+error 96: expected SEMI
+error 96: expected expression
+error 97: expected SEMI
+error 103: expected a name
+error 104: expected `{`
+error 108: expected pattern
+error 108: expected SEMI
+error 108: expected expression
diff --git a/crates/ra_syntax/test_data/parser/err/0023_mismatched_paren.txt b/crates/ra_syntax/test_data/parser/err/0023_mismatched_paren.txt
index 775e4b0da4..6dd04f80e8 100644
--- a/crates/ra_syntax/test_data/parser/err/0023_mismatched_paren.txt
+++ b/crates/ra_syntax/test_data/parser/err/0023_mismatched_paren.txt
@@ -40,5 +40,5 @@ SOURCE_FILE@[0; 94)
   ERROR@[92; 93)
     R_CURLY@[92; 93) "}"
   WHITESPACE@[93; 94) "\n"
-error [49; 49): unmatched `}`
-error [92; 92): unmatched `}`
+error 49: unmatched `}`
+error 92: unmatched `}`
diff --git a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.txt b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.txt
index c5c8a29ba3..0a93e11a53 100644
--- a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.txt
+++ b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.txt
@@ -290,32 +290,32 @@ SOURCE_FILE@[0; 240)
         WHITESPACE@[237; 238) "\n"
         R_CURLY@[238; 239) "}"
   WHITESPACE@[239; 240) "\n"
-error [88; 88): expected COMMA
-error [88; 88): expected R_ANGLE
-error [121; 121): expected SEMI
-error [121; 121): expected expression
-error [140; 140): expected type
-error [141; 141): expected R_PAREN
-error [141; 141): expected COMMA
-error [141; 141): expected R_ANGLE
-error [141; 141): expected SEMI
-error [146; 146): expected SEMI
-error [146; 146): expected expression
-error [147; 147): expected SEMI
-error [148; 148): expected expression
-error [149; 149): expected SEMI
-error [154; 154): expected pattern
-error [155; 155): expected IN_KW
-error [155; 155): expected expression
-error [157; 157): expected a block
-error [165; 165): expected expression
-error [168; 168): expected expression
-error [179; 179): expected expression
-error [180; 180): expected COMMA
-error [180; 180): expected expression
-error [180; 180): expected R_PAREN
-error [180; 180): expected SEMI
-error [215; 215): expected COMMA
-error [215; 215): expected R_ANGLE
-error [235; 235): expected SEMI
-error [235; 235): expected expression
+error 88: expected COMMA
+error 88: expected R_ANGLE
+error 121: expected SEMI
+error 121: expected expression
+error 140: expected type
+error 141: expected R_PAREN
+error 141: expected COMMA
+error 141: expected R_ANGLE
+error 141: expected SEMI
+error 146: expected SEMI
+error 146: expected expression
+error 147: expected SEMI
+error 148: expected expression
+error 149: expected SEMI
+error 154: expected pattern
+error 155: expected IN_KW
+error 155: expected expression
+error 157: expected a block
+error 165: expected expression
+error 168: expected expression
+error 179: expected expression
+error 180: expected COMMA
+error 180: expected expression
+error 180: expected R_PAREN
+error 180: expected SEMI
+error 215: expected COMMA
+error 215: expected R_ANGLE
+error 235: expected SEMI
+error 235: expected expression
diff --git a/crates/ra_syntax/test_data/parser/err/0025_nope.txt b/crates/ra_syntax/test_data/parser/err/0025_nope.txt
index ca7f2d2552..8369f4bdab 100644
--- a/crates/ra_syntax/test_data/parser/err/0025_nope.txt
+++ b/crates/ra_syntax/test_data/parser/err/0025_nope.txt
@@ -191,14 +191,14 @@ SOURCE_FILE@[0; 575)
         WHITESPACE@[572; 573) "\n"
         R_CURLY@[573; 574) "}"
   WHITESPACE@[574; 575) "\n"
-error [95; 95): expected type
-error [95; 95): expected COMMA
-error [96; 96): expected field
-error [98; 98): expected field declaration
-error [371; 371): expected COMMA
-error [372; 372): expected a type
-error [372; 372): expected R_PAREN
-error [372; 372): expected COMMA
-error [372; 372): expected enum variant
-error [374; 374): expected enum variant
-error [508; 508): expected expression
+error 95: expected type
+error 95: expected COMMA
+error 96: expected field
+error 98: expected field declaration
+error 371: expected COMMA
+error 372: expected a type
+error 372: expected R_PAREN
+error 372: expected COMMA
+error 372: expected enum variant
+error 374: expected enum variant
+error 508: expected expression
diff --git a/crates/ra_syntax/test_data/parser/err/0026_imp_recovery.txt b/crates/ra_syntax/test_data/parser/err/0026_imp_recovery.txt
index 3942e09049..d3da2f54f7 100644
--- a/crates/ra_syntax/test_data/parser/err/0026_imp_recovery.txt
+++ b/crates/ra_syntax/test_data/parser/err/0026_imp_recovery.txt
@@ -45,5 +45,5 @@ SOURCE_FILE@[0; 38)
       L_CURLY@[35; 36) "{"
       R_CURLY@[36; 37) "}"
   WHITESPACE@[37; 38) "\n"
-error [14; 14): expected trait or type
-error [14; 14): expected `{`
+error 14: expected trait or type
+error 14: expected `{`
diff --git a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.txt b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.txt
index 4a28bcabff..9298b45a42 100644
--- a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.txt
+++ b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.txt
@@ -25,5 +25,5 @@ SOURCE_FILE@[0; 30)
         L_CURLY@[27; 28) "{"
         R_CURLY@[28; 29) "}"
   WHITESPACE@[29; 30) "\n"
-error [26; 26): expected a path
-error [26; 26): expected colon
+error 26: expected a path
+error 26: expected colon
diff --git a/crates/ra_syntax/test_data/parser/err/0029_field_completion.txt b/crates/ra_syntax/test_data/parser/err/0029_field_completion.txt
index 1778494760..e3e1282ecb 100644
--- a/crates/ra_syntax/test_data/parser/err/0029_field_completion.txt
+++ b/crates/ra_syntax/test_data/parser/err/0029_field_completion.txt
@@ -33,4 +33,4 @@ SOURCE_FILE@[0; 24)
         WHITESPACE@[21; 22) "\n"
         R_CURLY@[22; 23) "}"
   WHITESPACE@[23; 24) "\n"
-error [21; 21): expected field name or number
+error 21: expected field name or number
diff --git a/crates/ra_syntax/test_data/parser/err/0032_match_arms_inner_attrs.txt b/crates/ra_syntax/test_data/parser/err/0032_match_arms_inner_attrs.txt
index c36e2f770c..dbc0081ac5 100644
--- a/crates/ra_syntax/test_data/parser/err/0032_match_arms_inner_attrs.txt
+++ b/crates/ra_syntax/test_data/parser/err/0032_match_arms_inner_attrs.txt
@@ -191,14 +191,14 @@ SOURCE_FILE@[0; 293)
         WHITESPACE@[290; 291) "\n"
         R_CURLY@[291; 292) "}"
   WHITESPACE@[292; 293) "\n"
-error [52; 52): expected `[`
-error [52; 52): expected pattern
-error [53; 53): expected FAT_ARROW
-error [78; 78): expected COMMA
-error [161; 161): expected `[`
-error [161; 161): expected pattern
-error [162; 162): expected FAT_ARROW
-error [232; 232): expected `[`
-error [232; 232): expected pattern
-error [233; 233): expected FAT_ARROW
-error [250; 250): expected COMMA
+error 52: expected `[`
+error 52: expected pattern
+error 53: expected FAT_ARROW
+error 78: expected COMMA
+error 161: expected `[`
+error 161: expected pattern
+error 162: expected FAT_ARROW
+error 232: expected `[`
+error 232: expected pattern
+error 233: expected FAT_ARROW
+error 250: expected COMMA
diff --git a/crates/ra_syntax/test_data/parser/err/0033_match_arms_outer_attrs.txt b/crates/ra_syntax/test_data/parser/err/0033_match_arms_outer_attrs.txt
index e914e688bb..fd5ccc3f25 100644
--- a/crates/ra_syntax/test_data/parser/err/0033_match_arms_outer_attrs.txt
+++ b/crates/ra_syntax/test_data/parser/err/0033_match_arms_outer_attrs.txt
@@ -62,6 +62,6 @@ SOURCE_FILE@[0; 89)
         WHITESPACE@[86; 87) "\n"
         R_CURLY@[87; 88) "}"
   WHITESPACE@[88; 89) "\n"
-error [80; 80): expected pattern
-error [80; 80): expected FAT_ARROW
-error [80; 80): expected expression
+error 80: expected pattern
+error 80: expected FAT_ARROW
+error 80: expected expression
diff --git a/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt b/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt
index 2c91b6841f..21269fb027 100644
--- a/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt
+++ b/crates/ra_syntax/test_data/parser/err/0034_bad_box_pattern.txt
@@ -88,9 +88,9 @@ SOURCE_FILE@[0; 91)
         WHITESPACE@[87; 88) "\n"
         R_CURLY@[88; 89) "}"
   WHITESPACE@[89; 91) "\n\n"
-error [24; 24): expected a name
-error [27; 27): expected SEMI
-error [48; 48): expected a name
-error [51; 51): expected SEMI
-error [76; 76): expected a name
-error [79; 79): expected SEMI
+error 24: expected a name
+error 27: expected SEMI
+error 48: expected a name
+error 51: expected SEMI
+error 76: expected a name
+error 79: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt b/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt
index 8cb4ea7963..6368408288 100644
--- a/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt
+++ b/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt
@@ -48,7 +48,7 @@ SOURCE_FILE@[0; 48)
         L_CURLY@[45; 46) "{"
         R_CURLY@[46; 47) "}"
   WHITESPACE@[47; 48) "\n"
-error [17; 17): expected one of `*`, `::`, `{`, `self`, `super` or an identifier
-error [17; 17): expected SEMI
-error [37; 37): expected one of `*`, `::`, `{`, `self`, `super` or an identifier
-error [37; 37): expected SEMI
+error 17: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
+error 17: expected SEMI
+error 37: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
+error 37: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/err/0036_partial_use.txt b/crates/ra_syntax/test_data/parser/err/0036_partial_use.txt
index f5490fbe81..181f408c87 100644
--- a/crates/ra_syntax/test_data/parser/err/0036_partial_use.txt
+++ b/crates/ra_syntax/test_data/parser/err/0036_partial_use.txt
@@ -39,13 +39,13 @@ SOURCE_FILE@[0; 37)
         ERROR@[35; 36)
           SEMI@[35; 36) ";"
   WHITESPACE@[36; 37) "\n"
-error [22; 22): expected COMMA
-error [22; 22): expected one of `*`, `::`, `{`, `self`, `super` or an identifier
-error [23; 23): expected COMMA
-error [24; 24): expected one of `*`, `::`, `{`, `self`, `super` or an identifier
-error [27; 27): expected COMMA
-error [35; 35): expected COMMA
-error [35; 35): expected one of `*`, `::`, `{`, `self`, `super` or an identifier
-error [36; 36): expected COMMA
-error [36; 36): expected R_CURLY
-error [36; 36): expected SEMI
+error 22: expected COMMA
+error 22: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
+error 23: expected COMMA
+error 24: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
+error 27: expected COMMA
+error 35: expected COMMA
+error 35: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
+error 36: expected COMMA
+error 36: expected R_CURLY
+error 36: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt b/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt
index d0a128a5fb..749c8cddbc 100644
--- a/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt
+++ b/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt
@@ -94,6 +94,6 @@ SOURCE_FILE@[0; 118)
       WHITESPACE@[115; 116) "\n"
       R_CURLY@[116; 117) "}"
   WHITESPACE@[117; 118) "\n"
-error [36; 39): Unnecessary visibility qualifier
-error [56; 66): Unnecessary visibility qualifier
-error [86; 96): Unnecessary visibility qualifier
+error [36; 39): unnecessary visibility qualifier
+error [56; 66): unnecessary visibility qualifier
+error [86; 96): unnecessary visibility qualifier
diff --git a/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.txt b/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.txt
index 4a2f0a6961..d1544634ce 100644
--- a/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.txt
+++ b/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.txt
@@ -80,4 +80,4 @@ SOURCE_FILE@[0; 83)
         WHITESPACE@[80; 81) "\n"
         R_CURLY@[81; 82) "}"
   WHITESPACE@[82; 83) "\n"
-error [56; 56): expected expression
+error 56: expected expression
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0001_array_type_missing_semi.txt b/crates/ra_syntax/test_data/parser/inline/err/0001_array_type_missing_semi.txt
index 530533b71f..3f0f1b4805 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0001_array_type_missing_semi.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0001_array_type_missing_semi.txt
@@ -20,8 +20,8 @@ SOURCE_FILE@[0; 18)
   ERROR@[16; 17)
     SEMI@[16; 17) ";"
   WHITESPACE@[17; 18) "\n"
-error [12; 12): expected `;` or `]`
-error [12; 12): expected SEMI
-error [13; 13): expected an item
-error [15; 15): expected an item
-error [16; 16): expected an item
+error 12: expected `;` or `]`
+error 12: expected SEMI
+error 13: expected an item
+error 15: expected an item
+error 16: expected an item
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0002_misplaced_label_err.txt b/crates/ra_syntax/test_data/parser/inline/err/0002_misplaced_label_err.txt
index 0187d872da..2950d77efa 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0002_misplaced_label_err.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0002_misplaced_label_err.txt
@@ -23,7 +23,7 @@ SOURCE_FILE@[0; 30)
         WHITESPACE@[27; 28) "\n"
         R_CURLY@[28; 29) "}"
   WHITESPACE@[29; 30) "\n"
-error [22; 22): expected a loop
-error [22; 22): expected SEMI
-error [27; 27): expected type
-error [27; 27): expected `{`
+error 22: expected a loop
+error 22: expected SEMI
+error 27: expected type
+error 27: expected `{`
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0003_pointer_type_no_mutability.txt b/crates/ra_syntax/test_data/parser/inline/err/0003_pointer_type_no_mutability.txt
index 2ab29eecc1..b97e339bb4 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0003_pointer_type_no_mutability.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0003_pointer_type_no_mutability.txt
@@ -14,4 +14,4 @@ SOURCE_FILE@[0; 14)
         R_PAREN@[11; 12) ")"
     SEMI@[12; 13) ";"
   WHITESPACE@[13; 14) "\n"
-error [10; 10): expected mut or const in raw pointer type (use `*mut T` or `*const T` as appropriate)
+error 10: expected mut or const in raw pointer type (use `*mut T` or `*const T` as appropriate)
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0004_impl_type.txt b/crates/ra_syntax/test_data/parser/inline/err/0004_impl_type.txt
index d5aea05c2a..43321b1ed8 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0004_impl_type.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0004_impl_type.txt
@@ -73,7 +73,7 @@ SOURCE_FILE@[0; 87)
       L_CURLY@[84; 85) "{"
       R_CURLY@[85; 86) "}"
   WHITESPACE@[86; 87) "\n"
-error [38; 38): expected trait or type
-error [38; 38): expected `{`
-error [70; 70): expected trait or type
-error [70; 70): expected `{`
+error 38: expected trait or type
+error 38: expected `{`
+error 70: expected trait or type
+error 70: expected `{`
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.txt b/crates/ra_syntax/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.txt
index 9e9186ad45..451f29d398 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.txt
@@ -16,8 +16,8 @@ SOURCE_FILE@[0; 20)
   ERROR@[18; 19)
     SEMI@[18; 19) ";"
   WHITESPACE@[19; 20) "\n"
-error [15; 15): expected `fn`
-error [15; 15): expected SEMI
-error [16; 16): expected an item
-error [17; 17): expected an item
-error [18; 18): expected an item
+error 15: expected `fn`
+error 15: expected SEMI
+error 16: expected an item
+error 17: expected an item
+error 18: expected an item
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0006_unsafe_block_in_mod.txt b/crates/ra_syntax/test_data/parser/inline/err/0006_unsafe_block_in_mod.txt
index 690acdca35..f3fbc48280 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0006_unsafe_block_in_mod.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0006_unsafe_block_in_mod.txt
@@ -33,5 +33,5 @@ SOURCE_FILE@[0; 33)
         L_CURLY@[30; 31) "{"
         R_CURLY@[31; 32) "}"
   WHITESPACE@[32; 33) "\n"
-error [11; 11): expected an item
-error [18; 18): expected an item
+error 11: expected an item
+error 18: expected an item
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0007_async_without_semicolon.txt b/crates/ra_syntax/test_data/parser/inline/err/0007_async_without_semicolon.txt
index a4002a998d..5cfe766d78 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0007_async_without_semicolon.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0007_async_without_semicolon.txt
@@ -29,4 +29,4 @@ SOURCE_FILE@[0; 30)
         WHITESPACE@[27; 28) " "
         R_CURLY@[28; 29) "}"
   WHITESPACE@[29; 30) "\n"
-error [27; 27): expected SEMI
+error 27: expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0008_pub_expr.txt b/crates/ra_syntax/test_data/parser/inline/err/0008_pub_expr.txt
index 6f45a4fa6e..0594f148f0 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0008_pub_expr.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0008_pub_expr.txt
@@ -23,4 +23,4 @@ SOURCE_FILE@[0; 21)
         WHITESPACE@[18; 19) " "
         R_CURLY@[19; 20) "}"
   WHITESPACE@[20; 21) "\n"
-error [14; 14): expected an item
+error 14: expected an item
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.txt b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.txt
index e6d3a5c953..2ca5b8f32f 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.txt
@@ -57,5 +57,5 @@ SOURCE_FILE@[0; 48)
         WHITESPACE@[45; 46) "\n"
         R_CURLY@[46; 47) "}"
   WHITESPACE@[47; 48) "\n"
-error [24; 24): attributes are not allowed on BIN_EXPR
-error [44; 44): attributes are not allowed on IF_EXPR
+error 24: attributes are not allowed on BIN_EXPR
+error 44: attributes are not allowed on IF_EXPR
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.txt b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.txt
index f6ac0feaf5..289193b9e7 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.txt
@@ -37,5 +37,5 @@ SOURCE_FILE@[0; 50)
         L_CURLY@[47; 48) "{"
         R_CURLY@[48; 49) "}"
   WHITESPACE@[49; 50) "\n"
-error [6; 6): expected existential, fn, trait or impl
-error [31; 31): expected existential, fn, trait or impl
+error 6: expected existential, fn, trait or impl
+error 31: expected existential, fn, trait or impl
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0013_static_underscore.txt b/crates/ra_syntax/test_data/parser/inline/err/0013_static_underscore.txt
index 5b3dc5af27..28f75a3418 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0013_static_underscore.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0013_static_underscore.txt
@@ -18,4 +18,4 @@ SOURCE_FILE@[0; 19)
       INT_NUMBER@[16; 17) "5"
     SEMI@[17; 18) ";"
   WHITESPACE@[18; 19) "\n"
-error [7; 7): expected a name
+error 7: expected a name
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.txt b/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.txt
index 25d80be1d2..a1a0bd7c4f 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.txt
@@ -51,9 +51,9 @@ SOURCE_FILE@[0; 62)
       WHITESPACE@[59; 60) "\n"
       R_CURLY@[60; 61) "}"
   WHITESPACE@[61; 62) "\n"
-error [21; 21): expected EXCL
-error [21; 21): expected `{`, `[`, `(`
-error [21; 21): expected SEMI
-error [47; 47): expected EXCL
-error [47; 47): expected `{`, `[`, `(`
-error [47; 47): expected SEMI
+error 21: expected EXCL
+error 21: expected `{`, `[`, `(`
+error 21: expected SEMI
+error 47: expected EXCL
+error 47: expected `{`, `[`, `(`
+error 47: expected SEMI