From fa9b12b2890bca38d3799197a3a5d62ee398f484 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Sat, 5 Feb 2022 17:27:26 +0900 Subject: [PATCH] Add virtual reference to a used store --- src/parser/analyze-scope.ts | 132 +++++++++++++----- .../ast/$var-no-unused-vars-result.json | 8 -- .../parser/ast/$var-scope-output.json | 40 ++++++ ...rite-only-store-no-unused-vars-result.json | 8 -- .../unused-write-only-store-scope-output.json | 80 +++++++++++ 5 files changed, 215 insertions(+), 53 deletions(-) delete mode 100644 tests/fixtures/parser/ast/$var-no-unused-vars-result.json delete mode 100644 tests/fixtures/parser/ast/unused-write-only-store-no-unused-vars-result.json diff --git a/src/parser/analyze-scope.ts b/src/parser/analyze-scope.ts index db2e34ae..89c93915 100644 --- a/src/parser/analyze-scope.ts +++ b/src/parser/analyze-scope.ts @@ -90,30 +90,65 @@ export function analyzeReactiveScope(scopeManager: ScopeManager): void { * Analyze store scope. e.g. $count */ export function analyzeStoreScope(scopeManager: ScopeManager): void { + const moduleScope = scopeManager.scopes.find( + (scope) => scope.type === "module", + ) + if (!moduleScope) { + return + } + const toBeMarkAsUsedReferences: Reference[] = [] + for (const reference of [...scopeManager.globalScope.through]) { if (reference.identifier.name.startsWith("$")) { const realName = reference.identifier.name.slice(1) - const moduleScope = scopeManager.scopes.find( - (scope) => scope.type === "module", - ) - if (moduleScope) { - const variable = moduleScope?.set.get(realName) - if (variable) { - // It does not write directly to the original variable. - // Therefore, this variable is always a reference. - reference.isWrite = () => false - reference.isWriteOnly = () => false - reference.isReadWrite = () => false - reference.isReadOnly = () => true - reference.isRead = () => true - - variable.references.push(reference) - reference.resolved = variable - removeReferenceFromThrough(reference, moduleScope) + const variable = moduleScope.set.get(realName) + if (variable) { + if (reference.isWriteOnly()) { + // Need mark as used + toBeMarkAsUsedReferences.push(reference) } + + // It does not write directly to the original variable. + // Therefore, this variable is always a reference. + reference.isWrite = () => false + reference.isWriteOnly = () => false + reference.isReadWrite = () => false + reference.isReadOnly = () => true + reference.isRead = () => true + + variable.references.push(reference) + reference.resolved = variable + removeReferenceFromThrough(reference, moduleScope) } } } + + for (const variable of new Set( + toBeMarkAsUsedReferences.map((ref) => ref.resolved!), + )) { + if ( + variable.references.some( + (ref) => + !toBeMarkAsUsedReferences.includes(ref) && + ref.identifier !== variable.identifiers[0], + ) + ) { + // It is already used. + continue + } + + // Add the virtual reference for reading. + ;( + addVirtualReference( + variable.identifiers[0], + variable, + moduleScope, + { + read: true, + }, + ) as any + ).svelteMarkAsUsed = true + } } /** Transform props exports */ @@ -163,27 +198,25 @@ export function analyzePropsScope( } // Add the virtual reference for writing. - const reference = new Reference() - ;(reference as any).sveltePropReference = true - reference.from = scope - reference.identifier = { - ...node, - // @ts-expect-error -- ignore - parent: body, - loc: { - start: { ...node.loc!.start }, - end: { ...node.loc!.end }, + const reference = addVirtualReference( + { + ...node, + // @ts-expect-error -- ignore + parent: body, + loc: { + start: { ...node.loc!.start }, + end: { ...node.loc!.end }, + }, + range: [...node.range!], }, - range: [...node.range!], - } - reference.isWrite = () => true - reference.isWriteOnly = () => false - reference.isRead = () => true - reference.isReadOnly = () => false - reference.isReadWrite = () => true - - variable.references.push(reference) - reference.resolved = variable + variable, + scope, + { + write: true, + read: true, + }, + ) + ;(reference as any).sveltePropReference = true } } } @@ -210,6 +243,31 @@ function removeReferenceFromThrough(reference: Reference, baseScope: Scope) { } } +/** + * Add the virtual reference. + */ +function addVirtualReference( + node: ESTree.Identifier, + variable: Variable, + scope: Scope, + readWrite: { read?: boolean; write?: boolean }, +) { + const reference = new Reference() + ;(reference as any).svelteVirtualReference = true + reference.from = scope + reference.identifier = node + reference.isWrite = () => Boolean(readWrite.write) + reference.isWriteOnly = () => Boolean(readWrite.write) && !readWrite.read + reference.isRead = () => Boolean(readWrite.read) + reference.isReadOnly = () => Boolean(readWrite.read) && !readWrite.write + reference.isReadWrite = () => Boolean(readWrite.read && readWrite.write) + + variable.references.push(reference) + reference.resolved = variable + + return reference +} + /** Get parent node */ function getParent(node: ESTree.Node): ESTree.Node | null { return (node as any).parent diff --git a/tests/fixtures/parser/ast/$var-no-unused-vars-result.json b/tests/fixtures/parser/ast/$var-no-unused-vars-result.json deleted file mode 100644 index 00d65718..00000000 --- a/tests/fixtures/parser/ast/$var-no-unused-vars-result.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "ruleId": "no-unused-vars", - "code": "a", - "line": 2, - "column": 12 - } -] \ No newline at end of file diff --git a/tests/fixtures/parser/ast/$var-scope-output.json b/tests/fixtures/parser/ast/$var-scope-output.json index 30bf019d..4ade67f7 100644 --- a/tests/fixtures/parser/ast/$var-scope-output.json +++ b/tests/fixtures/parser/ast/$var-scope-output.json @@ -163,6 +163,46 @@ } } } + }, + { + "identifier": { + "type": "Identifier", + "name": "a", + "range": [ + 20, + 21 + ], + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 2, + "column": 12 + } + } + }, + "from": "module", + "init": null, + "resolved": { + "type": "Identifier", + "name": "a", + "range": [ + 20, + 21 + ], + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 2, + "column": 12 + } + } + } } ] } diff --git a/tests/fixtures/parser/ast/unused-write-only-store-no-unused-vars-result.json b/tests/fixtures/parser/ast/unused-write-only-store-no-unused-vars-result.json deleted file mode 100644 index 20a0dc08..00000000 --- a/tests/fixtures/parser/ast/unused-write-only-store-no-unused-vars-result.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "ruleId": "no-unused-vars", - "code": "writeOnly", - "line": 3, - "column": 8 - } -] \ No newline at end of file diff --git a/tests/fixtures/parser/ast/unused-write-only-store-scope-output.json b/tests/fixtures/parser/ast/unused-write-only-store-scope-output.json index e2095adf..b249c69b 100644 --- a/tests/fixtures/parser/ast/unused-write-only-store-scope-output.json +++ b/tests/fixtures/parser/ast/unused-write-only-store-scope-output.json @@ -243,6 +243,46 @@ } } } + }, + { + "identifier": { + "type": "Identifier", + "name": "imported", + "range": [ + 19, + 27 + ], + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 18 + } + } + }, + "from": "module", + "init": null, + "resolved": { + "type": "Identifier", + "name": "imported", + "range": [ + 19, + 27 + ], + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 18 + } + } + } } ] }, @@ -624,6 +664,46 @@ } } } + }, + { + "identifier": { + "type": "Identifier", + "name": "writeOnly", + "range": [ + 69, + 78 + ], + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 3, + "column": 16 + } + } + }, + "from": "module", + "init": null, + "resolved": { + "type": "Identifier", + "name": "writeOnly", + "range": [ + 69, + 78 + ], + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 3, + "column": 16 + } + } + } } ] },