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%)  <alloc::boxed::Box<T,A> 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<T,A>::grow_amortized
   12002 (1.4%, 11.6%)    706 (2.1%,  7.9%)  miette::eyreish::ptr::Own<T>::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%)  <miette::eyreish::error::ErrorImpl<E> as core::error::Error>::source
    8822 (1.1%, 15.9%)    353 (1.1%, 10.3%)  miette::eyreish::error::<impl miette::eyreish::Report>::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%)  <miette::eyreish::error::ErrorImpl<E> as core::fmt::Display>::fmt
    7413 (0.9%, 19.7%)    353 (1.1%, 14.6%)  miette::eyreish::ptr::Own<T>::new
    6669 (0.8%, 20.5%)     39 (0.1%, 14.7%)  alloc::raw_vec::RawVec<T,A>::try_allocate_in
    6173 (0.7%, 21.2%)    353 (1.1%, 15.7%)  miette::eyreish::error::<impl miette::eyreish::Report>::from_std
    6027 (0.7%, 21.9%)     70 (0.2%, 16.0%)  <alloc::vec::Vec<T> as alloc::vec::spec_from_iter_nested::SpecFromIterNested<T,I>>::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%)  <miette::eyreish::error::ErrorImpl<E> 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.
This commit is contained in:
Boshen 2024-05-11 04:56:22 +00:00
parent 46c02aee61
commit 2064ae9e0a
34 changed files with 682 additions and 509 deletions

View file

@ -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<T> = std::result::Result<T, Error>;
pub type Result<T> = std::result::Result<T, OxcDiagnostic>;
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<OxcDiagnosticInner>,
}
impl Deref for OxcDiagnostic {
type Target = Box<OxcDiagnosticInner>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Debug, Clone)]
pub struct OxcDiagnosticInner {
pub message: String,
pub labels: Option<Vec<LabeledSpan>>,
pub help: Option<String>,
}
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<Box<dyn Display + 'a>> {
self.help.as_ref().map(Box::new).map(|c| c as Box<dyn Display>)
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
self.labels
.as_ref()
.map(|ls| ls.iter().cloned())
.map(Box::new)
.map(|b| b as Box<dyn Iterator<Item = LabeledSpan>>)
}
}
impl OxcDiagnostic {
#[must_use]
pub fn new<T: Into<String>>(message: T) -> Self {
Self {
inner: Box::new(OxcDiagnosticInner {
message: message.into(),
labels: None,
help: None,
}),
}
}
#[must_use]
pub fn with_help<T: Into<String>>(mut self, help: T) -> Self {
self.inner.help = Some(help.into());
self
}
#[must_use]
pub fn with_label<T: Into<LabeledSpan>>(mut self, label: T) -> Self {
self.inner.labels = Some(vec![label.into()]);
self
}
#[must_use]
pub fn with_labels<T: IntoIterator<Item = LabeledSpan>>(mut self, labels: T) -> Self {
self.inner.labels = Some(labels.into_iter().collect());
self
}
#[must_use]
pub fn and_label<T: Into<LabeledSpan>>(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<T: IntoIterator<Item = LabeledSpan>>(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<T: SourceCode + Send + Sync + 'static>(self, code: T) -> Error {
Error::from(self).with_source_code(code)
}
}

View file

@ -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));
};

View file

@ -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<oxc_diagnostics::Error> = vec![];
let mut errors: Vec<OxcDiagnostic> = 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<oxc_diagnostics::Error>) {
fn print_errors(source_text: &str, errors: Vec<OxcDiagnostic>) {
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<oxc_diagnostics::Error>) {
// 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),
])
}

View file

@ -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);

View file

@ -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(())
}

View file

@ -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()])
}

View file

@ -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));
}
}

View file

@ -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()));
}
}

View file

@ -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);

View file

@ -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.
// <https://tc39.es/ecma262/#sec-left-hand-side-expressions-static-semantics-early-errors>
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)?;

View file

@ -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)?;

View file

@ -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));
}
}
}

View file

@ -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());
}

View file

@ -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))
}

View file

@ -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))

View file

@ -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

View file

@ -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
});

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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<Error>,
pub(crate) errors: Vec<OxcDiagnostic>,
lookahead: VecDeque<Lookahead<'a>>,
@ -223,8 +223,8 @@ impl<'a> Lexer<'a> {
}
// ---------- Private Methods ---------- //
fn error<T: Into<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)),
}
}

View file

@ -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
}
}

View file

@ -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;

View file

@ -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";
}

View file

@ -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> {

View file

@ -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;
},
};

View file

@ -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 => {

View file

@ -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::<usize>() >= 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<Error>,
pub errors: Vec<OxcDiagnostic>,
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<Error>,
errors: Vec<OxcDiagnostic>,
/// 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<Error> {
fn flow_error(&self) -> Option<OxcDiagnostic> {
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<Error> {
fn overlong_error(&self) -> Option<OxcDiagnostic> {
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<T: Into<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::<Vec<_>>();

View file

@ -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()?;

View file

@ -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<Span> for SourceSpan {
}
}
impl From<Span> for LabeledSpan {
fn from(val: Span) -> Self {
LabeledSpan::underline(val)
}
}
/// Get the span for an AST node
pub trait GetSpan {
fn span(&self) -> Span;

View file

@ -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::<Vec<_>>());
self.ir = format!("{:#?}", ret.program.body).into();

View file

@ -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<ParserOptions>) -> 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()
};

View file

@ -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::<Vec<_>>();
let errors = parser_ret
.errors
.into_iter()
.map(Error::from)
.chain(semantic_ret.errors)
.collect::<Vec<_>>();
let result = if errors.is_empty() {
Ok(String::new())

View file

@ -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)