From 365d9ba2524ab323554bf434b5536cf2b1cc24ff Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY Date: Tue, 9 Jul 2024 00:22:28 +0800 Subject: [PATCH] feat(oxc_codegen): generate annotation comments before `CallExpression` and `NewExpression` (#4119) 1. test case copy from `vue/core`, here are all usages of `#__PURE__` in `vue/core` https://gist.github.com/IWANABETHATGUY/c7911ecd98467a2969b2a994a34d32bc#file-pure_annotation_in_vue_repo-sh 2. Also took a look in other codebase, https://github.com/search?q=%23__PURE__&type=code, most of the usage of `#__PURE__` attached as leading comment before `CallExpression` and `NewExpression` --- crates/oxc_codegen/src/gen.rs | 20 +++------------- crates/oxc_codegen/src/gen_comment.rs | 33 +++++++++++++++++++++++++++ crates/oxc_codegen/src/lib.rs | 5 ++-- crates/oxc_codegen/tests/mod.rs | 33 +++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 crates/oxc_codegen/src/gen_comment.rs diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index d86e0c1231cb5..dab9cd8944e15 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -12,6 +12,7 @@ use oxc_syntax::{ use crate::{ annotation_comment::{gen_comment, get_leading_annotate_comment}, + gen_comment::GenComment, Codegen, Context, Operator, }; @@ -32,11 +33,6 @@ where } } -/// the [GenComment] trait only generate annotate comments like `/* @__PURE__ */` and `/* @__NO_SIDE_EFFECTS__ */`. -pub trait GenComment { - fn gen_comment(&self, _p: &mut Codegen<{ MINIFY }>, _ctx: Context) {} -} - impl<'a, const MINIFY: bool, T> GenExpr for Box<'a, T> where T: GenExpr, @@ -1075,18 +1071,6 @@ impl<'a, const MINIFY: bool> GenExpr for TSAsExpression<'a> { } } -impl GenComment for ArrowFunctionExpression<'_> { - fn gen_comment(&self, codegen: &mut Codegen<{ MINIFY }>, _ctx: Context) { - gen_comment(self.span.start, codegen); - } -} - -impl GenComment for Function<'_> { - fn gen_comment(&self, codegen: &mut Codegen<{ MINIFY }>, _ctx: Context) { - gen_comment(self.span.start, codegen); - } -} - impl<'a, const MINIFY: bool> GenExpr for ParenthesizedExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.print_str(b"("); @@ -1436,6 +1420,7 @@ impl<'a, const MINIFY: bool> GenExpr for CallExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { let wrap = precedence > self.precedence() || ctx.has_forbid_call(); let ctx = ctx.and_forbid_call(false); + self.gen_comment(p, ctx); p.wrap(wrap, |p| { p.add_source_mapping(self.span.start); self.callee.gen_expr(p, self.precedence(), ctx); @@ -2065,6 +2050,7 @@ impl<'a, const MINIFY: bool> GenExpr for ChainExpression<'a> { impl<'a, const MINIFY: bool> GenExpr for NewExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { + self.gen_comment(p, ctx); p.wrap(precedence > self.precedence(), |p| { p.add_source_mapping(self.span.start); p.print_str(b"new "); diff --git a/crates/oxc_codegen/src/gen_comment.rs b/crates/oxc_codegen/src/gen_comment.rs new file mode 100644 index 0000000000000..720933a46b22c --- /dev/null +++ b/crates/oxc_codegen/src/gen_comment.rs @@ -0,0 +1,33 @@ +use oxc_ast::ast::{ArrowFunctionExpression, CallExpression, Function, NewExpression}; + +use crate::annotation_comment::gen_comment; +use crate::{Codegen, Context}; + +/// the [GenComment] trait only generate annotate comments like `/* @__PURE__ */` and `/* @__NO_SIDE_EFFECTS__ */`. +pub trait GenComment { + fn gen_comment(&self, _p: &mut Codegen<{ MINIFY }>, _ctx: Context) {} +} + +impl GenComment for ArrowFunctionExpression<'_> { + fn gen_comment(&self, codegen: &mut Codegen<{ MINIFY }>, _ctx: Context) { + gen_comment(self.span.start, codegen); + } +} + +impl GenComment for Function<'_> { + fn gen_comment(&self, codegen: &mut Codegen<{ MINIFY }>, _ctx: Context) { + gen_comment(self.span.start, codegen); + } +} + +impl GenComment for CallExpression<'_> { + fn gen_comment(&self, codegen: &mut Codegen<{ MINIFY }>, _ctx: Context) { + gen_comment(self.span.start, codegen); + } +} + +impl GenComment for NewExpression<'_> { + fn gen_comment(&self, codegen: &mut Codegen<{ MINIFY }>, _ctx: Context) { + gen_comment(self.span.start, codegen); + } +} diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index e774cdc4b48b9..c7e4e5df3bc30 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -6,6 +6,7 @@ mod annotation_comment; mod context; mod gen; +mod gen_comment; mod operator; mod sourcemap_builder; @@ -520,9 +521,9 @@ pub(crate) type MoveCommentMap = FxHashMap; // Comment related impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { - /// This method to avoid rustc borrow checker issue. + /// Avoid issue related to rustc borrow checker . /// Since if you want to print a range of source code, you need to borrow the source code - /// immutable first, and call the [Self::print_str] which is a mutable borrow. + /// as immutable first, and call the [Self::print_str] which is a mutable borrow. fn print_range_of_source_code(&mut self, range: Range) { self.code.extend_from_slice(self.source_text[range].as_bytes()); } diff --git a/crates/oxc_codegen/tests/mod.rs b/crates/oxc_codegen/tests/mod.rs index 8d327196f55a5..e1353c2ce3d19 100644 --- a/crates/oxc_codegen/tests/mod.rs +++ b/crates/oxc_codegen/tests/mod.rs @@ -372,6 +372,39 @@ let l2 = () => {}, l3 = () => {}; const c2 = /* #__NO_SIDE_EFFECTS__ */ () => {}, c3 = () => {}; ", ); + + test_comment_helper( + r" +isFunction(options) + ? // #8326: extend call and options.name access are considered side-effects + // by Rollup, so we have to wrap it in a pure-annotated IIFE. + /*#__PURE__*/ (() => + extend({ name: options.name }, extraOptions, { setup: options }))() + : options + ", + r"isFunction(options) ? /*#__PURE__*/ (() => extend({name: options.name}, extraOptions, {setup: options}))() : options; +", + ); + + test_comment_helper( + r" +const obj = { + props: /*#__PURE__*/ extend({}, TransitionPropsValidators, { + tag: String, + moveClass: String, + }), +}; +const p = /*#__PURE__*/ Promise.resolve(); +", + "const obj = {props: /*#__PURE__*/ extend({}, TransitionPropsValidators, {\n\ttag: String,\n\tmoveClass: String\n})};\nconst p = /*#__PURE__*/ Promise.resolve();\n", + ); + + test_comment_helper( + r" +const staticCacheMap = /*#__PURE__*/ new WeakMap() +", + "const staticCacheMap = /*#__PURE__*/ new WeakMap();\n", + ); } #[test]