From c40d2066b2ebee4763681566e822f1a79ebac33e Mon Sep 17 00:00:00 2001 From: kzc Date: Fri, 14 May 2021 11:16:28 -0400 Subject: [PATCH] ignore invalid trailing pure annotations (#4068) that were incorrectly associated with subsequent calls Co-authored-by: Lukas Taegert-Atkinson --- src/Graph.ts | 2 +- src/utils/pureComments.ts | 21 ++++++++++++-- .../pure-comment-line-break/_expected.js | 22 +++++++++++++-- .../samples/pure-comment-line-break/main.js | 28 +++++++++++++++++++ .../pure-comments-disabled/_expected.js | 24 +++++++++++++++- .../samples/pure-comments-disabled/main.js | 22 +++++++++++++++ 6 files changed, 113 insertions(+), 6 deletions(-) diff --git a/src/Graph.ts b/src/Graph.ts index 847b9b67d70..9afb0fdda66 100644 --- a/src/Graph.ts +++ b/src/Graph.ts @@ -136,7 +136,7 @@ export default class Graph { options.onComment = onCommentOrig; - markPureCallExpressions(comments, ast); + markPureCallExpressions(comments, ast, code); return ast; } diff --git a/src/utils/pureComments.ts b/src/utils/pureComments.ts index f6c681d3074..bad56369326 100644 --- a/src/utils/pureComments.ts +++ b/src/utils/pureComments.ts @@ -19,10 +19,25 @@ basicWalker.PropertyDefinition = function (node: any, st: any, c: any) { }; interface CommentState { + code: string; commentIndex: number; commentNodes: acorn.Comment[]; } +function isOnlyWhitespaceOrComments(code: string) { + // streamline the typical case + if (/^\s*$/.test(code)) return true; + try { + // successful only if it's a valid Program without statements + const ast = acorn.parse(code, { ecmaVersion: 'latest' }) as any; + return ast.body && ast.body.length === 0; + } catch { + // should never be reached - + // the entire module was previously successfully parsed + } + return false; +} + function handlePureAnnotationsOfNode( node: acorn.Node, state: CommentState, @@ -30,7 +45,8 @@ function handlePureAnnotationsOfNode( ) { let commentNode = state.commentNodes[state.commentIndex]; while (commentNode && node.start >= commentNode.end) { - markPureNode(node, commentNode); + const between = state.code.substring(commentNode.end, node.start); + if (isOnlyWhitespaceOrComments(between)) markPureNode(node, commentNode); commentNode = state.commentNodes[++state.commentIndex]; } if (commentNode && commentNode.end <= node.end) { @@ -62,8 +78,9 @@ function markPureNode( const pureCommentRegex = /[@#]__PURE__/; const isPureComment = (comment: acorn.Comment) => pureCommentRegex.test(comment.value); -export function markPureCallExpressions(comments: acorn.Comment[], esTreeAst: acorn.Node) { +export function markPureCallExpressions(comments: acorn.Comment[], esTreeAst: acorn.Node, code: string) { handlePureAnnotationsOfNode(esTreeAst, { + code, commentIndex: 0, commentNodes: comments.filter(isPureComment) }); diff --git a/test/form/samples/pure-comment-line-break/_expected.js b/test/form/samples/pure-comment-line-break/_expected.js index 2594544f535..a28db869c3b 100644 --- a/test/form/samples/pure-comment-line-break/_expected.js +++ b/test/form/samples/pure-comment-line-break/_expected.js @@ -6,7 +6,7 @@ console.log('should remain impure'); console.log('code'); console.log('should remain impure'); -console.log('code'); +console.log('code')/*@__PURE__*/; console.log('should remain impure'); console.log('should remain impure'); @@ -16,7 +16,7 @@ console.log('should remain impure'); console.log('code'), console.log('should remain impure'); -console.log('code'), +console.log('code')/*@__PURE__*/, console.log('should remain impure'); console.log('should remain impure'); @@ -35,3 +35,21 @@ console.log('should remain impure', x); { console.log('should remain impure'); } +keep1() /*@__PURE__*/ ; keep2(); +keep3() ; +keep4() /*@__PURE__*/ ; /* other comment */ keep5(); +keep6() /*@__PURE__*/ ; // other comment +keep7(); +keep8() /*@__PURE__*/ && keep9(); + +/*@__PURE__*/ Drop1(), // FIXME: unrelated issue +Keep1() /*@__PURE__*/ , Keep2(), +Keep3() , +Keep4() /*@__PURE__*/ , /* other comment */ Keep5(), +Keep6() /*@__PURE__*/ , // other comment +Keep7(), +Keep8() /*@__PURE__*/ && Keep9(); + +// FIXME: unrelated issue +/*@__PURE__*/ Drop10(), +/*@__PURE__*/ Drop13(); diff --git a/test/form/samples/pure-comment-line-break/main.js b/test/form/samples/pure-comment-line-break/main.js index a3a6d9a181c..38dc5792269 100644 --- a/test/form/samples/pure-comment-line-break/main.js +++ b/test/form/samples/pure-comment-line-break/main.js @@ -42,3 +42,31 @@ console.log('should remain impure', code); if (true) { console.log('should remain impure'); } + +/*@__PURE__*/ drop1(); +/*@__PURE__*/ +drop2(); +keep1() /*@__PURE__*/ ; keep2(); +keep3() ; /*@__PURE__*/ +drop3(); +keep4() /*@__PURE__*/ ; /* other comment */ keep5(); +keep6() /*@__PURE__*/ ; // other comment +keep7(); +keep8() /*@__PURE__*/ && keep9(); + +/*@__PURE__*/ Drop1(), // FIXME: unrelated issue +/*@__PURE__*/ +Drop2(), +Keep1() /*@__PURE__*/ , Keep2(), +Keep3() , /*@__PURE__*/ +Drop3(), +Keep4() /*@__PURE__*/ , /* other comment */ Keep5(), +Keep6() /*@__PURE__*/ , // other comment +Keep7(), +Keep8() /*@__PURE__*/ && Keep9(); + +// FIXME: unrelated issue +/*@__PURE__*/ Drop10(), +/*@__PURE__*/ Drop11(), +/*@__PURE__*/ Drop12(), +/*@__PURE__*/ Drop13(); diff --git a/test/form/samples/pure-comments-disabled/_expected.js b/test/form/samples/pure-comments-disabled/_expected.js index dae6dbd4fa4..e19ce552917 100644 --- a/test/form/samples/pure-comments-disabled/_expected.js +++ b/test/form/samples/pure-comments-disabled/_expected.js @@ -2,5 +2,27 @@ /*@__PURE__*/ a(); /*@__PURE__*/ new a(); -console.log('code'); +console.log('code')/*@__PURE__*/; console.log('should remain impure'); + +/*@__PURE__*/ drop1(); +/*@__PURE__*/ +drop2(); +keep1() /*@__PURE__*/ ; keep2(); +keep3() ; /*@__PURE__*/ +drop3(); +keep4() /*@__PURE__*/ ; /* other comment */ keep5(); +keep6() /*@__PURE__*/ ; // other comment +keep7(); +keep8() /*@__PURE__*/ && keep9(); + +/*@__PURE__*/ Drop1(), +/*@__PURE__*/ +Drop2(), +Keep1() /*@__PURE__*/ , Keep2(), +Keep3() , /*@__PURE__*/ +Drop3(), +Keep4() /*@__PURE__*/ , /* other comment */ Keep5(), +Keep6() /*@__PURE__*/ , // other comment +Keep7(), +Keep8() /*@__PURE__*/ && Keep9(); diff --git a/test/form/samples/pure-comments-disabled/main.js b/test/form/samples/pure-comments-disabled/main.js index bafd2b7cba8..04c495a0f05 100644 --- a/test/form/samples/pure-comments-disabled/main.js +++ b/test/form/samples/pure-comments-disabled/main.js @@ -5,3 +5,25 @@ console.log('code')/*@__PURE__*/; /*@__PURE__*/(() => {})(); console.log('should remain impure'); + +/*@__PURE__*/ drop1(); +/*@__PURE__*/ +drop2(); +keep1() /*@__PURE__*/ ; keep2(); +keep3() ; /*@__PURE__*/ +drop3(); +keep4() /*@__PURE__*/ ; /* other comment */ keep5(); +keep6() /*@__PURE__*/ ; // other comment +keep7(); +keep8() /*@__PURE__*/ && keep9(); + +/*@__PURE__*/ Drop1(), +/*@__PURE__*/ +Drop2(), +Keep1() /*@__PURE__*/ , Keep2(), +Keep3() , /*@__PURE__*/ +Drop3(), +Keep4() /*@__PURE__*/ , /* other comment */ Keep5(), +Keep6() /*@__PURE__*/ , // other comment +Keep7(), +Keep8() /*@__PURE__*/ && Keep9();