Skip to content

Commit

Permalink
fix: SWC lexer settings and silent errors (denoland#5752)
Browse files Browse the repository at this point in the history
This commit changes how error occurring in SWC are handled.
Changed lexer settings to properly handle TS decorators.
Changed output of SWC error to annotate with position in file.
  • Loading branch information
bartlomieju committed May 22, 2020
1 parent e191c70 commit 960f9cc
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 26 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Expand Up @@ -2,6 +2,7 @@ cli/compilers/wasm_wrap.js
cli/tests/error_syntax.js
cli/tests/badly_formatted.js
cli/tests/top_level_for_await.js
cli/tests/swc_syntax_error.ts
std/**/testdata
std/**/vendor
std/node_modules
2 changes: 2 additions & 0 deletions cli/module_graph.rs
Expand Up @@ -188,6 +188,7 @@ impl ModuleGraphLoader {
};

let (import_descs, ref_descs) = analyze_dependencies_and_references(
&specifier,
&source_code,
self.analyze_dynamic_imports,
)?;
Expand Down Expand Up @@ -402,6 +403,7 @@ impl ModuleGraphLoader {
}

let (import_descs, ref_descs) = analyze_dependencies_and_references(
&module_specifier.to_string(),
&source_code,
self.analyze_dynamic_imports,
)?;
Expand Down
68 changes: 45 additions & 23 deletions cli/swc_util.rs
Expand Up @@ -29,45 +29,63 @@ use std::sync::RwLock;

#[derive(Clone, Debug)]
pub struct SwcDiagnosticBuffer {
pub diagnostics: Vec<Diagnostic>,
pub diagnostics: Vec<String>,
}

impl Error for SwcDiagnosticBuffer {}

impl fmt::Display for SwcDiagnosticBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = self
.diagnostics
.iter()
.map(|d| d.message())
.collect::<Vec<String>>()
.join(",");
let msg = self.diagnostics.join(",");

f.pad(&msg)
}
}

impl SwcDiagnosticBuffer {
pub fn from_swc_error(
error_buffer: SwcErrorBuffer,
parser: &AstParser,
) -> Self {
let s = error_buffer.0.read().unwrap().clone();

let diagnostics = s
.iter()
.map(|d| {
let mut msg = d.message();

if let Some(span) = d.span.primary_span() {
let location = parser.get_span_location(span);
let filename = match &location.file.name {
FileName::Custom(n) => n,
_ => unreachable!(),
};
msg = format!(
"{} at {}:{}:{}",
msg, filename, location.line, location.col_display
);
}

msg
})
.collect::<Vec<String>>();

Self { diagnostics }
}
}

#[derive(Clone)]
pub struct SwcErrorBuffer(Arc<RwLock<SwcDiagnosticBuffer>>);
pub struct SwcErrorBuffer(Arc<RwLock<Vec<Diagnostic>>>);

impl SwcErrorBuffer {
pub fn default() -> Self {
Self(Arc::new(RwLock::new(SwcDiagnosticBuffer {
diagnostics: vec![],
})))
Self(Arc::new(RwLock::new(vec![])))
}
}

impl Emitter for SwcErrorBuffer {
fn emit(&mut self, db: &DiagnosticBuilder) {
self.0.write().unwrap().diagnostics.push((**db).clone());
}
}

impl From<SwcErrorBuffer> for SwcDiagnosticBuffer {
fn from(buf: SwcErrorBuffer) -> Self {
let s = buf.0.read().unwrap();
s.clone()
self.0.write().unwrap().push((**db).clone());
}
}

Expand Down Expand Up @@ -125,8 +143,10 @@ impl AstParser {
handler: &self.handler,
};

// TODO(bartlomieju): lexer should be configurable by the caller
let mut ts_config = TsConfig::default();
ts_config.dynamic_import = true;
ts_config.decorators = true;
let syntax = Syntax::Typescript(ts_config);

let lexer = Lexer::new(
Expand All @@ -143,8 +163,8 @@ impl AstParser {
parser
.parse_module()
.map_err(move |mut err: DiagnosticBuilder| {
err.cancel();
SwcDiagnosticBuffer::from(buffered_err)
err.emit();
SwcDiagnosticBuffer::from_swc_error(buffered_err, self)
});

callback(parse_result)
Expand Down Expand Up @@ -411,14 +431,15 @@ pub struct TsReferenceDescriptor {
}

pub fn analyze_dependencies_and_references(
file_name: &str,
source_code: &str,
analyze_dynamic_imports: bool,
) -> Result<
(Vec<ImportDescriptor>, Vec<TsReferenceDescriptor>),
SwcDiagnosticBuffer,
> {
let parser = AstParser::new();
parser.parse_module("root.ts", source_code, |parse_result| {
parser.parse_module(file_name, source_code, |parse_result| {
let module = parse_result?;
let mut collector = NewDependencyVisitor {
dependencies: vec![],
Expand Down Expand Up @@ -526,7 +547,8 @@ console.log(qat.qat);
"#;

let (imports, references) =
analyze_dependencies_and_references(source, true).expect("Failed to parse");
analyze_dependencies_and_references("some/file.ts", source, true)
.expect("Failed to parse");

assert_eq!(
imports,
Expand Down
12 changes: 11 additions & 1 deletion cli/tests/integration_tests.rs
Expand Up @@ -1575,7 +1575,17 @@ itest!(ts_type_imports {
args: "run --reload ts_type_imports.ts",
output: "ts_type_imports.ts.out",
exit_code: 1,
http_server: true,
});

itest!(ts_decorators {
args: "run --reload -c tsconfig.decorators.json ts_decorators.ts",
output: "ts_decorators.ts.out",
});

itest!(swc_syntax_error {
args: "run --reload swc_syntax_error.ts",
output: "swc_syntax_error.ts.out",
exit_code: 1,
});

itest!(types {
Expand Down
3 changes: 3 additions & 0 deletions cli/tests/swc_syntax_error.ts
@@ -0,0 +1,3 @@
for await (const req of s) {
let something:
}
1 change: 1 addition & 0 deletions cli/tests/swc_syntax_error.ts.out
@@ -0,0 +1 @@
error: Unexpected token Some(RBrace) at [WILDCARD]syntax_error.ts:3:0
14 changes: 14 additions & 0 deletions cli/tests/ts_decorators.ts
@@ -0,0 +1,14 @@
/* eslint-disable */

function Decorate() {
return function (constructor: any): any {
return class extends constructor {
protected someField: string = "asdf";
};
};
}

@Decorate()
class SomeClass {}

console.log(new SomeClass());
2 changes: 2 additions & 0 deletions cli/tests/ts_decorators.ts.out
@@ -0,0 +1,2 @@
Compile [WILDCARD]
SomeClass { someField: "asdf" }
5 changes: 5 additions & 0 deletions cli/tests/tsconfig.decorators.json
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"experimentalDecorators": true
}
}
1 change: 0 additions & 1 deletion cli/tsc.rs
Expand Up @@ -466,7 +466,6 @@ impl TsCompiler {
let module_graph = module_graph_loader.get_graph();
let module_graph_json =
serde_json::to_value(module_graph).expect("Failed to serialize data");

let target = match target {
TargetLib::Main => "main",
TargetLib::Worker => "worker",
Expand Down
3 changes: 2 additions & 1 deletion tools/lint.py
Expand Up @@ -61,7 +61,8 @@ def eslint():
"eslint")
# Find all *directories* in the main repo that contain .ts/.js files.
source_files = get_sources(root_path, [
"*.js", "*.ts", ":!:std/**/testdata/*", ":!:std/**/node_modules/*",
"*.js", "*.ts", ":!:cli/tests/swc_syntax_error.ts",
":!:std/**/testdata/*", ":!:std/**/node_modules/*",
":!:cli/compilers/wasm_wrap.js", ":!:cli/tests/error_syntax.js"
])
if source_files:
Expand Down

0 comments on commit 960f9cc

Please sign in to comment.