diff --git a/crates/oxc_diagnostics/src/lib.rs b/crates/oxc_diagnostics/src/lib.rs index be5dbb760864..aa86aeaaa8b1 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 d187a41c332f..12afc3eae5ad 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 d24565865c4b..417137e2cb16 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 44e537b99a5b..02cf94bcb535 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 35f910e57209..af18ec6a5c97 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 9e09e2450ec0..6b44a416bf50 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 f284409113d2..96ff6ae464a3 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 806d799e9204..5c06a80b84d6 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 e2ffb5f60786..2fb984d4f796 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 7d90b6a261a1..a178f0c2e6c7 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 572eb6c753de..023b91aa6e80 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 e14b500795e5..8129095f2be0 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 d3c75956ab48..6a09bc9f75d9 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 53447faaa3bf..c0b3ff4bb755 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 8a4eff9e5f58..17c2f835b40b 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 a8a4faa649a6..3c15514e7f66 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 415f7255ed26..dad9f010d844 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 8122dfd1e6da..7fa0368a0f24 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 312fa92ca06f..ac1c8324021f 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 1d0221d3818f..8a460b503a12 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 dd0502228a00..ee493c45b7d4 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 560bab2fdca8..963bc063e40e 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 e161ff163a5f..de3f3867eeb2 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 8f1396be81c6..8de5635bbdc7 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 0af68bf4a854..23ff04eca42b 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 462d7b81b131..86c886736146 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 651adffec0c1..94ef31fb3a43 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 7ca123f481d3..b6c156e49f98 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 31d21e6dbe9f..cd23044de6f9 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 736b7a6fc066..333df42e80db 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 3c996d4056c3..5931022f85e4 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 64794a1d1d69..a20d2ad4fa35 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 f844bf907339..50b220ffc976 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 531133adf329..8103d14d816c 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)