From 2064ae9e0a95c7a2bd5b23baed2b846cad5f6a6a Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Sat, 11 May 2024 04:56:22 +0000 Subject: [PATCH] refactor(parser,diagnostic): one diagnostic struct to eliminate monomorphization of generic types (#3214) part of #3213 We should only have one diagnostic struct instead 353 copies of them, so we don't end up choking LLVM with 50k lines of the same code due to monomorphization. If the proposed approach is good, then I'll start writing a codemod to turn all the existing structs to plain functions. --- Background: Using `--timings`, we see `oxc_linter` is slow on codegen (the purple part). ![image](https://github.com/zkat/miette/assets/1430279/c1df4f7d-90ef-4c0f-9956-2ec3194db7ca) The crate currently contains 353 miette errors. [cargo-llvm-lines](https://github.com/dtolnay/cargo-llvm-lines) displays ``` cargo llvm-lines -p oxc_linter --lib --release Lines Copies Function name ----- ------ ------------- 830350 33438 (TOTAL) 29252 (3.5%, 3.5%) 808 (2.4%, 2.4%) as core::ops::drop::Drop>::drop 23298 (2.8%, 6.3%) 353 (1.1%, 3.5%) miette::eyreish::error::object_downcast 19062 (2.3%, 8.6%) 706 (2.1%, 5.6%) core::error::Error::type_id 12610 (1.5%, 10.1%) 65 (0.2%, 5.8%) alloc::raw_vec::RawVec::grow_amortized 12002 (1.4%, 11.6%) 706 (2.1%, 7.9%) miette::eyreish::ptr::Own::boxed 9215 (1.1%, 12.7%) 115 (0.3%, 8.2%) core::iter::traits::iterator::Iterator::try_fold 9150 (1.1%, 13.8%) 1 (0.0%, 8.2%) oxc_linter::rules::RuleEnum::read_json 8825 (1.1%, 14.9%) 353 (1.1%, 9.3%) as core::error::Error>::source 8822 (1.1%, 15.9%) 353 (1.1%, 10.3%) miette::eyreish::error::::construct 8119 (1.0%, 16.9%) 353 (1.1%, 11.4%) miette::eyreish::error::object_ref 8119 (1.0%, 17.9%) 353 (1.1%, 12.5%) miette::eyreish::error::object_ref_stderr 7413 (0.9%, 18.8%) 353 (1.1%, 13.5%) as core::fmt::Display>::fmt 7413 (0.9%, 19.7%) 353 (1.1%, 14.6%) miette::eyreish::ptr::Own::new 6669 (0.8%, 20.5%) 39 (0.1%, 14.7%) alloc::raw_vec::RawVec::try_allocate_in 6173 (0.7%, 21.2%) 353 (1.1%, 15.7%) miette::eyreish::error::::from_std 6027 (0.7%, 21.9%) 70 (0.2%, 16.0%) as alloc::vec::spec_from_iter_nested::SpecFromIterNested>::from_iter 6001 (0.7%, 22.7%) 353 (1.1%, 17.0%) miette::eyreish::error::object_drop 6001 (0.7%, 23.4%) 353 (1.1%, 18.1%) miette::eyreish::error::object_drop_front 5648 (0.7%, 24.1%) 353 (1.1%, 19.1%) as core::fmt::Debug>::fmt ``` It's totalling more than 50k llvm lines, and is putting pressure on rustc codegen (the purple part on `oxc_linter` in the image above. --- It's pretty obvious by looking at https://github.com/zkat/miette/blob/main/src/eyreish/error.rs, the generics can expand out to lots of code. --- crates/oxc_diagnostics/src/lib.rs | 106 ++- crates/oxc_language_server/src/linter.rs | 5 +- crates/oxc_linter/examples/linter.rs | 31 +- crates/oxc_linter/src/service.rs | 6 +- crates/oxc_parser/src/cursor.rs | 8 +- crates/oxc_parser/src/diagnostics.rs | 740 ++++++++++--------- crates/oxc_parser/src/js/binding.rs | 8 +- crates/oxc_parser/src/js/class.rs | 20 +- crates/oxc_parser/src/js/declaration.rs | 16 +- crates/oxc_parser/src/js/expression.rs | 24 +- crates/oxc_parser/src/js/function.rs | 12 +- crates/oxc_parser/src/js/grammar.rs | 10 +- crates/oxc_parser/src/js/list.rs | 27 +- crates/oxc_parser/src/js/module.rs | 14 +- crates/oxc_parser/src/js/statement.rs | 22 +- crates/oxc_parser/src/jsx/mod.rs | 2 +- crates/oxc_parser/src/lexer/byte_handlers.rs | 2 +- crates/oxc_parser/src/lexer/comment.rs | 4 +- crates/oxc_parser/src/lexer/identifier.rs | 4 +- crates/oxc_parser/src/lexer/jsx.rs | 2 +- crates/oxc_parser/src/lexer/mod.rs | 12 +- crates/oxc_parser/src/lexer/numeric.rs | 2 +- crates/oxc_parser/src/lexer/regex.rs | 8 +- crates/oxc_parser/src/lexer/source.rs | 2 +- crates/oxc_parser/src/lexer/string.rs | 14 +- crates/oxc_parser/src/lexer/template.rs | 6 +- crates/oxc_parser/src/lexer/unicode.rs | 20 +- crates/oxc_parser/src/lib.rs | 28 +- crates/oxc_parser/src/ts/types.rs | 8 +- crates/oxc_span/src/span.rs | 8 +- crates/oxc_wasm/src/lib.rs | 2 +- napi/parser/src/lib.rs | 4 +- tasks/coverage/src/suite.rs | 9 +- wasm/parser/src/lib.rs | 5 +- 34 files changed, 682 insertions(+), 509 deletions(-) diff --git a/crates/oxc_diagnostics/src/lib.rs b/crates/oxc_diagnostics/src/lib.rs index be5dbb7608647..aa86aeaaa8b12 100644 --- a/crates/oxc_diagnostics/src/lib.rs +++ b/crates/oxc_diagnostics/src/lib.rs @@ -6,7 +6,11 @@ mod graphical_theme; mod reporter; mod service; -use std::path::PathBuf; +use std::{ + fmt::{self, Display}, + ops::Deref, + path::PathBuf, +}; pub use miette; pub use thiserror; @@ -21,9 +25,10 @@ pub type Error = miette::Error; pub type Severity = miette::Severity; pub type Report = miette::Report; -pub type Result = std::result::Result; +pub type Result = std::result::Result; -use miette::Diagnostic; +pub use miette::LabeledSpan; +use miette::{Diagnostic, SourceCode}; use thiserror::Error; #[derive(Debug, Error, Diagnostic)] @@ -35,3 +40,98 @@ pub struct MinifiedFileError(pub PathBuf); #[error("Failed to open file {0:?} with error \"{1}\"")] #[diagnostic(help("Failed to open file {0:?} with error \"{1}\""))] pub struct FailedToOpenFileError(pub PathBuf, pub std::io::Error); + +#[derive(Debug, Clone)] +pub struct OxcDiagnostic { + // `Box` the data to make `OxcDiagnostic` 8 bytes so that `Result` is small. + // This is required because rust does not performance return value optimization. + inner: Box, +} + +impl Deref for OxcDiagnostic { + type Target = Box; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[derive(Debug, Clone)] +pub struct OxcDiagnosticInner { + pub message: String, + pub labels: Option>, + pub help: Option, +} + +impl fmt::Display for OxcDiagnostic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.message) + } +} + +impl std::error::Error for OxcDiagnostic {} + +impl Diagnostic for OxcDiagnostic { + fn help<'a>(&'a self) -> Option> { + self.help.as_ref().map(Box::new).map(|c| c as Box) + } + + fn labels(&self) -> Option + '_>> { + self.labels + .as_ref() + .map(|ls| ls.iter().cloned()) + .map(Box::new) + .map(|b| b as Box>) + } +} + +impl OxcDiagnostic { + #[must_use] + pub fn new>(message: T) -> Self { + Self { + inner: Box::new(OxcDiagnosticInner { + message: message.into(), + labels: None, + help: None, + }), + } + } + + #[must_use] + pub fn with_help>(mut self, help: T) -> Self { + self.inner.help = Some(help.into()); + self + } + + #[must_use] + pub fn with_label>(mut self, label: T) -> Self { + self.inner.labels = Some(vec![label.into()]); + self + } + + #[must_use] + pub fn with_labels>(mut self, labels: T) -> Self { + self.inner.labels = Some(labels.into_iter().collect()); + self + } + + #[must_use] + pub fn and_label>(mut self, label: T) -> Self { + let mut labels = self.inner.labels.unwrap_or_default(); + labels.push(label.into()); + self.inner.labels = Some(labels); + self + } + + #[must_use] + pub fn and_labels>(mut self, labels: T) -> Self { + let mut all_labels = self.inner.labels.unwrap_or_default(); + all_labels.extend(labels); + self.inner.labels = Some(all_labels); + self + } + + #[must_use] + pub fn with_source_code(self, code: T) -> Error { + Error::from(self).with_source_code(code) + } +} diff --git a/crates/oxc_language_server/src/linter.rs b/crates/oxc_language_server/src/linter.rs index d187a41c332ff..12afc3eae5ad3 100644 --- a/crates/oxc_language_server/src/linter.rs +++ b/crates/oxc_language_server/src/linter.rs @@ -279,7 +279,10 @@ impl IsolatedLintHandler { let reports = ret .errors .into_iter() - .map(|diagnostic| ErrorReport { error: diagnostic, fixed_content: None }) + .map(|diagnostic| ErrorReport { + error: Error::from(diagnostic), + fixed_content: None, + }) .collect(); return Some(Self::wrap_diagnostics(path, &original_source_text, reports, start)); }; diff --git a/crates/oxc_linter/examples/linter.rs b/crates/oxc_linter/examples/linter.rs index d24565865c4b8..417137e2cb160 100644 --- a/crates/oxc_linter/examples/linter.rs +++ b/crates/oxc_linter/examples/linter.rs @@ -4,10 +4,7 @@ use std::{env, path::Path}; use oxc_allocator::Allocator; use oxc_ast::AstKind; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::{SourceType, Span}; @@ -35,18 +32,18 @@ fn main() -> std::io::Result<()> { let semantic_ret = SemanticBuilder::new(&source_text, source_type).with_trivias(ret.trivias).build(program); - let mut errors: Vec = vec![]; + let mut errors: Vec = vec![]; for node in semantic_ret.semantic.nodes().iter() { match node.kind() { AstKind::DebuggerStatement(stmt) => { - errors.push(NoDebugger(stmt.span).into()); + errors.push(no_debugger(stmt.span)); } AstKind::ArrayPattern(array) if array.elements.is_empty() => { - errors.push(NoEmptyPattern("array", array.span).into()); + errors.push(no_empty_pattern("array", array.span)); } AstKind::ObjectPattern(object) if object.properties.is_empty() => { - errors.push(NoEmptyPattern("object", object.span).into()); + errors.push(no_empty_pattern("object", object.span)); } _ => {} } @@ -61,7 +58,7 @@ fn main() -> std::io::Result<()> { Ok(()) } -fn print_errors(source_text: &str, errors: Vec) { +fn print_errors(source_text: &str, errors: Vec) { for error in errors { let error = error.with_source_code(source_text.to_string()); println!("{error:?}"); @@ -75,10 +72,9 @@ fn print_errors(source_text: &str, errors: Vec) { // 1 │ debugger; // · ───────── // ╰──── -#[derive(Debug, Error, Diagnostic)] -#[error("`debugger` statement is not allowed")] -#[diagnostic(severity(warning))] -struct NoDebugger(#[label] pub Span); +fn no_debugger(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("`debugger` statement is not allowed").with_labels([span0.into()]) +} // This prints: // @@ -88,7 +84,8 @@ struct NoDebugger(#[label] pub Span); // · ─┬ // · ╰── Empty object binding pattern // ╰──── -#[derive(Debug, Error, Diagnostic)] -#[error("empty destructuring pattern is not allowed")] -#[diagnostic(severity(warning))] -struct NoEmptyPattern(&'static str, #[label("Empty {0} binding pattern")] pub Span); +fn no_empty_pattern(s0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::new("empty destructuring pattern is not allowed").with_labels([ + LabeledSpan::new_with_span(Some(format!("Empty {s0} binding pattern")), span1), + ]) +} diff --git a/crates/oxc_linter/src/service.rs b/crates/oxc_linter/src/service.rs index 44e537b99a5bc..02cf94bcb5358 100644 --- a/crates/oxc_linter/src/service.rs +++ b/crates/oxc_linter/src/service.rs @@ -256,7 +256,11 @@ impl Runtime { .parse(); if !ret.errors.is_empty() { - return ret.errors.into_iter().map(|err| Message::new(err, None)).collect(); + return ret + .errors + .into_iter() + .map(|err| Message::new(Error::from(err), None)) + .collect(); }; let program = allocator.alloc(ret.program); diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs index 35f910e572091..af18ec6a5c976 100644 --- a/crates/oxc_parser/src/cursor.rs +++ b/crates/oxc_parser/src/cursor.rs @@ -107,7 +107,7 @@ impl<'a> ParserImpl<'a> { fn test_escaped_keyword(&mut self, kind: Kind) { if self.cur_token().escaped() && kind.is_all_keyword() { let span = self.cur_token().span(); - self.error(diagnostics::EscapedKeyword(span)); + self.error(diagnostics::escaped_keyword(span)); } } @@ -158,7 +158,7 @@ impl<'a> ParserImpl<'a> { pub(crate) fn asi(&mut self) -> Result<()> { if !self.can_insert_semicolon() { let span = Span::new(self.prev_token_end, self.cur_token().start); - return Err(diagnostics::AutoSemicolonInsertion(span).into()); + return Err(diagnostics::auto_semicolon_insertion(span)); } if self.at(Kind::Semicolon) { self.advance(Kind::Semicolon); @@ -178,9 +178,7 @@ impl<'a> ParserImpl<'a> { pub(crate) fn expect_without_advance(&mut self, kind: Kind) -> Result<()> { if !self.at(kind) { let range = self.cur_token().span(); - return Err( - diagnostics::ExpectToken(kind.to_str(), self.cur_kind().to_str(), range).into() - ); + return Err(diagnostics::expect_token(kind.to_str(), self.cur_kind().to_str(), range)); } Ok(()) } diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 9e09e2450ec09..6b44a416bf507 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -1,338 +1,406 @@ -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, -}; +use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_span::Span; -#[derive(Debug, Error, Diagnostic)] -#[error("Source length exceeds 4 GiB limit")] -#[diagnostic()] -pub struct OverlongSource; - -#[derive(Debug, Error, Diagnostic)] -#[error("Flow is not supported")] -#[diagnostic()] -pub struct Flow(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Unexpected token")] -#[diagnostic()] -pub struct UnexpectedToken(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Expected `{0}` but found `{1}`")] -#[diagnostic()] -pub struct ExpectToken(pub &'static str, pub &'static str, #[label("`{0}` expected")] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Invalid escape sequence")] -pub struct InvalidEscapeSequence(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Invalid Unicode escape sequence")] -pub struct UnicodeEscapeSequence(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Invalid Character `{0}`")] -pub struct InvalidCharacter(pub char, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Invalid characters after number")] -pub struct InvalidNumberEnd(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Unterminated multiline comment")] -pub struct UnterminatedMultiLineComment(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Unterminated string")] -pub struct UnterminatedString(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Unexpected flag {0} in regular expression literal")] -pub struct RegExpFlag(pub char, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Flag {0} is mentioned twice in regular expression literal")] -pub struct RegExpFlagTwice(pub char, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Unexpected end of file")] -pub struct UnexpectedEnd(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Unterminated regular expression")] -pub struct UnterminatedRegExp(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Invalid Number {0}")] -pub struct InvalidNumber(pub &'static str, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Keywords cannot contain escape characters")] -#[diagnostic()] -pub struct EscapedKeyword(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Expected a semicolon or an implicit semicolon after a statement, but found none")] -#[diagnostic(help("Try insert a semicolon here"))] -pub struct AutoSemicolonInsertion(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Line terminator not permitted before arrow")] -#[diagnostic()] -pub struct LineterminatorBeforeArrow(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Missing initializer in destructuring declaration")] -#[diagnostic()] -pub struct InvalidDestrucuringDeclaration(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Missing initializer in const declaration")] -#[diagnostic()] -pub struct MissinginitializerInConst(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Lexical declaration cannot appear in a single-statement context")] -#[diagnostic(help("Wrap this declaration in a block statement"))] -pub struct LexicalDeclarationSingleStatement(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Async functions can only be declared at the top level or inside a block")] -#[diagnostic()] -pub struct AsyncFunctionDeclaration(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Generators can only be declared at the top level or inside a block")] -#[diagnostic()] -pub struct GeneratorFunctionDeclaration(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("`await` is only allowed within async functions and at the top levels of modules")] -#[diagnostic()] -pub struct AwaitExpression(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("A 'yield' expression is only allowed in a generator body.")] -#[diagnostic()] -pub struct YieldExpression(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Invalid class declaration")] -#[diagnostic(help("Classes can only be declared at top level or inside a block"))] -pub struct ClassDeclaration(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("A rest element must be last in a destructuring pattern")] -#[diagnostic()] -pub struct BindingRestElementLast(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("A rest parameter must be last in a parameter list")] -#[diagnostic()] -pub struct RestParameterLast(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Spread must be last element")] -#[diagnostic()] -pub struct SpreadLastElement(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Unexpected trailing comma after rest element")] -#[diagnostic()] -pub struct BindingRestElementTrailingComma(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Invalid rest element")] -#[diagnostic(help("Expected identifier in rest element"))] -pub struct InvalidBindingRestElement(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("A rest parameter cannot be optional")] -#[diagnostic()] -pub struct ARestParameterCannotBeOptional(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Cannot assign to this expression")] -#[diagnostic()] -pub struct InvalidAssignment(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Optional chaining cannot appear in the callee of new expressions")] -#[diagnostic()] -pub struct NewOptionalChain(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("The left-hand side of a `for...of` statement may not be `async`")] -#[diagnostic()] -pub struct ForLoopAsyncOf(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("await can only be used in conjunction with `for...of` statements")] -#[diagnostic()] -pub struct ForAwait(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Cannot use new with dynamic import")] -#[diagnostic()] -pub struct NewDynamicImport(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Classes can't have an element named '#constructor'")] -#[diagnostic()] -pub struct PrivateNameConstructor(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Classes may not have a static property named prototype")] -#[diagnostic()] -pub struct StaticPrototype(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Constructor can't have get/set modifier")] -#[diagnostic()] -pub struct ConstructorGetterSetter(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Constructor can't be an async method")] -#[diagnostic()] -pub struct ConstructorAsync(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Cannot use `{0}` as an identifier in an async context")] -#[diagnostic()] -pub struct IdentifierAsync(pub &'static str, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Cannot use `{0}` as an identifier in a generator context")] -#[diagnostic()] -pub struct IdentifierGenerator(pub &'static str, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Constructor can't be a generator")] -#[diagnostic()] -pub struct ConstructorGenerator(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Classes can't have a field named 'constructor'")] -#[diagnostic()] -pub struct FieldConstructor(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("An export name cannot include a unicode lone surrogate")] -#[diagnostic()] -pub struct ExportLoneSurrogate(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("A string literal cannot be used as an exported binding without `from`")] -#[diagnostic(help("Did you mean `export {{ {0} as {1} }} from 'some-module'`?"))] -pub struct ExportNamedString(pub String, pub String, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("A reserved word cannot be used as an exported binding without `from`")] -#[diagnostic(help("Did you mean `export {{ {0} as {1} }} from 'some-module'`?"))] -pub struct ExportReservedWord(pub String, pub String, #[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Bad escape sequence in untagged template literal")] -#[diagnostic()] -pub struct TemplateLiteral(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Empty parenthesized expression")] -#[diagnostic()] -pub struct EmptyParenthesizedExpression(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Illegal newline after {0}")] -#[diagnostic()] -pub struct IllegalNewline( - pub &'static str, - #[label("{0} starts here")] pub Span, - #[label("A newline is not expected here")] pub Span, -); - -#[derive(Debug, Error, Diagnostic)] -#[error("Tagged template expressions are not permitted in an optional chain")] -#[diagnostic()] -pub struct OptionalChainTaggedTemplate(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("TS2681: A constructor cannot have a `this` parameter.")] -#[diagnostic()] -pub struct TSConstructorThisParameter(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("TS2730: An arrow function cannot have a `this` parameter.")] -#[diagnostic()] -pub struct TSArrowFunctionThisParameter(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("'super' can only be used with function calls or in property accesses")] -#[diagnostic(help("replace with `super()` or `super.prop` or `super[prop]`"))] -pub struct UnexpectedSuper(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Expected function name")] -#[diagnostic(help("Function name is required in function declaration or named export"))] -pub struct ExpectFunctionName(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Missing catch or finally clause")] -#[diagnostic()] -pub struct ExpectCatchFinally(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("TS1095: A 'set' accessor cannot have a return type annotation")] -#[diagnostic()] -pub struct ASetAccessorCannotHaveAReturnTypeAnnotation(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("TS1108: A 'return' statement can only be used within a function body")] -#[diagnostic()] -pub struct ReturnStatementOnlyInFunctionBody(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("TS18007: JSX expressions may not use the comma operator.")] -#[diagnostic(help("Did you mean to write an array?"))] -pub struct JSXExpressionsMayNotUseTheCommaOperator(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Line terminator not permitted before using declaration.")] -#[diagnostic()] -pub struct LineTerminatorBeforeUsingDeclaration(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Await is not allowed in using declarations.")] -#[diagnostic()] -pub struct AwaitInUsingDeclaration(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Using declarations may not have binding patterns.")] -#[diagnostic()] -pub struct InvalidIdentifierInUsingDeclaration(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("The left-hand side of a for...in statement cannot be an await using declaration.")] -#[diagnostic()] -pub struct AwaitUsingDeclarationNotAllowedInForInStatement(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("The left-hand side of a for...in statement cannot be an using declaration.")] -#[diagnostic()] -pub struct UsingDeclarationNotAllowedInForInStatement(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Using declarations must have an initializer.")] -#[diagnostic()] -pub struct UsingDeclarationsMustBeInitialized(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("TS1089: `static` modifier cannot appear on a constructor declaration.")] -#[diagnostic()] -pub struct StaticConstructor(#[label] pub Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("No line break is allowed before '=>'.")] -#[diagnostic()] -pub struct NoLineBreakIsAllowedBeforeArrow(#[label] pub Span); +#[cold] +pub fn redeclaration(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Identifier `{x0}` has already been declared")).with_labels([ + LabeledSpan::new_with_span(Some(format!("`{x0}` has already been declared here")), span1), + LabeledSpan::new_with_span(Some("It can not be redeclared here".to_string()), span2), + ]) +} + +#[cold] +pub fn overlong_source() -> OxcDiagnostic { + OxcDiagnostic::new("Source length exceeds 4 GiB limit") +} + +#[cold] +pub fn flow(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Flow is not supported").with_labels([span0.into()]) +} + +#[cold] +pub fn unexpected_token(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Unexpected token").with_labels([span0.into()]) +} + +#[cold] +pub fn expect_token(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Expected `{x0}` but found `{x1}`")) + .with_labels([LabeledSpan::new_with_span(Some(format!("`{x0}` expected")), span2)]) +} + +#[cold] +pub fn invalid_escape_sequence(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Invalid escape sequence").with_labels([span0.into()]) +} + +#[cold] +pub fn unicode_escape_sequence(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Invalid Unicode escape sequence").with_labels([span0.into()]) +} + +#[cold] +pub fn invalid_character(x0: char, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Invalid Character `{x0}`")).with_labels([span1.into()]) +} + +#[cold] +pub fn invalid_number_end(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Invalid characters after number").with_labels([span0.into()]) +} + +#[cold] +pub fn unterminated_multi_line_comment(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Unterminated multiline comment").with_labels([span0.into()]) +} + +#[cold] +pub fn unterminated_string(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Unterminated string").with_labels([span0.into()]) +} + +#[cold] +pub fn reg_exp_flag(x0: char, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Unexpected flag {x0} in regular expression literal")) + .with_labels([span1.into()]) +} + +#[cold] +pub fn reg_exp_flag_twice(x0: char, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Flag {x0} is mentioned twice in regular expression literal")) + .with_labels([span1.into()]) +} + +#[cold] +pub fn unexpected_end(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Unexpected end of file").with_labels([span0.into()]) +} + +#[cold] +pub fn unterminated_reg_exp(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Unterminated regular expression").with_labels([span0.into()]) +} + +#[cold] +pub fn invalid_number(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Invalid Number {x0}")).with_labels([span1.into()]) +} + +#[cold] +pub fn escaped_keyword(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Keywords cannot contain escape characters").with_labels([span0.into()]) +} + +#[cold] +pub fn auto_semicolon_insertion(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new( + "Expected a semicolon or an implicit semicolon after a statement, but found none", + ) + .with_help("Try insert a semicolon here") + .with_labels([span0.into()]) +} + +#[cold] +pub fn lineterminator_before_arrow(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Line terminator not permitted before arrow").with_labels([span0.into()]) +} + +#[cold] +pub fn invalid_destrucuring_declaration(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Missing initializer in destructuring declaration") + .with_labels([span0.into()]) +} + +#[cold] +pub fn missinginitializer_in_const(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Missing initializer in const declaration").with_labels([span0.into()]) +} + +#[cold] +pub fn lexical_declaration_single_statement(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Lexical declaration cannot appear in a single-statement context") + .with_help("Wrap this declaration in a block statement") + .with_labels([span0.into()]) +} + +#[cold] +pub fn async_function_declaration(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Async functions can only be declared at the top level or inside a block") + .with_labels([span0.into()]) +} + +#[cold] +pub fn generator_function_declaration(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Generators can only be declared at the top level or inside a block") + .with_labels([span0.into()]) +} + +#[cold] +pub fn await_expression(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new( + "`await` is only allowed within async functions and at the top levels of modules", + ) + .with_labels([span0.into()]) +} + +#[cold] +pub fn yield_expression(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("A 'yield' expression is only allowed in a generator body.") + .with_labels([span0.into()]) +} + +#[cold] +pub fn class_declaration(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Invalid class declaration") + .with_help("Classes can only be declared at top level or inside a block") + .with_labels([span0.into()]) +} + +#[cold] +pub fn binding_rest_element_last(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("A rest element must be last in a destructuring pattern") + .with_labels([span0.into()]) +} + +#[cold] +pub fn rest_parameter_last(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("A rest parameter must be last in a parameter list") + .with_labels([span0.into()]) +} + +#[cold] +pub fn spread_last_element(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Spread must be last element").with_labels([span0.into()]) +} + +#[cold] +pub fn binding_rest_element_trailing_comma(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Unexpected trailing comma after rest element").with_labels([span0.into()]) +} + +#[cold] +pub fn invalid_binding_rest_element(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Invalid rest element") + .with_help("Expected identifier in rest element") + .with_labels([span0.into()]) +} + +#[cold] +pub fn a_rest_parameter_cannot_be_optional(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("A rest parameter cannot be optional").with_labels([span0.into()]) +} + +#[cold] +pub fn invalid_assignment(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Cannot assign to this expression").with_labels([span0.into()]) +} + +#[cold] +pub fn new_optional_chain(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Optional chaining cannot appear in the callee of new expressions") + .with_labels([span0.into()]) +} + +#[cold] +pub fn for_loop_async_of(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("The left-hand side of a `for...of` statement may not be `async`") + .with_labels([span0.into()]) +} + +#[cold] +pub fn for_await(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("await can only be used in conjunction with `for...of` statements") + .with_labels([span0.into()]) +} + +#[cold] +pub fn new_dynamic_import(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Cannot use new with dynamic import").with_labels([span0.into()]) +} + +#[cold] +pub fn private_name_constructor(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Classes can't have an element named '#constructor'") + .with_labels([span0.into()]) +} + +#[cold] +pub fn static_prototype(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Classes may not have a static property named prototype") + .with_labels([span0.into()]) +} + +#[cold] +pub fn constructor_getter_setter(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Constructor can't have get/set modifier").with_labels([span0.into()]) +} + +#[cold] +pub fn constructor_async(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Constructor can't be an async method").with_labels([span0.into()]) +} + +#[cold] +pub fn identifier_async(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Cannot use `{x0}` as an identifier in an async context")) + .with_labels([span1.into()]) +} + +#[cold] +pub fn identifier_generator(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Cannot use `{x0}` as an identifier in a generator context")) + .with_labels([span1.into()]) +} + +#[cold] +pub fn constructor_generator(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Constructor can't be a generator").with_labels([span0.into()]) +} + +#[cold] +pub fn field_constructor(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Classes can't have a field named 'constructor'").with_labels([span0.into()]) +} + +#[cold] +pub fn export_lone_surrogate(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("An export name cannot include a unicode lone surrogate") + .with_labels([span0.into()]) +} + +#[cold] +pub fn export_named_string(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::new("A string literal cannot be used as an exported binding without `from`") + .with_help(format!("Did you mean `export {{ {x0} as {x1} }} from 'some-module'`?")) + .with_labels([span2.into()]) +} + +#[cold] +pub fn export_reserved_word(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::new("A reserved word cannot be used as an exported binding without `from`") + .with_help(format!("Did you mean `export {{ {x0} as {x1} }} from 'some-module'`?")) + .with_labels([span2.into()]) +} + +#[cold] +pub fn template_literal(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Bad escape sequence in untagged template literal") + .with_labels([span0.into()]) +} + +#[cold] +pub fn empty_parenthesized_expression(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Empty parenthesized expression").with_labels([span0.into()]) +} + +#[cold] +pub fn illegal_newline(x0: &str, span1: Span, span2: Span) -> OxcDiagnostic { + OxcDiagnostic::new(format!("Illegal newline after {x0}")).with_labels([ + LabeledSpan::new_with_span(Some(format!("{x0} starts here")), span1), + LabeledSpan::new_with_span(Some("A newline is not expected here".to_string()), span2), + ]) +} + +#[cold] +pub fn optional_chain_tagged_template(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Tagged template expressions are not permitted in an optional chain") + .with_labels([span0.into()]) +} + +#[cold] +pub fn ts_constructor_this_parameter(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("TS2681: A constructor cannot have a `this` parameter.") + .with_labels([span0.into()]) +} + +#[cold] +pub fn ts_arrow_function_this_parameter(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("TS2730: An arrow function cannot have a `this` parameter.") + .with_labels([span0.into()]) +} + +#[cold] +pub fn unexpected_super(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("'super' can only be used with function calls or in property accesses") + .with_help("replace with `super()` or `super.prop` or `super[prop]`") + .with_labels([span0.into()]) +} + +#[cold] +pub fn expect_function_name(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Expected function name") + .with_help("Function name is required in function declaration or named export") + .with_labels([span0.into()]) +} + +#[cold] +pub fn expect_catch_finally(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Missing catch or finally clause").with_labels([span0.into()]) +} + +#[cold] +pub fn a_set_accessor_cannot_have_a_return_type_annotation(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("TS1095: A 'set' accessor cannot have a return type annotation") + .with_labels([span0.into()]) +} + +#[cold] +pub fn return_statement_only_in_function_body(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("TS1108: A 'return' statement can only be used within a function body") + .with_labels([span0.into()]) +} + +#[cold] +pub fn jsx_expressions_may_not_use_the_comma_operator(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("TS18007: JSX expressions may not use the comma operator.") + .with_help("Did you mean to write an array?") + .with_labels([span0.into()]) +} + +#[cold] +pub fn line_terminator_before_using_declaration(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Line terminator not permitted before using declaration.") + .with_labels([span0.into()]) +} + +#[cold] +pub fn await_in_using_declaration(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Await is not allowed in using declarations.").with_labels([span0.into()]) +} + +#[cold] +pub fn invalid_identifier_in_using_declaration(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Using declarations may not have binding patterns.") + .with_labels([span0.into()]) +} + +#[cold] +pub fn await_using_declaration_not_allowed_in_for_in_statement(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new( + "The left-hand side of a for...in statement cannot be an await using declaration.", + ) + .with_labels([span0.into()]) +} + +#[cold] +pub fn using_declaration_not_allowed_in_for_in_statement(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("The left-hand side of a for...in statement cannot be an using declaration.") + .with_labels([span0.into()]) +} + +#[cold] +pub fn using_declarations_must_be_initialized(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("Using declarations must have an initializer.").with_labels([span0.into()]) +} + +#[cold] +pub fn static_constructor(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("TS1089: `static` modifier cannot appear on a constructor declaration.") + .with_labels([span0.into()]) +} + +#[cold] +pub fn no_line_break_is_allowed_before_arrow(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::new("No line break is allowed before '=>'.").with_labels([span0.into()]) +} diff --git a/crates/oxc_parser/src/js/binding.rs b/crates/oxc_parser/src/js/binding.rs index f284409113d22..96ff6ae464a30 100644 --- a/crates/oxc_parser/src/js/binding.rs +++ b/crates/oxc_parser/src/js/binding.rs @@ -68,7 +68,7 @@ impl<'a> ParserImpl<'a> { if self.at(Kind::Question) && self.ts_enabled() { let span = self.cur_token().span(); self.bump_any(); - self.error(diagnostics::ARestParameterCannotBeOptional(span)); + self.error(diagnostics::a_rest_parameter_cannot_be_optional(span)); } // The span is not extended to its type_annotation let type_annotation = self.parse_ts_type_annotation()?; @@ -80,9 +80,11 @@ impl<'a> ParserImpl<'a> { if self.at(Kind::Comma) { if self.peek_at(Kind::RBrack) { - self.error(diagnostics::BindingRestElementTrailingComma(self.cur_token().span())); + self.error(diagnostics::binding_rest_element_trailing_comma( + self.cur_token().span(), + )); } else if !self.ctx.has_ambient() { - self.error(diagnostics::BindingRestElementLast(span)); + self.error(diagnostics::binding_rest_element_last(span)); } } diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 806d799e9204e..5c06a80b84d69 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -22,7 +22,7 @@ impl<'a> ParserImpl<'a> { let decl = self.parse_class_declaration(start_span, Modifiers::empty())?; if stmt_ctx.is_single_statement() { - self.error(diagnostics::ClassDeclaration(Span::new( + self.error(diagnostics::class_declaration(Span::new( decl.span.start, decl.body.span.start, ))); @@ -253,7 +253,7 @@ impl<'a> ParserImpl<'a> { if let PropertyKey::PrivateIdentifier(private_ident) = &key { if private_ident.name == "constructor" { - self.error(diagnostics::PrivateNameConstructor(private_ident.span)); + self.error(diagnostics::private_name_constructor(private_ident.span)); } } @@ -281,17 +281,17 @@ impl<'a> ParserImpl<'a> { )?; if let Some((name, span)) = definition.prop_name() { if r#static && name == "prototype" && !self.ctx.has_ambient() { - self.error(diagnostics::StaticPrototype(span)); + self.error(diagnostics::static_prototype(span)); } if !r#static && name == "constructor" { if kind == MethodDefinitionKind::Get || kind == MethodDefinitionKind::Set { - self.error(diagnostics::ConstructorGetterSetter(span)); + self.error(diagnostics::constructor_getter_setter(span)); } if r#async { - self.error(diagnostics::ConstructorAsync(span)); + self.error(diagnostics::constructor_async(span)); } if generator { - self.error(diagnostics::ConstructorGenerator(span)); + self.error(diagnostics::constructor_generator(span)); } } } @@ -316,10 +316,10 @@ impl<'a> ParserImpl<'a> { )?; if let Some((name, span)) = definition.prop_name() { if name == "constructor" { - self.error(diagnostics::FieldConstructor(span)); + self.error(diagnostics::field_constructor(span)); } if r#static && name == "prototype" && !self.ctx.has_ambient() { - self.error(diagnostics::StaticPrototype(span)); + self.error(diagnostics::static_prototype(span)); } } Ok(definition) @@ -367,11 +367,11 @@ impl<'a> ParserImpl<'a> { if kind == MethodDefinitionKind::Constructor { if let Some(this_param) = &value.this_param { // class Foo { constructor(this: number) {} } - self.error(diagnostics::TSConstructorThisParameter(this_param.span)); + self.error(diagnostics::ts_constructor_this_parameter(this_param.span)); } if r#static { - self.error(diagnostics::StaticConstructor(key.span())); + self.error(diagnostics::static_constructor(key.span())); } } diff --git a/crates/oxc_parser/src/js/declaration.rs b/crates/oxc_parser/src/js/declaration.rs index e2ffb5f60786d..2fb984d4f796f 100644 --- a/crates/oxc_parser/src/js/declaration.rs +++ b/crates/oxc_parser/src/js/declaration.rs @@ -126,10 +126,10 @@ impl<'a> ParserImpl<'a> { // BindingPattern[?Yield, ?Await] Initializer[?In, ?Yield, ?Await] // the grammar forbids `let []`, `let {}` if !matches!(id.kind, BindingPatternKind::BindingIdentifier(_)) { - self.error(diagnostics::InvalidDestrucuringDeclaration(id.span())); + self.error(diagnostics::invalid_destrucuring_declaration(id.span())); } else if kind == VariableDeclarationKind::Const && !self.ctx.has_ambient() { // It is a Syntax Error if Initializer is not present and IsConstantDeclaration of the LexicalDeclaration containing this LexicalBinding is true. - self.error(diagnostics::MissinginitializerInConst(id.span())); + self.error(diagnostics::missinginitializer_in_const(id.span())); } } @@ -151,12 +151,14 @@ impl<'a> ParserImpl<'a> { // `[no LineTerminator here]` if self.cur_token().is_on_new_line { - self.error(diagnostics::LineTerminatorBeforeUsingDeclaration(self.cur_token().span())); + self.error(diagnostics::line_terminator_before_using_declaration( + self.cur_token().span(), + )); } // [lookahead ≠ await] if self.cur_kind() == Kind::Await { - self.error(diagnostics::AwaitInUsingDeclaration(self.cur_token().span())); + self.error(diagnostics::await_in_using_declaration(self.cur_token().span())); self.eat(Kind::Await); } @@ -171,7 +173,7 @@ impl<'a> ParserImpl<'a> { match declaration.id.kind { BindingPatternKind::BindingIdentifier(_) => {} _ => { - self.error(diagnostics::InvalidIdentifierInUsingDeclaration( + self.error(diagnostics::invalid_identifier_in_using_declaration( declaration.id.span(), )); } @@ -179,7 +181,9 @@ impl<'a> ParserImpl<'a> { // Excluding `for` loops, an initializer is required in a UsingDeclaration. if declaration.init.is_none() && !matches!(statement_ctx, StatementContext::For) { - self.error(diagnostics::UsingDeclarationsMustBeInitialized(declaration.id.span())); + self.error(diagnostics::using_declarations_must_be_initialized( + declaration.id.span(), + )); } declarations.push(declaration); diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 7d90b6a261a16..a178f0c2e6c70 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -117,11 +117,11 @@ impl<'a> ParserImpl<'a> { pub(crate) fn check_identifier(&mut self, span: Span, name: &Atom) { // It is a Syntax Error if this production has an [Await] parameter. if self.ctx.has_await() && *name == "await" { - self.error(diagnostics::IdentifierAsync("await", span)); + self.error(diagnostics::identifier_async("await", span)); } // It is a Syntax Error if this production has a [Yield] parameter. if self.ctx.has_yield() && *name == "yield" { - self.error(diagnostics::IdentifierGenerator("yield", span)); + self.error(diagnostics::identifier_generator("yield", span)); } } @@ -214,7 +214,7 @@ impl<'a> ParserImpl<'a> { let paren_span = self.end_span(span); if expressions.is_empty() { - return Err(diagnostics::EmptyParenthesizedExpression(paren_span).into()); + return Err(diagnostics::empty_parenthesized_expression(paren_span)); } // ParenthesizedExpression is from acorn --preserveParens @@ -294,7 +294,7 @@ impl<'a> ParserImpl<'a> { Kind::Float | Kind::PositiveExponential | Kind::NegativeExponential => parse_float(src), _ => unreachable!(), } - .map_err(|err| diagnostics::InvalidNumber(err, token.span()))?; + .map_err(|err| diagnostics::invalid_number(err, token.span()))?; let base = match token.kind { Kind::Decimal => NumberBase::Decimal, Kind::Float => NumberBase::Float, @@ -327,7 +327,7 @@ impl<'a> ParserImpl<'a> { let raw = self.cur_src(); let src = raw.strip_suffix('n').unwrap(); let _value = parse_big_int(src, token.kind) - .map_err(|err| diagnostics::InvalidNumber(err, token.span()))?; + .map_err(|err| diagnostics::invalid_number(err, token.span()))?; self.bump_any(); Ok(self.ast.bigint_literal(self.end_span(span), Atom::from(raw), base)) } @@ -437,7 +437,7 @@ impl<'a> ParserImpl<'a> { // It is a Syntax Error if any source text is matched by this production. // if in_optional_chain { - self.error(diagnostics::OptionalChainTaggedTemplate(quasi.span)); + self.error(diagnostics::optional_chain_tagged_template(quasi.span)); } Ok(self.ast.tagged_template_expression(span, lhs, quasi, type_parameters)) } @@ -470,7 +470,7 @@ impl<'a> ParserImpl<'a> { span.end -= end_offset; if !tagged && cooked.is_none() { - self.error(diagnostics::TemplateLiteral(span)); + self.error(diagnostics::template_literal(span)); } let tail = matches!(cur_kind, Kind::TemplateTail | Kind::NoSubstitutionTemplate); @@ -547,7 +547,7 @@ impl<'a> ParserImpl<'a> { // SuperCall: // super ( Arguments ) if !matches!(self.cur_kind(), Kind::Dot | Kind::LBrack | Kind::LParen) { - self.error(diagnostics::UnexpectedSuper(span)); + self.error(diagnostics::unexpected_super(span)); } self.ast.super_(span) @@ -689,13 +689,13 @@ impl<'a> ParserImpl<'a> { }; if matches!(callee, Expression::ImportExpression(_)) { - self.error(diagnostics::NewDynamicImport(self.end_span(rhs_span))); + self.error(diagnostics::new_dynamic_import(self.end_span(rhs_span))); } let span = self.end_span(span); if optional { - self.error(diagnostics::NewOptionalChain(span)); + self.error(diagnostics::new_optional_chain(span)); } Ok(self.ast.new_expression(span, callee, arguments, type_parameter)) @@ -975,7 +975,7 @@ impl<'a> ParserImpl<'a> { self.bump_any(); // bump async let arrow_token = self.peek_token(); if arrow_token.is_on_new_line { - self.error(diagnostics::NoLineBreakIsAllowedBeforeArrow(arrow_token.span())); + self.error(diagnostics::no_line_break_is_allowed_before_arrow(arrow_token.span())); } self.parse_single_param_function_expression(span, true, false) } else { @@ -1042,7 +1042,7 @@ impl<'a> ParserImpl<'a> { self.bump_any(); let has_await = self.ctx.has_await(); if !has_await { - self.error(diagnostics::AwaitExpression(Span::new(span.start, span.start + 5))); + self.error(diagnostics::await_expression(Span::new(span.start, span.start + 5))); } self.ctx = self.ctx.and_await(true); let argument = self.parse_unary_expression_base(lhs_span)?; diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index 572eb6c753ded..023b91aa6e80e 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -154,12 +154,12 @@ impl<'a> ParserImpl<'a> { let decl = self.parse_function_impl(func_kind)?; if stmt_ctx.is_single_statement() { if decl.r#async { - self.error(diagnostics::AsyncFunctionDeclaration(Span::new( + self.error(diagnostics::async_function_declaration(Span::new( decl.span.start, decl.params.span.end, ))); } else if decl.generator { - self.error(diagnostics::GeneratorFunctionDeclaration(Span::new( + self.error(diagnostics::generator_function_declaration(Span::new( decl.span.start, decl.params.span.end, ))); @@ -304,7 +304,7 @@ impl<'a> ParserImpl<'a> { let has_yield = self.ctx.has_yield(); if !has_yield { - self.error(diagnostics::YieldExpression(Span::new(span.start, span.start + 5))); + self.error(diagnostics::yield_expression(Span::new(span.start, span.start + 5))); } let mut delegate = false; @@ -351,7 +351,7 @@ impl<'a> ParserImpl<'a> { self.ctx = ctx; if kind.is_id_required() && id.is_none() { - self.error(diagnostics::ExpectFunctionName(self.cur_token().span())); + self.error(diagnostics::expect_function_name(self.cur_token().span())); } id @@ -479,7 +479,7 @@ impl<'a> ParserImpl<'a> { if let Some(this_param) = this_param { // const x = (this: number) => {}; - self.error(diagnostics::TSArrowFunctionThisParameter(this_param.span)); + self.error(diagnostics::ts_arrow_function_this_parameter(this_param.span)); } let return_type = self.parse_ts_return_type_annotation()?; @@ -487,7 +487,7 @@ impl<'a> ParserImpl<'a> { self.ctx = self.ctx.and_await(has_await); if self.cur_token().is_on_new_line { - self.error(diagnostics::LineterminatorBeforeArrow(self.cur_token().span())); + self.error(diagnostics::lineterminator_before_arrow(self.cur_token().span())); } self.expect(Kind::Arrow)?; diff --git a/crates/oxc_parser/src/js/grammar.rs b/crates/oxc_parser/src/js/grammar.rs index e14b500795e5b..8129095f2be05 100644 --- a/crates/oxc_parser/src/js/grammar.rs +++ b/crates/oxc_parser/src/js/grammar.rs @@ -43,7 +43,7 @@ impl<'a> CoverGrammar<'a, Expression<'a>> for SimpleAssignmentTarget<'a> { let span = expr.span; match expr.unbox().expression { Expression::ObjectExpression(_) | Expression::ArrayExpression(_) => { - Err(diagnostics::InvalidAssignment(span).into()) + Err(diagnostics::invalid_assignment(span)) } expr => SimpleAssignmentTarget::cover(expr, p), } @@ -59,7 +59,7 @@ impl<'a> CoverGrammar<'a, Expression<'a>> for SimpleAssignmentTarget<'a> { Expression::TSInstantiationExpression(expr) => { Ok(SimpleAssignmentTarget::TSInstantiationExpression(expr)) } - expr => Err(diagnostics::InvalidAssignment(expr.span()).into()), + expr => Err(diagnostics::invalid_assignment(expr.span())), } } } @@ -84,10 +84,10 @@ impl<'a> CoverGrammar<'a, ArrayExpression<'a>> for ArrayAssignmentTarget<'a> { target: AssignmentTarget::cover(elem.unbox().argument, p)?, }); if let Some(span) = expr.trailing_comma { - p.error(diagnostics::BindingRestElementTrailingComma(span)); + p.error(diagnostics::binding_rest_element_trailing_comma(span)); } } else { - return Err(diagnostics::SpreadLastElement(elem.span).into()); + return Err(diagnostics::spread_last_element(elem.span)); } } ArrayExpressionElement::Elision(_) => elements.push(None), @@ -143,7 +143,7 @@ impl<'a> CoverGrammar<'a, ObjectExpression<'a>> for ObjectAssignmentTarget<'a> { target: AssignmentTarget::cover(spread.unbox().argument, p)?, }); } else { - return Err(diagnostics::SpreadLastElement(spread.span).into()); + return Err(diagnostics::spread_last_element(spread.span)); } } } diff --git a/crates/oxc_parser/src/js/list.rs b/crates/oxc_parser/src/js/list.rs index d3c75956ab488..6a09bc9f75d96 100644 --- a/crates/oxc_parser/src/js/list.rs +++ b/crates/oxc_parser/src/js/list.rs @@ -1,11 +1,7 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::{self, Error}, - Result, -}; -use oxc_span::{Atom, CompactStr, GetSpan, Span}; +use oxc_diagnostics::Result; +use oxc_span::{Atom, GetSpan, Span}; use rustc_hash::FxHashMap; use crate::{ @@ -15,15 +11,6 @@ use crate::{ ParserImpl, }; -#[derive(Debug, Error, Diagnostic)] -#[error("Identifier `{0}` has already been declared")] -#[diagnostic()] -struct Redeclaration( - pub CompactStr, - #[label("`{0}` has already been declared here")] pub Span, - #[label("It can not be redeclared here")] pub Span, -); - /// ObjectExpression.properties pub struct ObjectExpressionProperties<'a> { pub elements: Vec<'a, ObjectPropertyKind<'a>>, @@ -83,10 +70,10 @@ impl<'a> SeparatedList<'a> for ObjectPatternProperties<'a> { if p.cur_kind() == Kind::Dot3 { let rest = p.parse_rest_element()?; if !matches!(&rest.argument.kind, BindingPatternKind::BindingIdentifier(_)) { - p.error(diagnostics::InvalidBindingRestElement(rest.argument.span())); + p.error(diagnostics::invalid_binding_rest_element(rest.argument.span())); } if let Some(r) = self.rest.replace(rest) { - p.error(diagnostics::BindingRestElementLast(r.span)); + p.error(diagnostics::binding_rest_element_last(r.span)); } } else { let prop = p.parse_binding_property()?; @@ -160,7 +147,7 @@ impl<'a> SeparatedList<'a> for ArrayPatternList<'a> { Kind::Dot3 => { let rest = p.parse_rest_element()?; if let Some(r) = self.rest.replace(rest) { - p.error(diagnostics::BindingRestElementLast(r.span)); + p.error(diagnostics::binding_rest_element_last(r.span)); } } _ => { @@ -272,7 +259,7 @@ impl<'a> SeparatedList<'a> for FormalParameterList<'a> { Kind::Dot3 => { let rest = p.parse_rest_element()?; if let Some(r) = self.rest.replace(rest) { - p.error(diagnostics::RestParameterLast(r.span)); + p.error(diagnostics::rest_parameter_last(r.span)); } } _ => { @@ -321,7 +308,7 @@ impl<'a> SeparatedList<'a> for AssertEntries<'a> { }; if let Some(old_span) = self.keys.get(&key.as_atom()) { - p.error(Redeclaration(key.as_atom().into_compact_str(), *old_span, key.span())); + p.error(diagnostics::redeclaration(key.as_atom().as_str(), *old_span, key.span())); } else { self.keys.insert(key.as_atom(), key.span()); } diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 53447faaa3bfe..c0b3ff4bb7551 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -244,9 +244,9 @@ impl<'a> ParserImpl<'a> { match &specifier.local { // It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals. ModuleExportName::StringLiteral(literal) => { - self.error(diagnostics::ExportNamedString( - specifier.local.to_string(), - specifier.exported.to_string(), + self.error(diagnostics::export_named_string( + &specifier.local.to_string(), + &specifier.exported.to_string(), literal.span, )); } @@ -258,9 +258,9 @@ impl<'a> ParserImpl<'a> { if match_result.is_reserved_keyword() || match_result.is_future_reserved_keyword() { - self.error(diagnostics::ExportReservedWord( - specifier.local.to_string(), - specifier.exported.to_string(), + self.error(diagnostics::export_reserved_word( + &specifier.local.to_string(), + &specifier.exported.to_string(), id.span, )); } @@ -431,7 +431,7 @@ impl<'a> ParserImpl<'a> { // ModuleExportName : StringLiteral // It is a Syntax Error if IsStringWellFormedUnicode(the SV of StringLiteral) is false. if !literal.is_string_well_formed_unicode() { - self.error(diagnostics::ExportLoneSurrogate(literal.span)); + self.error(diagnostics::export_lone_surrogate(literal.span)); }; Ok(ModuleExportName::StringLiteral(literal)) } diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index 8a4eff9e5f588..17c2f835b40b6 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -188,7 +188,7 @@ impl<'a> ParserImpl<'a> { )?; if stmt_ctx.is_single_statement() && decl.kind.is_lexical() { - self.error(diagnostics::LexicalDeclarationSingleStatement(decl.span)); + self.error(diagnostics::lexical_declaration_single_statement(decl.span)); } Ok(Statement::VariableDeclaration(decl)) @@ -288,13 +288,13 @@ impl<'a> ParserImpl<'a> { // for (a.b in ...), for ([a] in ..), for ({a} in ..) if self.at(Kind::In) || self.at(Kind::Of) { let target = AssignmentTarget::cover(init_expression, self) - .map_err(|_| diagnostics::UnexpectedToken(self.end_span(expr_span)))?; + .map_err(|_| diagnostics::unexpected_token(self.end_span(expr_span)))?; let for_stmt_left = ForStatementLeft::from(target); if !r#await && is_async_of { - self.error(diagnostics::ForLoopAsyncOf(self.end_span(expr_span))); + self.error(diagnostics::for_loop_async_of(self.end_span(expr_span))); } if is_let_of { - self.error(diagnostics::UnexpectedToken(self.end_span(expr_span))); + self.error(diagnostics::unexpected_token(self.end_span(expr_span))); } return self.parse_for_in_or_of_loop(span, r#await, for_stmt_left); } @@ -332,11 +332,11 @@ impl<'a> ParserImpl<'a> { if matches!(self.cur_kind(), Kind::In) { if using_decl.is_await { - self.error(diagnostics::AwaitUsingDeclarationNotAllowedInForInStatement( + self.error(diagnostics::await_using_declaration_not_allowed_in_for_in_statement( using_decl.span, )); } else { - self.error(diagnostics::UsingDeclarationNotAllowedInForInStatement( + self.error(diagnostics::using_declaration_not_allowed_in_for_in_statement( using_decl.span, )); } @@ -371,7 +371,7 @@ impl<'a> ParserImpl<'a> { }; self.expect(Kind::RParen)?; if r#await { - self.error(diagnostics::ForAwait(self.end_span(span))); + self.error(diagnostics::for_await(self.end_span(span))); } let body = self.parse_statement_list_item(StatementContext::For)?; Ok(self.ast.for_statement(self.end_span(span), init, test, update, body)) @@ -393,7 +393,7 @@ impl<'a> ParserImpl<'a> { self.expect(Kind::RParen)?; if r#await && is_for_in { - self.error(diagnostics::ForAwait(self.end_span(span))); + self.error(diagnostics::for_await(self.end_span(span))); } let body = self.parse_statement_list_item(StatementContext::For)?; @@ -438,7 +438,7 @@ impl<'a> ParserImpl<'a> { Some(expr) }; if !self.ctx.has_return() { - self.error(diagnostics::ReturnStatementOnlyInFunctionBody(Span::new( + self.error(diagnostics::return_statement_only_in_function_body(Span::new( span.start, span.start + 6, ))); @@ -497,7 +497,7 @@ impl<'a> ParserImpl<'a> { let span = self.start_span(); self.bump_any(); // advance `throw` if self.cur_token().is_on_new_line { - self.error(diagnostics::IllegalNewline( + self.error(diagnostics::illegal_newline( "throw", self.end_span(span), self.cur_token().span(), @@ -521,7 +521,7 @@ impl<'a> ParserImpl<'a> { if handler.is_none() && finalizer.is_none() { let range = Span::new(block.span.end, block.span.end); - self.error(diagnostics::ExpectCatchFinally(range)); + self.error(diagnostics::expect_catch_finally(range)); } Ok(self.ast.try_statement(self.end_span(span), block, handler, finalizer)) diff --git a/crates/oxc_parser/src/jsx/mod.rs b/crates/oxc_parser/src/jsx/mod.rs index a8a4faa649a65..3c15514e7f66b 100644 --- a/crates/oxc_parser/src/jsx/mod.rs +++ b/crates/oxc_parser/src/jsx/mod.rs @@ -262,7 +262,7 @@ impl<'a> ParserImpl<'a> { self.ctx = Context::default().and_await(ctx.has_await()); let expr = self.parse_expression(); if let Ok(Expression::SequenceExpression(seq)) = &expr { - return Err(diagnostics::JSXExpressionsMayNotUseTheCommaOperator(seq.span).into()); + return Err(diagnostics::jsx_expressions_may_not_use_the_comma_operator(seq.span)); } self.ctx = ctx; expr diff --git a/crates/oxc_parser/src/lexer/byte_handlers.rs b/crates/oxc_parser/src/lexer/byte_handlers.rs index 415f7255ed26c..dad9f010d844c 100644 --- a/crates/oxc_parser/src/lexer/byte_handlers.rs +++ b/crates/oxc_parser/src/lexer/byte_handlers.rs @@ -183,7 +183,7 @@ macro_rules! ascii_identifier_handler { // `\0` `\1` etc ascii_byte_handler!(ERR(lexer) { let c = lexer.consume_char(); - lexer.error(diagnostics::InvalidCharacter(c, lexer.unterminated_range())); + lexer.error(diagnostics::invalid_character(c, lexer.unterminated_range())); Kind::Undetermined }); diff --git a/crates/oxc_parser/src/lexer/comment.rs b/crates/oxc_parser/src/lexer/comment.rs index 8122dfd1e6dab..7fa0368a0f24a 100644 --- a/crates/oxc_parser/src/lexer/comment.rs +++ b/crates/oxc_parser/src/lexer/comment.rs @@ -145,7 +145,7 @@ impl<'a> Lexer<'a> { } }, handle_eof: { - self.error(diagnostics::UnterminatedMultiLineComment(self.unterminated_range())); + self.error(diagnostics::unterminated_multi_line_comment(self.unterminated_range())); return Kind::Eof; }, }; @@ -174,7 +174,7 @@ impl<'a> Lexer<'a> { Kind::Skip } else { self.source.advance_to_end(); - self.error(diagnostics::UnterminatedMultiLineComment(self.unterminated_range())); + self.error(diagnostics::unterminated_multi_line_comment(self.unterminated_range())); Kind::Eof } } diff --git a/crates/oxc_parser/src/lexer/identifier.rs b/crates/oxc_parser/src/lexer/identifier.rs index 312fa92ca06ff..ac1c8324021f1 100644 --- a/crates/oxc_parser/src/lexer/identifier.rs +++ b/crates/oxc_parser/src/lexer/identifier.rs @@ -216,7 +216,7 @@ impl<'a> Lexer<'a> { if start_pos.addr() == self.source.end_addr() { return cold_branch(|| { let start = self.offset(); - self.error(diagnostics::UnexpectedEnd(Span::new(start, start))); + self.error(diagnostics::unexpected_end(Span::new(start, start))); Kind::Undetermined }); } @@ -290,7 +290,7 @@ impl<'a> Lexer<'a> { // No identifier found let start = self.offset(); let c = self.consume_char(); - self.error(diagnostics::InvalidCharacter(c, Span::new(start, self.offset()))); + self.error(diagnostics::invalid_character(c, Span::new(start, self.offset()))); Kind::Undetermined } } diff --git a/crates/oxc_parser/src/lexer/jsx.rs b/crates/oxc_parser/src/lexer/jsx.rs index 1d0221d3818f4..8a460b503a124 100644 --- a/crates/oxc_parser/src/lexer/jsx.rs +++ b/crates/oxc_parser/src/lexer/jsx.rs @@ -43,7 +43,7 @@ impl<'a> Lexer<'a> { Kind::Str } else { self.source.advance_to_end(); - self.error(diagnostics::UnterminatedString(self.unterminated_range())); + self.error(diagnostics::unterminated_string(self.unterminated_range())); Kind::Undetermined } } diff --git a/crates/oxc_parser/src/lexer/mod.rs b/crates/oxc_parser/src/lexer/mod.rs index dd0502228a003..ee493c45b7d4f 100644 --- a/crates/oxc_parser/src/lexer/mod.rs +++ b/crates/oxc_parser/src/lexer/mod.rs @@ -32,7 +32,7 @@ use std::collections::VecDeque; use oxc_allocator::Allocator; use oxc_ast::ast::RegExpFlags; -use oxc_diagnostics::Error; +use oxc_diagnostics::OxcDiagnostic; use oxc_span::{SourceType, Span}; use self::{ @@ -80,7 +80,7 @@ pub struct Lexer<'a> { token: Token, - pub(crate) errors: Vec, + pub(crate) errors: Vec, lookahead: VecDeque>, @@ -223,8 +223,8 @@ impl<'a> Lexer<'a> { } // ---------- Private Methods ---------- // - fn error>(&mut self, error: T) { - self.errors.push(error.into()); + fn error(&mut self, error: OxcDiagnostic) { + self.errors.push(error); } /// Get the length offset from the source, in UTF-8 bytes @@ -282,8 +282,8 @@ impl<'a> Lexer<'a> { fn unexpected_err(&mut self) { let offset = self.current_offset(); match self.peek() { - Some(c) => self.error(diagnostics::InvalidCharacter(c, offset)), - None => self.error(diagnostics::UnexpectedEnd(offset)), + Some(c) => self.error(diagnostics::invalid_character(c, offset)), + None => self.error(diagnostics::unexpected_end(offset)), } } diff --git a/crates/oxc_parser/src/lexer/numeric.rs b/crates/oxc_parser/src/lexer/numeric.rs index 560bab2fdca8c..963bc063e40e6 100644 --- a/crates/oxc_parser/src/lexer/numeric.rs +++ b/crates/oxc_parser/src/lexer/numeric.rs @@ -193,7 +193,7 @@ impl<'a> Lexer<'a> { break; } } - self.error(diagnostics::InvalidNumberEnd(Span::new(offset, self.offset()))); + self.error(diagnostics::invalid_number_end(Span::new(offset, self.offset()))); Kind::Undetermined } } diff --git a/crates/oxc_parser/src/lexer/regex.rs b/crates/oxc_parser/src/lexer/regex.rs index e161ff163a5f5..de3f3867eeb2e 100644 --- a/crates/oxc_parser/src/lexer/regex.rs +++ b/crates/oxc_parser/src/lexer/regex.rs @@ -30,11 +30,11 @@ impl<'a> Lexer<'a> { loop { match self.next_char() { None => { - self.error(diagnostics::UnterminatedRegExp(self.unterminated_range())); + self.error(diagnostics::unterminated_reg_exp(self.unterminated_range())); return (self.offset(), RegExpFlags::empty()); } Some(c) if is_line_terminator(c) => { - self.error(diagnostics::UnterminatedRegExp(self.unterminated_range())); + self.error(diagnostics::unterminated_reg_exp(self.unterminated_range())); #[allow(clippy::cast_possible_truncation)] let pattern_end = self.offset() - c.len_utf8() as u32; return (pattern_end, RegExpFlags::empty()); @@ -61,11 +61,11 @@ impl<'a> Lexer<'a> { while let Some(ch @ ('$' | '_' | 'a'..='z' | 'A'..='Z' | '0'..='9')) = self.peek() { self.consume_char(); let Ok(flag) = RegExpFlags::try_from(ch) else { - self.error(diagnostics::RegExpFlag(ch, self.current_offset())); + self.error(diagnostics::reg_exp_flag(ch, self.current_offset())); continue; }; if flags.contains(flag) { - self.error(diagnostics::RegExpFlagTwice(ch, self.current_offset())); + self.error(diagnostics::reg_exp_flag_twice(ch, self.current_offset())); continue; } flags |= flag; diff --git a/crates/oxc_parser/src/lexer/source.rs b/crates/oxc_parser/src/lexer/source.rs index 8f1396be81c60..8de5635bbdc7c 100644 --- a/crates/oxc_parser/src/lexer/source.rs +++ b/crates/oxc_parser/src/lexer/source.rs @@ -83,7 +83,7 @@ impl<'a> Source<'a> { #[allow(clippy::needless_pass_by_value)] pub(super) fn new(mut source_text: &'a str, _unique: UniquePromise) -> Self { // If source text exceeds size limit, substitute a short source text which will fail to parse. - // `Parser::parse` will convert error to `diagnostics::OverlongSource`. + // `Parser::parse` will convert error to `diagnostics::overlong_source()`. if source_text.len() > MAX_LEN { source_text = "\0"; } diff --git a/crates/oxc_parser/src/lexer/string.rs b/crates/oxc_parser/src/lexer/string.rs index 0af68bf4a8549..23ff04eca42b5 100644 --- a/crates/oxc_parser/src/lexer/string.rs +++ b/crates/oxc_parser/src/lexer/string.rs @@ -42,7 +42,7 @@ macro_rules! handle_string_literal { table: $table, start: after_opening_quote, handle_eof: { - $lexer.error(diagnostics::UnterminatedString($lexer.unterminated_range())); + $lexer.error(diagnostics::unterminated_string($lexer.unterminated_range())); return Kind::Undetermined; }, }; @@ -64,7 +64,7 @@ macro_rules! handle_string_literal { cold_branch(|| { debug_assert!(matches!(next_byte, b'\r' | b'\n')); $lexer.consume_char(); - $lexer.error(diagnostics::UnterminatedString($lexer.unterminated_range())); + $lexer.error(diagnostics::unterminated_string($lexer.unterminated_range())); Kind::Undetermined }) } @@ -94,7 +94,7 @@ macro_rules! handle_string_literal_escape { $lexer.read_string_escape_sequence(&mut str, false, &mut is_valid_escape_sequence); if !is_valid_escape_sequence { let range = Span::new(escape_start_offset, $lexer.offset()); - $lexer.error(diagnostics::InvalidEscapeSequence(range)); + $lexer.error(diagnostics::invalid_escape_sequence(range)); } // Consume bytes until reach end of string, line break, or another escape @@ -127,12 +127,12 @@ macro_rules! handle_string_literal_escape { str.push_str(chunk); continue 'outer; } - _ => { + _ => { // Line break. This is impossible in valid JS, so cold path. return cold_branch(|| { debug_assert!(matches!(b, b'\r' | b'\n')); $lexer.consume_char(); - $lexer.error(diagnostics::UnterminatedString($lexer.unterminated_range())); + $lexer.error(diagnostics::unterminated_string($lexer.unterminated_range())); Kind::Undetermined }); } @@ -140,7 +140,7 @@ macro_rules! handle_string_literal_escape { } // EOF - $lexer.error(diagnostics::UnterminatedString($lexer.unterminated_range())); + $lexer.error(diagnostics::unterminated_string($lexer.unterminated_range())); return Kind::Undetermined; } @@ -148,7 +148,7 @@ macro_rules! handle_string_literal_escape { $lexer.save_string(true, str.into_bump_str()); Kind::Str - }} + }}; } impl<'a> Lexer<'a> { diff --git a/crates/oxc_parser/src/lexer/template.rs b/crates/oxc_parser/src/lexer/template.rs index 462d7b81b1315..86c8867361465 100644 --- a/crates/oxc_parser/src/lexer/template.rs +++ b/crates/oxc_parser/src/lexer/template.rs @@ -74,7 +74,7 @@ impl<'a> Lexer<'a> { } }, handle_eof: { - self.error(diagnostics::UnterminatedString(self.unterminated_range())); + self.error(diagnostics::unterminated_string(self.unterminated_range())); return Kind::Undetermined; }, }; @@ -105,7 +105,7 @@ impl<'a> Lexer<'a> { if pos.addr() == self.source.end_addr() { return cold_branch(|| { self.source.advance_to_end(); - self.error(diagnostics::UnterminatedString(self.unterminated_range())); + self.error(diagnostics::unterminated_string(self.unterminated_range())); Kind::Undetermined }); } @@ -295,7 +295,7 @@ impl<'a> Lexer<'a> { } }, handle_eof: { - self.error(diagnostics::UnterminatedString(self.unterminated_range())); + self.error(diagnostics::unterminated_string(self.unterminated_range())); return Kind::Undetermined; }, }; diff --git a/crates/oxc_parser/src/lexer/unicode.rs b/crates/oxc_parser/src/lexer/unicode.rs index 651adffec0c1a..94ef31fb3a43b 100644 --- a/crates/oxc_parser/src/lexer/unicode.rs +++ b/crates/oxc_parser/src/lexer/unicode.rs @@ -39,7 +39,7 @@ impl<'a> Lexer<'a> { } _ => { self.consume_char(); - self.error(diagnostics::InvalidCharacter(c, self.unterminated_range())); + self.error(diagnostics::invalid_character(c, self.unterminated_range())); Kind::Undetermined } } @@ -56,7 +56,7 @@ impl<'a> Lexer<'a> { let start = self.offset(); if self.next_char() != Some('u') { let range = Span::new(start, self.offset()); - self.error(diagnostics::UnicodeEscapeSequence(range)); + self.error(diagnostics::unicode_escape_sequence(range)); return; } @@ -67,7 +67,7 @@ impl<'a> Lexer<'a> { let Some(value) = value else { let range = Span::new(start, self.offset()); - self.error(diagnostics::UnicodeEscapeSequence(range)); + self.error(diagnostics::unicode_escape_sequence(range)); return; }; @@ -75,7 +75,7 @@ impl<'a> Lexer<'a> { let ch = match value { SurrogatePair::Astral(..) | SurrogatePair::HighLow(..) => { let range = Span::new(start, self.offset()); - self.error(diagnostics::UnicodeEscapeSequence(range)); + self.error(diagnostics::unicode_escape_sequence(range)); return; } SurrogatePair::CodePoint(code_point) => { @@ -83,7 +83,7 @@ impl<'a> Lexer<'a> { ch } else { let range = Span::new(start, self.offset()); - self.error(diagnostics::UnicodeEscapeSequence(range)); + self.error(diagnostics::unicode_escape_sequence(range)); return; } } @@ -93,7 +93,7 @@ impl<'a> Lexer<'a> { if check_identifier_start { is_identifier_start(ch) } else { is_identifier_part(ch) }; if !is_valid { - self.error(diagnostics::InvalidCharacter(ch, self.current_offset())); + self.error(diagnostics::invalid_character(ch, self.current_offset())); return; } @@ -115,7 +115,7 @@ impl<'a> Lexer<'a> { }; let Some(value) = value else { - // error raised within the parser by `diagnostics::TemplateLiteral` + // error raised within the parser by `diagnostics::template_literal` *is_valid_escape_sequence = false; return; }; @@ -220,7 +220,7 @@ impl<'a> Lexer<'a> { ) { match self.next_char() { None => { - self.error(diagnostics::UnterminatedString(self.unterminated_range())); + self.error(diagnostics::unterminated_string(self.unterminated_range())); } Some(c) => match c { // \ LineTerminatorSequence @@ -299,12 +299,12 @@ impl<'a> Lexer<'a> { } '0' if in_template && self.peek().is_some_and(|c| c.is_ascii_digit()) => { self.consume_char(); - // error raised within the parser by `diagnostics::TemplateLiteral` + // error raised within the parser by `diagnostics::template_literal` *is_valid_escape_sequence = false; } // NotEscapeSequence :: DecimalDigit but not 0 '1'..='9' if in_template => { - // error raised within the parser by `diagnostics::TemplateLiteral` + // error raised within the parser by `diagnostics::template_literal` *is_valid_escape_sequence = false; } other => { diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index 7ca123f481d36..b6c156e49f983 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -84,7 +84,7 @@ pub use crate::lexer::Kind; // re-export for codegen use context::{Context, StatementContext}; use oxc_allocator::Allocator; use oxc_ast::{ast::Program, AstBuilder, Trivias}; -use oxc_diagnostics::{Error, Result}; +use oxc_diagnostics::{OxcDiagnostic, Result}; use oxc_span::{ModuleKind, SourceType, Span}; use crate::{ @@ -113,7 +113,7 @@ pub const MAX_LEN: usize = if std::mem::size_of::() >= 8 { /// When `errors.len() > 0`, then program may or may not be empty due to error recovery. pub struct ParserReturn<'a> { pub program: Program<'a>, - pub errors: Vec, + pub errors: Vec, pub trivias: Trivias, pub panicked: bool, } @@ -245,7 +245,7 @@ struct ParserImpl<'a> { /// All syntax errors from parser and lexer /// Note: favor adding to `Diagnostics` instead of raising Err - errors: Vec, + errors: Vec, /// The current parsing token token: Token, @@ -361,21 +361,21 @@ impl<'a> ParserImpl<'a> { /// Check for Flow declaration if the file cannot be parsed. /// The declaration must be [on the first line before any code](https://flow.org/en/docs/usage/#toc-prepare-your-code-for-flow) - fn flow_error(&self) -> Option { + fn flow_error(&self) -> Option { if self.source_type.is_javascript() && (self.source_text.starts_with("// @flow") || self.source_text.starts_with("/* @flow */")) { - return Some(diagnostics::Flow(Span::new(0, 8)).into()); + return Some(diagnostics::flow(Span::new(0, 8))); } None } /// Check if source length exceeds MAX_LEN, if the file cannot be parsed. /// Original parsing error is not real - `Lexer::new` substituted "\0" as the source text. - fn overlong_error(&self) -> Option { + fn overlong_error(&self) -> Option { if self.source_text.len() > MAX_LEN { - return Some(diagnostics::OverlongSource.into()); + return Some(diagnostics::overlong_source()); } None } @@ -383,7 +383,7 @@ impl<'a> ParserImpl<'a> { /// Return error info at current token /// # Panics /// * The lexer did not push a diagnostic when `Kind::Undetermined` is returned - fn unexpected(&mut self) -> Error { + fn unexpected(&mut self) -> OxcDiagnostic { // The lexer should have reported a more meaningful diagnostic // when it is a undetermined kind. if self.cur_kind() == Kind::Undetermined { @@ -391,12 +391,12 @@ impl<'a> ParserImpl<'a> { return error; } } - diagnostics::UnexpectedToken(self.cur_token().span()).into() + diagnostics::unexpected_token(self.cur_token().span()) } /// Push a Syntax Error - fn error>(&mut self, error: T) { - self.errors.push(error.into()); + fn error(&mut self, error: OxcDiagnostic) { + self.errors.push(error); } fn ts_enabled(&self) -> bool { @@ -465,11 +465,7 @@ mod test { fn comments() { let allocator = Allocator::default(); let source_type = SourceType::default().with_typescript(true); - let sources = [ - ("// line comment", CommentKind::SingleLine), - ("/* line comment */", CommentKind::MultiLine), - ("type Foo = ( /* Require properties which are not generated automatically. */ 'bar')", CommentKind::MultiLine), - ]; + let sources = [("// line comment", CommentKind::SingleLine), ("/* line comment */", CommentKind::MultiLine), ("type Foo = ( /* Require properties which are not generated automatically. */ 'bar')", CommentKind::MultiLine)]; for (source, kind) in sources { let ret = Parser::new(&allocator, source, source_type).parse(); let comments = ret.trivias.comments().collect::>(); diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index 31d21e6dbe9f8..cd23044de6f91 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -813,7 +813,7 @@ impl<'a> ParserImpl<'a> { if let Some(this_param) = this_param { // type Foo = new (this: number) => any; - self.error(diagnostics::TSConstructorThisParameter(this_param.span)); + self.error(diagnostics::ts_constructor_this_parameter(this_param.span)); } self.expect(Kind::Arrow)?; @@ -994,7 +994,9 @@ impl<'a> ParserImpl<'a> { self.bump(Kind::Comma); self.bump(Kind::Semicolon); if let Some(return_type) = return_type.as_ref() { - self.error(diagnostics::ASetAccessorCannotHaveAReturnTypeAnnotation(return_type.span)); + self.error(diagnostics::a_set_accessor_cannot_have_a_return_type_annotation( + return_type.span, + )); } Ok(self.ast.ts_method_signature( self.end_span(span), @@ -1066,7 +1068,7 @@ impl<'a> ParserImpl<'a> { if let Some(this_param) = this_param { // interface Foo { new(this: number): Foo } - self.error(diagnostics::TSConstructorThisParameter(this_param.span)); + self.error(diagnostics::ts_constructor_this_parameter(this_param.span)); } let return_type = self.parse_ts_return_type_annotation()?; diff --git a/crates/oxc_span/src/span.rs b/crates/oxc_span/src/span.rs index 736b7a6fc066c..333df42e80db1 100644 --- a/crates/oxc_span/src/span.rs +++ b/crates/oxc_span/src/span.rs @@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher}; -use miette::{SourceOffset, SourceSpan}; +use miette::{LabeledSpan, SourceOffset, SourceSpan}; #[cfg(feature = "serialize")] use serde::Serialize; @@ -59,6 +59,12 @@ impl From for SourceSpan { } } +impl From for LabeledSpan { + fn from(val: Span) -> Self { + LabeledSpan::underline(val) + } +} + /// Get the span for an AST node pub trait GetSpan { fn span(&self) -> Span; diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 3c996d4056c36..5931022f85e45 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -172,7 +172,7 @@ impl Oxc { .parse(); self.comments = self.map_comments(&ret.trivias); - self.save_diagnostics(ret.errors); + self.save_diagnostics(ret.errors.into_iter().map(Error::from).collect::>()); self.ir = format!("{:#?}", ret.program.body).into(); diff --git a/napi/parser/src/lib.rs b/napi/parser/src/lib.rs index 64794a1d1d698..a20d2ad4fa358 100644 --- a/napi/parser/src/lib.rs +++ b/napi/parser/src/lib.rs @@ -7,7 +7,7 @@ use napi_derive::napi; use oxc_allocator::Allocator; pub use oxc_ast::ast::Program; use oxc_ast::CommentKind; -use oxc_diagnostics::miette::NamedSource; +use oxc_diagnostics::{miette::NamedSource, Error}; use oxc_parser::{Parser, ParserReturn}; use oxc_span::SourceType; @@ -103,7 +103,7 @@ pub fn parse_sync(source_text: String, options: Option) -> ParseR let source = Arc::new(NamedSource::new(file_name, source_text.to_string())); ret.errors .into_iter() - .map(|diagnostic| diagnostic.with_source_code(Arc::clone(&source))) + .map(|diagnostic| Error::from(diagnostic).with_source_code(Arc::clone(&source))) .map(|error| format!("{error:?}")) .collect() }; diff --git a/tasks/coverage/src/suite.rs b/tasks/coverage/src/suite.rs index f844bf907339f..50b220ffc9764 100644 --- a/tasks/coverage/src/suite.rs +++ b/tasks/coverage/src/suite.rs @@ -18,7 +18,7 @@ use walkdir::WalkDir; use oxc_allocator::Allocator; use oxc_ast::Trivias; -use oxc_diagnostics::{miette::NamedSource, GraphicalReportHandler, GraphicalTheme}; +use oxc_diagnostics::{miette::NamedSource, Error, GraphicalReportHandler, GraphicalTheme}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::{SourceType, Span}; @@ -334,7 +334,12 @@ pub trait Case: Sized + Sync + Send + UnwindSafe { if let Some(res) = self.check_semantic(&semantic_ret.semantic) { return res; } - let errors = parser_ret.errors.into_iter().chain(semantic_ret.errors).collect::>(); + let errors = parser_ret + .errors + .into_iter() + .map(Error::from) + .chain(semantic_ret.errors) + .collect::>(); let result = if errors.is_empty() { Ok(String::new()) diff --git a/wasm/parser/src/lib.rs b/wasm/parser/src/lib.rs index 531133adf3292..8103d14d816ce 100644 --- a/wasm/parser/src/lib.rs +++ b/wasm/parser/src/lib.rs @@ -82,13 +82,14 @@ pub fn parse_sync( ret.errors .iter() .flat_map(|error| { - let Some(labels) = error.labels() else { return vec![] }; + let Some(labels) = &error.labels else { return vec![] }; labels + .iter() .map(|label| { Diagnostic { start: label.offset(), end: label.offset() + label.len(), - severity: format!("{:?}", error.severity().unwrap_or_default()), + severity: "Error".to_string(), message: format!("{error}"), } .serialize(&serializer)