Skip to content

Commit

Permalink
chore: 🤖 update pure annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
IWANABETHATGUY committed May 23, 2024
1 parent d1a4086 commit befb6e4
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 17 deletions.
3 changes: 2 additions & 1 deletion crates/rolldown/src/ast_scanner/impl_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ impl<'me, 'ast> Visit<'ast> for AstScanner<'me> {
for (idx, stmt) in program.body.iter().enumerate() {
self.current_stmt_info.stmt_idx = Some(idx);
self.current_stmt_info.side_effect =
SideEffectDetector::new(self.scope).detect_side_effect_of_stmt(stmt);
SideEffectDetector::new(self.scope, &self.travias, self.source)
.detect_side_effect_of_stmt(stmt);

if cfg!(debug_assertions) {
let mut codegen = Codegen::<false>::new(
Expand Down
5 changes: 4 additions & 1 deletion crates/rolldown/src/ast_scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use oxc::{
ExportAllDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, IdentifierReference,
ImportDeclaration, ModuleDeclaration, Program,
},
Visit,
Trivias, Visit,
},
semantic::SymbolId,
span::{Atom, Span},
Expand Down Expand Up @@ -46,6 +46,7 @@ pub struct AstScanner<'me> {
module_type: ModuleType,
file_path: &'me ResourceId,
scope: &'me AstScope,
travias: &'me Trivias,
symbol_table: &'me mut AstSymbols,
current_stmt_info: StmtInfo,
result: ScanResult,
Expand All @@ -65,6 +66,7 @@ impl<'me> AstScanner<'me> {
module_type: ModuleType,
source: &'me Arc<str>,
file_path: &'me ResourceId,
travias: &'me Trivias,
) -> Self {
// This is used for converting "export default foo;" => "var default_symbol = foo;"
let symbol_id_for_default_export_ref =
Expand Down Expand Up @@ -106,6 +108,7 @@ impl<'me> AstScanner<'me> {
used_module_ref: false,
source,
file_path,
travias,
}
}

Expand Down
54 changes: 46 additions & 8 deletions crates/rolldown/src/ast_scanner/side_effect_detector.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::sync::Arc;

use once_cell::sync::Lazy;
use oxc::ast::ast::{
BindingPatternKind, Expression, IdentifierReference, MemberExpression, PropertyKey,
};
use oxc::ast::{CommentKind, Trivias};
use oxc::span::Span;
use rolldown_common::AstScope;
use rustc_hash::FxHashSet;

Expand All @@ -19,6 +23,9 @@ static SIDE_EFFECT_FREE_MEMBER_EXPR_2: Lazy<FxHashSet<(&'static str, &'static st
.collect()
});

static PURE_COMMENTS: Lazy<regex::Regex> =
Lazy::new(|| regex::Regex::new("^\\s*(#|@)__PURE__\\s*$").expect("Should create the regex"));

static SIDE_EFFECT_FREE_MEMBER_EXPR_3: Lazy<FxHashSet<(&'static str, &'static str, &'static str)>> =
Lazy::new(|| {
[("Object", "prototype", "hasOwnProperty"), ("Object", "prototype", "constructor")]
Expand All @@ -29,11 +36,13 @@ static SIDE_EFFECT_FREE_MEMBER_EXPR_3: Lazy<FxHashSet<(&'static str, &'static st
/// Detect if a statement "may" have side effect.
pub struct SideEffectDetector<'a> {
pub scope: &'a AstScope,
pub travias: &'a Trivias,
pub source: &'a Arc<str>,
}

impl<'a> SideEffectDetector<'a> {
pub fn new(scope: &'a AstScope) -> Self {
Self { scope }
pub fn new(scope: &'a AstScope, travias: &'a Trivias, source: &'a Arc<str>) -> Self {
Self { scope, travias, source }
}

fn is_unresolved_reference(&self, ident_ref: &IdentifierReference) -> bool {
Expand Down Expand Up @@ -185,7 +194,6 @@ impl<'a> SideEffectDetector<'a> {
| Expression::ArrayExpression(_)
| Expression::AssignmentExpression(_)
| Expression::AwaitExpression(_)
| Expression::CallExpression(_)
| Expression::ChainExpression(_)
| Expression::ImportExpression(_)
| Expression::NewExpression(_)
Expand All @@ -194,6 +202,37 @@ impl<'a> SideEffectDetector<'a> {
| Expression::YieldExpression(_)
| Expression::JSXElement(_)
| Expression::JSXFragment(_) => true,
Expression::CallExpression(expr) => {
let mut leading_comment: Option<(CommentKind, Span)> = None;
self.travias.comments().for_each(|(kind, span)| {
if span.end < expr.span.start && matches!(kind, CommentKind::MultiLine) {
leading_comment = Some((kind, span));
}
});
let is_pure_comment = if let Some(leading_comment) = leading_comment {
// dbg!(&leading_comment);
let res = &self.source[leading_comment.1.end as usize + 2..expr.span.start as usize];
let is_between_str_all_whitespace = res.chars().all(|ch| ch == ' ');
if is_between_str_all_whitespace {
let comment_content =
&self.source[leading_comment.1.start as usize..leading_comment.1.end as usize];
PURE_COMMENTS.is_match(comment_content)
} else {
false

Check warning on line 221 in crates/rolldown/src/ast_scanner/side_effect_detector.rs

View check run for this annotation

Codecov / codecov/patch

crates/rolldown/src/ast_scanner/side_effect_detector.rs#L221

Added line #L221 was not covered by tests
}
} else {
false
};
if is_pure_comment {
expr.arguments.iter().any(|arg| match arg {
oxc::ast::ast::Argument::SpreadElement(_) => true,

Check warning on line 228 in crates/rolldown/src/ast_scanner/side_effect_detector.rs

View check run for this annotation

Codecov / codecov/patch

crates/rolldown/src/ast_scanner/side_effect_detector.rs#L228

Added line #L228 was not covered by tests
// TODO: implementa this
_ => false,

Check warning on line 230 in crates/rolldown/src/ast_scanner/side_effect_detector.rs

View check run for this annotation

Codecov / codecov/patch

crates/rolldown/src/ast_scanner/side_effect_detector.rs#L230

Added line #L230 was not covered by tests
})
} else {
true
}
}
}
}

Expand Down Expand Up @@ -356,11 +395,10 @@ mod test {
)
};

let has_side_effect = ast
.program()
.body
.iter()
.any(|stmt| SideEffectDetector::new(&ast_scope).detect_side_effect_of_stmt(stmt));
let has_side_effect = ast.program().body.iter().any(|stmt| {
SideEffectDetector::new(&ast_scope, ast.trivias(), ast.source())
.detect_side_effect_of_stmt(stmt)
});

has_side_effect
}
Expand Down
2 changes: 2 additions & 0 deletions crates/rolldown/src/module_loader/normal_module_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ impl NormalModuleTask {
let mut symbol_for_module = AstSymbols::from_symbol_table(symbol_table);
let file_path = Arc::<str>::clone(&self.resolved_path.path).into();
let repr_name = ResourceId::representative_name(&file_path);
let trivias = program.with_mut(|fields| std::mem::take(fields.trivias));
let scanner = AstScanner::new(
self.module_id,
&ast_scope,
Expand All @@ -201,6 +202,7 @@ impl NormalModuleTask {
self.module_type,
source,
&file_path,
&trivias,
);
let namespace_symbol = scanner.namespace_ref;
program.hoist_import_export_from_stmts();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl RuntimeNormalModuleTask {
);
let mut symbol_for_module = AstSymbols::from_symbol_table(symbol_table);
let facade_path = ResourceId::new("runtime");
let trivias = ast.with_mut(|fields| std::mem::take(fields.trivias));
let scanner = AstScanner::new(
self.module_id,
&ast_scope,
Expand All @@ -120,6 +121,7 @@ impl RuntimeNormalModuleTask {
ModuleType::EsmMjs,
source,
&facade_path,
&trivias,
);
let namespace_symbol = scanner.namespace_ref;
ast.hoist_import_export_from_stmts();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"config": {
"treeshake": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a = 100;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
source: crates/rolldown/tests/common/case.rs
expression: content
input_file: crates/rolldown/tests/fixtures/tree_shaking/pure_annotation
---
# Assets

## main.mjs

```js
// a.js
const a = 100;
// main.js
console.log(`a: `, a);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const b = 100;

function test() {}

export const c = /* #__PURE__ */ test();
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { a } from "./a";
import { b } from "./b";

console.log(`a: `, a);
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,10 @@ expression: "snapshot_outputs.join(\"\\n\")"

- main-!~{000}~.mjs => main-Ep15x52X.mjs

# tests/fixtures/tree_shaking/pure_annotation

- main-!~{000}~.mjs => main-Km8M_iGP.mjs

# tests/fixtures/tree_shaking/unused_import_external

- main-!~{000}~.mjs => main-Ix9nDVBV.mjs
Expand Down
2 changes: 1 addition & 1 deletion crates/rolldown_oxc_utils/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl OxcCompiler {
let allocator = oxc::allocator::Allocator::default();
let inner = Inner::new((source.into(), allocator), |(source, allocator)| {
let parser = Parser::new(allocator, source, ty);
parser.parse().program
parser.parse()
});
OxcAst { inner, source_type: ty }
}
Expand Down
4 changes: 2 additions & 2 deletions crates/rolldown_oxc_utils/src/oxc_ast/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::WithFieldsMut;

impl OxcAst {
pub fn is_body_empty(&self) -> bool {
self.inner.with_dependent(|_, program| program.body.is_empty())
self.inner.with_dependent(|_, praser_return| praser_return.program.body.is_empty())
}

pub fn make_semantic<'ast>(
Expand All @@ -24,7 +24,7 @@ impl OxcAst {

pub fn make_symbol_table_and_scope_tree(&self) -> (SymbolTable, ScopeTree) {
self.inner.with_dependent(|dep, program| {
let semantic = Self::make_semantic(&dep.0, program, self.source_type);
let semantic = Self::make_semantic(&dep.0, &program.program, self.source_type);
semantic.into_symbol_table_and_scope_tree()
})
}
Expand Down
20 changes: 16 additions & 4 deletions crates/rolldown_oxc_utils/src/oxc_ast/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{fmt::Debug, sync::Arc};

use crate::OxcCompiler;
use oxc::ast::Trivias;
use oxc::parser::ParserReturn;
use oxc::{allocator::Allocator, ast::ast::Program, span::SourceType};

use self_cell::self_cell;
Expand All @@ -12,7 +14,7 @@ self_cell!(
owner: (Arc<str>, Allocator),

#[covariant]
dependent: Program,
dependent: ParserReturn,
}
);

Expand All @@ -32,7 +34,11 @@ impl OxcAst {
}

pub fn program(&self) -> &Program {
self.inner.borrow_dependent()
&self.inner.borrow_dependent().program
}

pub fn trivias(&self) -> &Trivias {
&self.inner.borrow_dependent().trivias
}

/// Visit all fields including `&mut Program` within a closure.
Expand All @@ -51,8 +57,13 @@ impl OxcAst {
&'outer mut self,
func: impl for<'inner> ::core::ops::FnOnce(WithFieldsMut<'outer, 'inner>) -> Ret,
) -> Ret {
self.inner.with_dependent_mut::<'outer, Ret>(|owner, program| {
func(WithFieldsMut { source: &owner.0, allocator: &owner.1, program })
self.inner.with_dependent_mut::<'outer, Ret>(|owner, parse_return| {
func(WithFieldsMut {
source: &owner.0,
allocator: &owner.1,
program: &mut parse_return.program,
trivias: &mut parse_return.trivias,
})
})
}
}
Expand All @@ -61,6 +72,7 @@ pub struct WithFieldsMut<'outer, 'inner> {
pub source: &'inner Arc<str>,
pub allocator: &'inner Allocator,
pub program: &'outer mut Program<'inner>,
pub trivias: &'outer mut Trivias,
}

impl Debug for OxcAst {
Expand Down

0 comments on commit befb6e4

Please sign in to comment.