Repository: rust-analyzer/rust-analyzer
Pull request #3026 (2020-02-06 00:40:08)

ra_syntax: reshape SyntaxError for the sake of removing redundancy
Followup of #2911, also puts some crosses to the todo list of #223.

**AHTUNG!** A big part of the diff of this PR are test data files changes.

Simplified `SyntaxError` that was `SyntaxError { kind: { /* big enum */  }, location: Location }` to `SyntaxError(String, TextRange)`. I am not sure whether the tuple struct here is best fit, I am inclined to add names to the fields, because I already provide getters `SyntaxError::message()`, `SyntaxError::range()`.
I also removed `Location` altogether ...

This is currently WIP, because the following is not done:
- [ ] ~~Add tests to `test_data` dir for unescape errors *// I don't know where to put these errors in particular, because they are out of the scope of the lexer and parser. However, I have an idea in mind that we move all validators we have right now to parsing stage, but this is up to discussion...*~~ **[UPD]** I came to a conclusion that tree validation logic, which unescape errors are a part of, should be rethought of, we currently have no tests and no place to put tests for tree validations. So I'd like to extract potential redesign (maybe move of tree validation to ra_parser) and adding tests for this into a separate task.

matklad: >A big part of the diff of this PR are test data files changes.

A nice trick here to commit code changes and test data changes in sepparate commits, so that the diff is easier to review. 
matklad: No strong opinion here, but I prefer to document modules, rather than
structs, because they are more useful for high-level picture.

On Thu, 6 Feb 2020 at 12:01, Veetaha <notifications@github.com> wrote:

> *@Veetaha* commented on this pull request.
> ------------------------------
>
> In crates/ra_syntax/src/syntax_error.rs
> <https://github.com/rust-analyzer/rust-analyzer/pull/3026#discussion_r375770341>
> :
>
> > @@ -1,209 +1,47 @@
> -//! FIXME: write short doc here
> +//! Module that defines `SyntaxError`.
>
> I moved this description to doc comment on top of SynraxError itself,
> otherwise it would be repetition
>
> —
> You are receiving this because your review was requested.
> Reply to this email directly, view it on GitHub
> <https://github.com/rust-analyzer/rust-analyzer/pull/3026?email_source=notifications&email_token=AANB3M7MBEF3LGDHA4GRAVTRBPU2FA5CNFSM4KQUPYH2YY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCUPS7CI#discussion_r375770341>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AANB3M5RXDLNDUEYVN7ZGE3RBPU2FANCNFSM4KQUPYHQ>
> .
>

Veetaha: @matklad 
> No strong opinion here, but I prefer to document modules, rather than
structs, because they are more useful for high-level picture.

https://github.com/rust-analyzer/rust-analyzer/blob/355c98fd0861acf0f0fddad08cbc923fee0698fb/crates/ra_ide_db/src/feature_flags.rs#L1-L16

But I see here you decided to document a struct instead of a module ...
matklad: I cannot be right because of that specific reason: my point was rather that
we should now allow UI concerns (how exactly is the red squiggle shown)
bleed into domain model. If we want to customize the squiggle, the place to
do that is conv.rs all the way up in ra+lsp-server. But yeah, it is
convenient that vscode just does the right thing out of the box here,

On Friday, 7 February 2020, Veetaha <notifications@github.com> wrote:

> *@Veetaha* commented on this pull request.
> ------------------------------
>
> In crates/ra_syntax/src/syntax_error.rs
> <https://github.com/rust-analyzer/rust-analyzer/pull/3026#discussion_r376280249>
> :
>
> >      }
> -
> -    pub fn kind(&self) -> SyntaxErrorKind {
> -        self.kind.clone()
> +    pub fn new_at_offset(message: impl Into<String>, offset: TextUnit) -> Self {
> +        Self(message.into(), TextRange::offset_len(offset, 1.into()))
>
> Apparently you are right, this is what I found in vscode docs
> <https://code.visualstudio.com/api/language-extensions/language-server-extension-guide#diagnostics-tips-and-tricks>
>
> If the start and end positions are the same, VS Code will underline with a
> squiggle the word at that position.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/rust-analyzer/rust-analyzer/pull/3026?email_source=notifications&email_token=AANB3M2ZKB4IHT5FEOLCP4LRBUPUJA5CNFSM4KQUPYH2YY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCUUQ5GI#discussion_r376280249>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AANB3MZSK72ISQXLC445AFDRBUPUJANCNFSM4KQUPYHQ>
> .
>

Veetaha: @matklad, I know this is not the most important thing, but might I ask you, please to elaborate on [this comment](https://github.com/rust-analyzer/rust-analyzer/pull/3026#issuecomment-582929678)?
matklad: Still no strong opinion, though I find there's a meaningful difference between "See X" (which is a redirect) and "This module defines X" (which reads like a placeholder comment) :)
Veetaha: But I do have a strong opinion. When you use the struct itself, you get documentation popups with more detailed docs, but you don't get them when you trigger autocompletion for the module itself (mentioning that you often just `use module` and never trigger completion for it thereafter), (maybe this is a bug, that docs don't appear on modules?)
Veetaha: See X sounds better, you are right!
matklad: Yeah, convinced, you are right! We should move all single-struct modules to `See X` style!

Patch:
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 22bd497231..82596c665c 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},
-    Location, SyntaxNode, TextRange, T,
+    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: location_to_range(err.location()),
+        range: err.range(),
         message: format!("Syntax Error: {}", err),
         severity: Severity::Error,
         fix: None,
@@ -116,12 +116,6 @@ 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_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index f8f4b64c1e..e3f74da6de 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -41,11 +41,9 @@ 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, TokenizeError,
-    },
+    parsing::{lex_single_syntax_kind, lex_single_valid_syntax_kind, tokenize, Token},
     ptr::{AstPtr, SyntaxNodePtr},
-    syntax_error::{Location, SyntaxError, SyntaxErrorKind},
+    syntax_error::SyntaxError,
     syntax_node::{
         Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder,
     },
@@ -117,7 +115,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.location(), err.kind()).unwrap();
+            writeln!(buf, "error {:?}: {}", err.range(), err).unwrap();
         }
         buf
     }
diff --git a/crates/ra_syntax/src/parsing/lexer.rs b/crates/ra_syntax/src/parsing/lexer.rs
index f889e6a1d6..f2684c852a 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, SyntaxErrorKind,
+    SyntaxError,
     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, error) =
+        let (syntax_kind, err_message) =
             rustc_token_kind_to_syntax_kind(&rustc_token.kind, &text[token_range]);
 
         tokens.push(Token { kind: syntax_kind, len: token_len });
 
-        if let Some(error) = error {
-            errors.push(SyntaxError::new(SyntaxErrorKind::TokenizeError(error), token_range));
+        if let Some(err_message) = err_message {
+            errors.push(SyntaxError::new(err_message, token_range));
         }
 
         offset += rustc_token.len;
@@ -94,61 +94,21 @@ fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> {
     }
 
     let rustc_token = rustc_lexer::first_token(text);
-    let (syntax_kind, error) = rustc_token_kind_to_syntax_kind(&rustc_token.kind, text);
+    let (syntax_kind, err_message) = rustc_token_kind_to_syntax_kind(&rustc_token.kind, text);
 
     let token = Token { kind: syntax_kind, len: TextUnit::from_usize(rustc_token.len) };
-    let error = error.map(|error| {
-        SyntaxError::new(
-            SyntaxErrorKind::TokenizeError(error),
-            TextRange::from_to(TextUnit::from(0), TextUnit::of_str(text)),
-        )
+    let optional_error = err_message.map(|err_message| {
+        SyntaxError::new(err_message, TextRange::from_to(0.into(), TextUnit::of_str(text)))
     });
 
-    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,
+    Some((token, optional_error))
 }
 
+/// 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<TokenizeError>) {
+) -> (SyntaxKind, Option<&'static str>) {
     // 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
@@ -156,14 +116,15 @@ 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(TE::UnterminatedBlockComment));
+                return (
+                    COMMENT,
+                    Some("Missing trailing `*/` symbols to terminate the block comment"),
+                );
             }
 
             TK::Whitespace => WHITESPACE,
@@ -181,7 +142,7 @@ fn rustc_token_kind_to_syntax_kind(
 
             TK::Lifetime { starts_with_number: false } => LIFETIME,
             TK::Lifetime { starts_with_number: true } => {
-                return (LIFETIME, Some(TE::LifetimeStartsWithNumber))
+                return (LIFETIME, Some("Lifetime name cannot start with a number"))
             }
 
             TK::Semi => SEMI,
@@ -217,57 +178,56 @@ fn rustc_token_kind_to_syntax_kind(
 
     return (syntax_kind, None);
 
-    fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<TokenizeError>) {
+    fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) {
         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(TE::EmptyInt))
+                return (INT_NUMBER, Some("Missing digits after the integer base prefix"))
             }
 
             LK::Float { empty_exponent: false, .. } => FLOAT_NUMBER,
             LK::Float { empty_exponent: true, .. } => {
-                return (FLOAT_NUMBER, Some(TE::EmptyExponent))
+                return (FLOAT_NUMBER, Some("Missing digits after the exponent symbol"))
             }
 
             LK::Char { terminated: true } => CHAR,
             LK::Char { terminated: false } => {
-                return (CHAR, Some(TE::UnterminatedChar))
+                return (CHAR, Some("Missing trailing `'` symbol to terminate the character literal"))
             }
 
             LK::Byte { terminated: true } => BYTE,
             LK::Byte { terminated: false } => {
-                return (BYTE, Some(TE::UnterminatedByte))
+                return (BYTE, Some("Missing trailing `'` symbol to terminate the byte literal"))
             }
 
             LK::Str { terminated: true } => STRING,
             LK::Str { terminated: false } => {
-                return (STRING, Some(TE::UnterminatedString))
+                return (STRING, Some("Missing trailing `\"` symbol to terminate the string literal"))
             }
 
 
             LK::ByteStr { terminated: true } => BYTE_STRING,
             LK::ByteStr { terminated: false } => {
-                return (BYTE_STRING, Some(TE::UnterminatedByteString))
+                return (BYTE_STRING, Some("Missing trailing `\"` symbol to terminate the byte string literal"))
             }
 
             LK::RawStr { started: true, terminated: true, .. } => RAW_STRING,
             LK::RawStr { started: true, terminated: false, .. } => {
-                return (RAW_STRING, Some(TE::UnterminatedRawString))
+                return (RAW_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw string literal"))
             }
             LK::RawStr { started: false, .. } => {
-                return (RAW_STRING, Some(TE::UnstartedRawString))
+                return (RAW_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw string literal"))
             }
 
             LK::RawByteStr { started: true, terminated: true, .. } => RAW_BYTE_STRING,
             LK::RawByteStr { started: true, terminated: false, .. } => {
-                return (RAW_BYTE_STRING, Some(TE::UnterminatedRawByteString))
+                return (RAW_BYTE_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"))
             }
             LK::RawByteStr { started: false, .. } => {
-                return (RAW_BYTE_STRING, Some(TE::UnstartedRawByteString))
+                return (RAW_BYTE_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw byte string literal"))
             }
         };
 
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index a86da06754..aad70d0151 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, 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_token(node, &edit) {
+        return Some((green, merge_errors(errors, new_errors, 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, TextRange)> {
+) -> Option<(GreenNode, Vec<SyntaxError>, 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, _error) = lex_single_syntax_kind(&new_text)?;
+            let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?;
 
             if new_token_kind != prev_token_kind
                 || (new_token_kind == IDENT && is_contextual_kw(&new_text))
@@ -76,7 +76,11 @@ 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), prev_token.text_range()))
+            Some((
+                prev_token.replace_with(new_token),
+                new_err.into_iter().collect(),
+                prev_token.text_range(),
+            ))
         }
         _ => None,
     }
@@ -87,7 +91,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) {
@@ -162,20 +166,27 @@ fn is_balanced(tokens: &[Token]) -> bool {
 fn merge_errors(
     old_errors: Vec<SyntaxError>,
     new_errors: Vec<SyntaxError>,
-    old_range: TextRange,
+    range_before_reparse: TextRange,
     edit: &AtomTextEdit,
 ) -> Vec<SyntaxError> {
     let mut res = Vec::new();
-    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()));
+
+    for old_err in old_errors {
+        let old_err_range = old_err.range();
+        // FIXME: make sure that .start() was here previously by a mistake
+        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 new_errors {
-        res.push(e.add_offset(old_range.start(), 0.into()));
-    }
+    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)
+    }));
     res
 }
 
@@ -193,9 +204,9 @@ mod tests {
 
         let fully_reparsed = SourceFile::parse(&after);
         let incrementally_reparsed: Parse<SourceFile> = {
-            let f = SourceFile::parse(&before);
+            let before = SourceFile::parse(&before);
             let (green, new_errors, range) =
-                incremental_reparse(f.tree().syntax(), &edit, f.errors.to_vec()).unwrap();
+                incremental_reparse(before.tree().syntax(), &edit, before.errors.to_vec()).unwrap();
             assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
             Parse::new(green, new_errors)
         };
@@ -204,6 +215,7 @@ 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
@@ -402,4 +414,42 @@ 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 7f9d366184..54acf7847b 100644
--- a/crates/ra_syntax/src/syntax_error.rs
+++ b/crates/ra_syntax/src/syntax_error.rs
@@ -1,209 +1,44 @@
-//! FIXME: write short doc here
+//! See docs for `SyntaxError`.
 
 use std::fmt;
 
-use ra_parser::ParseError;
-
-use crate::{validation::EscapeError, TextRange, TextUnit, TokenizeError};
+use crate::{TextRange, TextUnit};
 
+/// Represents the result of unsuccessful tokenization, parsing
+/// or tree validation.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-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),
-        }
-    }
-}
+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")
+// }
 
 impl SyntaxError {
-    pub fn new<L: Into<Location>>(kind: SyntaxErrorKind, loc: L) -> SyntaxError {
-        SyntaxError { kind, location: loc.into() }
+    pub fn new(message: impl Into<String>, range: TextRange) -> Self {
+        Self(message.into(), range)
     }
-
-    pub fn kind(&self) -> SyntaxErrorKind {
-        self.kind.clone()
+    pub fn new_at_offset(message: impl Into<String>, offset: TextUnit) -> Self {
+        Self(message.into(), TextRange::offset_len(offset, 0.into()))
     }
 
-    pub fn location(&self) -> Location {
-        self.location.clone()
+    pub fn range(&self) -> TextRange {
+        self.1
     }
 
-    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),
-        };
-
+    pub fn with_range(mut self, range: TextRange) -> Self {
+        self.1 = range;
         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.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)
+        self.0.fmt(f)
     }
 }
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index 7c2b18af34..4e3a1460d5 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -6,13 +6,9 @@
 //! 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::{
-    syntax_error::{SyntaxError, SyntaxErrorKind},
-    Parse, SmolStr, SyntaxKind, TextUnit,
-};
+use crate::{Parse, SmolStr, SyntaxError, SyntaxKind, TextUnit};
 
 pub(crate) use rowan::{GreenNode, GreenToken};
 
@@ -73,8 +69,7 @@ impl SyntaxTreeBuilder {
         self.inner.finish_node()
     }
 
-    pub fn error(&mut self, error: ParseError, text_pos: TextUnit) {
-        let error = SyntaxError::new(SyntaxErrorKind::ParseError(error), text_pos);
-        self.errors.push(error)
+    pub fn error(&mut self, error: ra_parser::ParseError, text_pos: TextUnit) {
+        self.errors.push(SyntaxError::new_at_offset(error.0, text_pos))
     }
 }
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 8a5f0e4b75..863859dcaa 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -5,92 +5,76 @@ mod block;
 use rustc_lexer::unescape;
 
 use crate::{
-    ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind,
+    ast, match_ast, AstNode, SyntaxError,
     SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF},
     SyntaxNode, SyntaxToken, TextUnit, T,
 };
 
-#[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,
-}
+fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
+    use unescape::EscapeError as EE;
 
-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
-            }
+    #[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 SyntaxErrorKind {
-    fn from(err: rustc_lexer::unescape::EscapeError) -> Self {
-        SyntaxErrorKind::EscapeError(err.into())
-    }
+    err_message
 }
 
 pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
@@ -118,6 +102,7 @@ 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))
     }
@@ -125,9 +110,10 @@ 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(err.into(), off));
+        acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
     };
 
     match token.kind() {
@@ -195,7 +181,8 @@ 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(
-                SyntaxErrorKind::InvalidTupleIndexFormat,
+                "Tuple (struct) field access is only allowed through \
+                decimal integers with no underscores or suffix",
                 int_token.text_range(),
             ));
         }
@@ -215,21 +202,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(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range()))
+        errors.push(SyntaxError::new("Unnecessary visibility qualifier", 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(
-            SyntaxErrorKind::InclusiveRangeMissingEnd,
+            "An inclusive range must have an end expression",
             expr.syntax().text_range(),
         ));
     }
diff --git a/crates/ra_syntax/src/validation/block.rs b/crates/ra_syntax/src/validation/block.rs
index c85bbc1f4f..8e962ab5b7 100644
--- a/crates/ra_syntax/src/validation/block.rs
+++ b/crates/ra_syntax/src/validation/block.rs
@@ -1,9 +1,8 @@
-//! FIXME: write short doc here
+//! Logic for validating block expressions i.e. `ast::BlockExpr`.
 
 use crate::{
     ast::{self, AstNode, AttrsOwner},
     SyntaxError,
-    SyntaxErrorKind::*,
     SyntaxKind::*,
 };
 
@@ -15,10 +14,11 @@ 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(InvalidBlockAttr, attr.syntax().text_range())),
-        )
+        errors.extend(block.attrs().map(|attr| {
+            SyntaxError::new(
+                "A block in this position cannot accept inner attributes",
+                attr.syntax().text_range(),
+            )
+        }))
     }
 }
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index bebd99ca93..d05f40d67a 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -16,7 +16,7 @@ export * from './ssr';
 
 export function collectGarbage(ctx: Ctx): Cmd {
     return async () => {
-        await ctx.client?.sendRequest<null>('rust-analyzer/collectGarbage', null);
+        ctx.client?.sendRequest<null>('rust-analyzer/collectGarbage', null);
     };
 }
 
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
index 9b814612aa..6287bf47b4 100644
--- a/editors/code/src/commands/ssr.ts
+++ b/editors/code/src/commands/ssr.ts
@@ -14,9 +14,9 @@ export function ssr(ctx: Ctx): Cmd {
                 if (x.includes('==>>')) {
                     return null;
                 }
-                return "Enter request: pattern ==>> template";
+                return "Enter request: pattern ==>> template"
             }
-        };
+        }
         const request = await vscode.window.showInputBox(options);
 
         if (!request) return;
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index c4d286aefe..a2db04de87 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -29,7 +29,7 @@ export function activateHighlighting(ctx: Ctx) {
                 highlighter.setHighlights(targetEditor, params.decorations);
             },
         );
-    }
+    };
 
     vscode.workspace.onDidChangeConfiguration(
         _ => highlighter.removeHighlights(),
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 26705067ae..f82df66aed 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -29,13 +29,13 @@ export function activateInlayHints(ctx: Ctx) {
 
     ctx.pushCleanup({
         dispose() {
-            hintsUpdater.clear();
+            hintsUpdater.clear()
         }
-    });
+    })
 
     // XXX: we don't await this, thus Promise rejections won't be handled, but
     // this should never throw in fact...
-    void hintsUpdater.setEnabled(ctx.config.displayInlayHints);
+    hintsUpdater.setEnabled(ctx.config.displayInlayHints)
 }
 
 interface InlayHintsParams {
@@ -57,7 +57,7 @@ const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
 const parameterHintDecorationType = vscode.window.createTextEditorDecorationType({
     before: {
         color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
-    },
+    }
 });
 
 class HintsUpdater {
diff --git a/editors/code/src/installation/download_artifact.ts b/editors/code/src/installation/download_artifact.ts
index 9996c556fc..de655f8f4b 100644
--- a/editors/code/src/installation/download_artifact.ts
+++ b/editors/code/src/installation/download_artifact.ts
@@ -15,7 +15,7 @@ import { throttle } from "throttle-debounce";
  * of the artifact as `displayName`.
  */
 export async function downloadArtifact(
-    { downloadUrl, releaseName }: ArtifactReleaseInfo,
+    {downloadUrl, releaseName}: ArtifactReleaseInfo,
     artifactFileName: string,
     installationDir: string,
     displayName: string,
@@ -23,7 +23,7 @@ export async function downloadArtifact(
     await fs.mkdir(installationDir).catch(err => assert.strictEqual(
         err?.code,
         "EEXIST",
-        `Couldn't create directory "${installationDir}" to download ` +
+        `Couldn't create directory "${installationDir}" to download `+
         `${artifactFileName} artifact: ${err.message}`
     ));
 
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index a22e0bc666..0ad7ef1bbd 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -11,7 +11,7 @@ import { Config } from './config';
 let ctx: Ctx | undefined;
 
 export async function activate(context: vscode.ExtensionContext) {
-    const config = new Config(context);
+    const config = new Config(context)
 
     const serverPath = await ensureServerBinary(config.serverSource);
     if (serverPath == null) {
@@ -33,7 +33,7 @@ export async function activate(context: vscode.ExtensionContext) {
             vscode.window.showInformationMessage('Reloading rust-analyzer...');
             // @DanTup maneuver
             // https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895
-            await deactivate();
+            await deactivate()
             for (const sub of ctx.subscriptions) {
                 try {
                     sub.dispose();
@@ -41,9 +41,9 @@ export async function activate(context: vscode.ExtensionContext) {
                     console.error(e);
                 }
             }
-            await activate(context);
-        };
-    });
+            await activate(context)
+        }
+    })
 
     ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
     ctx.registerCommand('collectGarbage', commands.collectGarbage);
@@ -54,7 +54,7 @@ export async function activate(context: vscode.ExtensionContext) {
     ctx.registerCommand('expandMacro', commands.expandMacro);
     ctx.registerCommand('run', commands.run);
     ctx.registerCommand('onEnter', commands.onEnter);
-    ctx.registerCommand('ssr', commands.ssr);
+    ctx.registerCommand('ssr', commands.ssr)
 
     // Internal commands which are invoked by the server.
     ctx.registerCommand('runSingle', commands.runSingle);
diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts
index 0f5f6ef99e..ed0d821665 100644
--- a/editors/code/src/status_display.ts
+++ b/editors/code/src/status_display.ts
@@ -15,7 +15,7 @@ export function activateStatusDisplay(ctx: Ctx) {
             WorkDoneProgress.type,
             'rustAnalyzer/cargoWatcher',
             params => statusDisplay.handleProgressNotification(params)
-        ));
+        ))
     }
 }
 


Test patch:
diff --git a/crates/ra_syntax/src/tests.rs b/crates/ra_syntax/src/tests.rs
index fb22b9e54c..912e6aec0e 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, Location, SourceFile, SyntaxError, TextRange, Token};
+use crate::{fuzz, tokenize, SourceFile, SyntaxError, Token};
 
 #[test]
 fn lexer_tests() {
@@ -128,25 +128,8 @@ fn dump_tokens_and_errors(tokens: &[Token], errors: &[SyntaxError], text: &str)
         writeln!(acc, "{:?} {} {:?}", token.kind, token_len, token_text).unwrap();
     }
     for err in errors {
-        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,
-        }
+        writeln!(acc, "> error{:?} token({:?}) msg({})", err.range(), &text[err.range()], err)
+            .unwrap();
     }
+    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 dc143945ad..edcd936b07 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: expected COMMA
+error [21; 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 181bcdb9ec..2d653715ea 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: expected an item
-error 3: expected an item
+error [0; 0): expected an item
+error [3; 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 bdb5fa6c5a..0026805838 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: 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
+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
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 58d005902b..8039a89137 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: expected item, found `;`
+error [39; 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 5210a884d9..5f6e109869 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: expected identifier
+error [9; 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 7c1a231701..cc11421a9a 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: expected R_PAREN
-error 53: expected `]`
-error 53: expected an item
+error [53; 53): expected R_PAREN
+error [53; 53): expected `]`
+error [53; 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 5f5198b31e..84fd928625 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: 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
+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
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 7ae4174410..1978f30fa4 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: unmatched `}`
-error 14: unmatched `}`
-error 29: unmatched `}`
+error [0; 0): unmatched `}`
+error [14; 14): unmatched `}`
+error [29; 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 1a4b40a2b0..98248227d8 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: expected EXCL
-error 19: expected SEMI
-error 20: expected an item
+error [17; 17): expected EXCL
+error [19; 19): expected SEMI
+error [20; 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 e147d6424b..ca508ac7c7 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: 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
+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
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 b73dda5ad8..e0edf6a2d8 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: expected `{`
+error [24; 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 381147dc0a..900894dcf9 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: expected existential, fn, trait or impl
+error [6; 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 4907bac6da..7a934cf663 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: 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
+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
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 0ba17cf8df..a25d641b82 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: expected colon
+error [19; 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 23ca0c4467..36b848be33 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: 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
+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
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 bc5be6a668..6343580e00 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: expected SEMI
+error [38; 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 9033a5b383..59480e9994 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: expected expression
+error [44; 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 d12f4c99be..b9e60f6c13 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: expected pattern
-error 34: expected COLON
-error 34: expected type
-error 180: expected function arguments
-error 180: expected a block
+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
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 cc3578e54f..97e91a94f5 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: 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
+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
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 70727d0595..c11dc23f53 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: expected a name
-error 2: expected function arguments
-error 2: expected a block
+error [2; 2): expected a name
+error [2; 2): expected function arguments
+error [2; 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 3337824800..ae04122d88 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: expected COLON
-error 16: expected type
+error [16; 16): expected COLON
+error [16; 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 cb45eb2fc2..bb87022b08 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: 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
+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
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 6dd04f80e8..775e4b0da4 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: unmatched `}`
-error 92: unmatched `}`
+error [49; 49): unmatched `}`
+error [92; 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 0a93e11a53..c5c8a29ba3 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: 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
+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
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 8369f4bdab..ca7f2d2552 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: 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
+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
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 d3da2f54f7..3942e09049 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: expected trait or type
-error 14: expected `{`
+error [14; 14): expected trait or type
+error [14; 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 9298b45a42..4a28bcabff 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: expected a path
-error 26: expected colon
+error [26; 26): expected a path
+error [26; 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 e3e1282ecb..1778494760 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: expected field name or number
+error [21; 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 dbc0081ac5..c36e2f770c 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: 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
+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
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 fd5ccc3f25..e914e688bb 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: expected pattern
-error 80: expected FAT_ARROW
-error 80: expected expression
+error [80; 80): expected pattern
+error [80; 80): expected FAT_ARROW
+error [80; 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 21269fb027..2c91b6841f 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: 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
+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
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 6368408288..8cb4ea7963 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: 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
+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
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 181f408c87..f5490fbe81 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: 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
+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
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 749c8cddbc..d0a128a5fb 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 d1544634ce..4a2f0a6961 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: expected expression
+error [56; 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 3f0f1b4805..530533b71f 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: expected `;` or `]`
-error 12: expected SEMI
-error 13: expected an item
-error 15: expected an item
-error 16: expected an item
+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
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 2950d77efa..0187d872da 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: expected a loop
-error 22: expected SEMI
-error 27: expected type
-error 27: expected `{`
+error [22; 22): expected a loop
+error [22; 22): expected SEMI
+error [27; 27): expected type
+error [27; 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 b97e339bb4..2ab29eecc1 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: expected mut or const in raw pointer type (use `*mut T` or `*const T` as appropriate)
+error [10; 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 43321b1ed8..d5aea05c2a 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: expected trait or type
-error 38: expected `{`
-error 70: expected trait or type
-error 70: expected `{`
+error [38; 38): expected trait or type
+error [38; 38): expected `{`
+error [70; 70): expected trait or type
+error [70; 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 451f29d398..9e9186ad45 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: expected `fn`
-error 15: expected SEMI
-error 16: expected an item
-error 17: expected an item
-error 18: expected an item
+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
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 f3fbc48280..690acdca35 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: expected an item
-error 18: expected an item
+error [11; 11): expected an item
+error [18; 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 5cfe766d78..a4002a998d 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: expected SEMI
+error [27; 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 0594f148f0..6f45a4fa6e 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: expected an item
+error [14; 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 2ca5b8f32f..e6d3a5c953 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: attributes are not allowed on BIN_EXPR
-error 44: attributes are not allowed on IF_EXPR
+error [24; 24): attributes are not allowed on BIN_EXPR
+error [44; 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 289193b9e7..f6ac0feaf5 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: expected existential, fn, trait or impl
-error 31: expected existential, fn, trait or impl
+error [6; 6): expected existential, fn, trait or impl
+error [31; 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 28f75a3418..5b3dc5af27 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: expected a name
+error [7; 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 a1a0bd7c4f..25d80be1d2 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: expected EXCL
-error 21: expected `{`, `[`, `(`
-error 21: expected SEMI
-error 47: expected EXCL
-error 47: expected `{`, `[`, `(`
-error 47: expected SEMI
+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