diff --git a/src/ast/nodes.ts b/src/ast/nodes.ts index 86e67990..104255fe 100644 --- a/src/ast/nodes.ts +++ b/src/ast/nodes.ts @@ -753,6 +753,10 @@ export interface Reference { id: ESLintIdentifier mode: "rw" | "r" | "w" variable: Variable | null + + // For typescript-eslint + isValueReference?: boolean + isTypeReference?: boolean } /** diff --git a/src/script-setup/scope-analyzer.ts b/src/script-setup/scope-analyzer.ts index 0d77fd22..e4f892a5 100644 --- a/src/script-setup/scope-analyzer.ts +++ b/src/script-setup/scope-analyzer.ts @@ -1,6 +1,7 @@ import type * as escopeTypes from "eslint-scope" import type { ParserOptions } from "../common/parser-options" import type { + Reference, VAttribute, VDirective, VDocumentFragment, @@ -169,7 +170,17 @@ function analyzeUsedInTemplateVariables( return false } - function markVariableAsUsed(name: string) { + function markVariableAsUsed(nameOrRef: string | Reference) { + let name: string + let isValueReference: boolean | undefined + let isTypeReference: boolean | undefined + if (typeof nameOrRef === "string") { + name = nameOrRef + } else { + name = nameOrRef.id.name + isValueReference = nameOrRef.isValueReference + isTypeReference = nameOrRef.isTypeReference + } const variable = scriptVariables.get(name) if (!variable || variable.identifiers.length === 0) { return @@ -188,7 +199,9 @@ function analyzeUsedInTemplateVariables( reference.isRead = () => true reference.isReadOnly = () => true reference.isReadWrite = () => false - reference.isValueReference = true // For typescript-eslint + // For typescript-eslint + reference.isValueReference = isValueReference + reference.isTypeReference = isTypeReference variable.references.push(reference) reference.resolved = variable @@ -198,7 +211,7 @@ function analyzeUsedInTemplateVariables( for (const reference of node.references.filter( (ref) => ref.variable == null, )) { - markVariableAsUsed(reference.id.name) + markVariableAsUsed(reference) } } diff --git a/src/script/index.ts b/src/script/index.ts index a2e9d14e..62cd8708 100644 --- a/src/script/index.ts +++ b/src/script/index.ts @@ -340,14 +340,15 @@ function parseExpressionBody( debug('[script] parse expression: "0(%s)"', code) try { - const ast = parseScriptFragment( + const result = parseScriptFragment( `0(${code})`, locationCalculator.getSubCalculatorShift(-2), parserOptions, - ).ast + ) + const { ast } = result const tokens = ast.tokens || [] const comments = ast.comments || [] - const references = analyzeExternalReferences(ast, parserOptions) + const references = analyzeExternalReferences(result, parserOptions) const statement = ast.body[0] as ESLintExpressionStatement const callExpression = statement.expression as ESLintCallExpression const expression = callExpression.arguments[0] @@ -461,13 +462,14 @@ function parseFilter( // Parse the arguments. if (argsCode != null) { - const { ast } = parseScriptFragment( + const result = parseScriptFragment( `0${argsCode}`, locationCalculator .getSubCalculatorAfter(paren) .getSubCalculatorShift(-1), parserOptions, ) + const { ast } = result const statement = ast.body[0] as ESLintExpressionStatement const callExpression = statement.expression @@ -501,7 +503,7 @@ function parseFilter( } tokens.push(...ast.tokens!) comments.push(...ast.comments!) - references.push(...analyzeExternalReferences(ast, parserOptions)) + references.push(...analyzeExternalReferences(result, parserOptions)) } // Update range. @@ -755,16 +757,20 @@ export function parseVForExpression( processed.iterator, ) - const ast = parseScriptFragment( + const result = parseScriptFragment( `for(let ${processed.aliasesWithBrackets}${processed.delimiter}${processed.iterator});`, locationCalculator.getSubCalculatorShift( processed.hasParens ? -8 : -9, ), parserOptions, - ).ast + ) + const { ast } = result const tokens = ast.tokens || [] const comments = ast.comments || [] - const scope = analyzeVariablesAndExternalReferences(ast, parserOptions) + const scope = analyzeVariablesAndExternalReferences( + result, + parserOptions, + ) const references = scope.references const variables = scope.variables const statement = ast.body[0] as @@ -934,14 +940,15 @@ function parseVForAliasesForEcmaVersion5( locationCalculator: LocationCalculatorForHtml, parserOptions: ParserOptions, ) { - const ast = parseScriptFragment( + const result = parseScriptFragment( `0(${code})`, locationCalculator.getSubCalculatorShift(-2), parserOptions, - ).ast + ) + const { ast } = result const tokens = ast.tokens || [] const comments = ast.comments || [] - const variables = analyzeExternalReferences(ast, parserOptions).map( + const variables = analyzeExternalReferences(result, parserOptions).map( transformVariable, ) @@ -984,14 +991,15 @@ function parseVForIteratorForEcmaVersion5( locationCalculator: LocationCalculatorForHtml, parserOptions: ParserOptions, ) { - const ast = parseScriptFragment( + const result = parseScriptFragment( `0(${code})`, locationCalculator.getSubCalculatorShift(-2), parserOptions, - ).ast + ) + const { ast } = result const tokens = ast.tokens || [] const comments = ast.comments || [] - const references = analyzeExternalReferences(ast, parserOptions) + const references = analyzeExternalReferences(result, parserOptions) const statement = ast.body[0] as ESLintExpressionStatement const callExpression = statement.expression as ESLintCallExpression @@ -1049,12 +1057,13 @@ function parseVOnExpressionBody( } try { - const ast = parseScriptFragment( + const result = parseScriptFragment( `void function($event){${code}}`, locationCalculator.getSubCalculatorShift(-22), parserOptions, - ).ast - const references = analyzeExternalReferences(ast, parserOptions) + ) + const { ast } = result + const references = analyzeExternalReferences(result, parserOptions) const outermostStatement = ast.body[0] as ESLintExpressionStatement const functionDecl = ( outermostStatement.expression as ESLintUnaryExpression @@ -1126,11 +1135,12 @@ export function parseSlotScopeExpression( } try { - const ast = parseScriptFragment( + const result = parseScriptFragment( `void function(${code}) {}`, locationCalculator.getSubCalculatorShift(-14), parserOptions, - ).ast + ) + const { ast } = result const statement = ast.body[0] as ESLintExpressionStatement const rawExpression = statement.expression as ESLintUnaryExpression const functionDecl = rawExpression.argument as ESLintFunctionExpression @@ -1148,7 +1158,10 @@ export function parseSlotScopeExpression( const tokens = ast.tokens || [] const comments = ast.comments || [] - const scope = analyzeVariablesAndExternalReferences(ast, parserOptions) + const scope = analyzeVariablesAndExternalReferences( + result, + parserOptions, + ) const references = scope.references const variables = scope.variables const firstParam = first(params)! diff --git a/src/script/scope-analyzer.ts b/src/script/scope-analyzer.ts index 369dbdb3..38de19f1 100644 --- a/src/script/scope-analyzer.ts +++ b/src/script/scope-analyzer.ts @@ -15,6 +15,11 @@ import { getFallbackKeys } from "../ast" import { getEslintScope } from "../common/eslint-scope" import { getEcmaVersionIfUseEspree } from "../common/espree" +type ParserResult = { + ast: ESLintProgram + scopeManager?: escopeTypes.ScopeManager +} + /** * Check whether the given reference is unique in the belonging array. * @param reference The current reference to check. @@ -54,6 +59,8 @@ function transformReference(reference: escopeTypes.Reference): Reference { ? "w" : /* otherwise */ "rw", variable: null, + isValueReference: reference.isValueReference, + isTypeReference: reference.isTypeReference, } Object.defineProperty(ret, "variable", { enumerable: false }) @@ -106,40 +113,43 @@ export function analyzeScope( } /** - * - * @param ast + * Analyze the scope of the given AST. + * @param {ParserResult} parserResult The parser result to analyze. * @param parserOptions */ function analyze( - ast: ESLintProgram, + parserResult: ParserResult, parserOptions: ParserOptions, ): escopeTypes.Scope { - return analyzeScope(ast, parserOptions).globalScope + const scopeManager = + parserResult.scopeManager || + analyzeScope(parserResult.ast, parserOptions) + return scopeManager.globalScope } /** * Analyze the external references of the given AST. - * @param {ASTNode} ast The root node to analyze. + * @param {ParserResult} parserResult The parser result to analyze. * @returns {Reference[]} The reference objects of external references. */ export function analyzeExternalReferences( - ast: ESLintProgram, + parserResult: ParserResult, parserOptions: ParserOptions, ): Reference[] { - const scope = analyze(ast, parserOptions) + const scope = analyze(parserResult, parserOptions) return scope.through.filter(isUnique).map(transformReference) } /** * Analyze the external references of the given AST. - * @param {ASTNode} ast The root node to analyze. + * @param {ParserResult} parserResult The parser result to analyze. * @returns {Reference[]} The reference objects of external references. */ export function analyzeVariablesAndExternalReferences( - ast: ESLintProgram, + parserResult: ParserResult, parserOptions: ParserOptions, ): { variables: Variable[]; references: Reference[] } { - const scope = analyze(ast, parserOptions) + const scope = analyze(parserResult, parserOptions) return { variables: getForScope(scope) .variables.filter(hasDefinition) diff --git a/test/fixtures/ast/parser-option-multiple-parsers-1/ast.json b/test/fixtures/ast/parser-option-multiple-parsers-1/ast.json index 97d900ed..e4419dca 100644 --- a/test/fixtures/ast/parser-option-multiple-parsers-1/ast.json +++ b/test/fixtures/ast/parser-option-multiple-parsers-1/ast.json @@ -656,7 +656,9 @@ } } }, - "mode": "r" + "mode": "r", + "isValueReference": true, + "isTypeReference": false }, { "id": { @@ -677,7 +679,55 @@ } } }, - "mode": "r" + "mode": "r", + "isValueReference": true, + "isTypeReference": false + }, + { + "id": { + "type": "Identifier", + "name": "b", + "range": [ + 17, + 18 + ], + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 7 + } + } + }, + "mode": "r", + "isValueReference": false, + "isTypeReference": true + }, + { + "id": { + "type": "Identifier", + "name": "c", + "range": [ + 19, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 9 + } + } + }, + "mode": "r", + "isValueReference": false, + "isTypeReference": true } ] }, diff --git a/test/fixtures/ast/parser-option-multiple-parsers-without-script/ast.json b/test/fixtures/ast/parser-option-multiple-parsers-without-script/ast.json index b7deda38..8ad44185 100644 --- a/test/fixtures/ast/parser-option-multiple-parsers-without-script/ast.json +++ b/test/fixtures/ast/parser-option-multiple-parsers-without-script/ast.json @@ -275,7 +275,9 @@ } } }, - "mode": "r" + "mode": "r", + "isValueReference": true, + "isTypeReference": false }, { "id": { @@ -296,7 +298,55 @@ } } }, - "mode": "r" + "mode": "r", + "isValueReference": true, + "isTypeReference": false + }, + { + "id": { + "type": "Identifier", + "name": "b", + "range": [ + 17, + 18 + ], + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 7 + } + } + }, + "mode": "r", + "isValueReference": false, + "isTypeReference": true + }, + { + "id": { + "type": "Identifier", + "name": "c", + "range": [ + 19, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 9 + } + } + }, + "mode": "r", + "isValueReference": false, + "isTypeReference": true } ] }, diff --git a/test/fixtures/ast/ts-script-setup-with-use-global-var/ast.json b/test/fixtures/ast/ts-script-setup-with-use-global-var/ast.json index b86e69af..6d83d037 100644 --- a/test/fixtures/ast/ts-script-setup-with-use-global-var/ast.json +++ b/test/fixtures/ast/ts-script-setup-with-use-global-var/ast.json @@ -357,7 +357,9 @@ } } }, - "mode": "r" + "mode": "r", + "isValueReference": true, + "isTypeReference": false } ] }, diff --git a/test/fixtures/ast/ts-script-setup-with-use-global-var/scope.json b/test/fixtures/ast/ts-script-setup-with-use-global-var/scope.json index 80bdb3dd..d82fa33c 100644 --- a/test/fixtures/ast/ts-script-setup-with-use-global-var/scope.json +++ b/test/fixtures/ast/ts-script-setup-with-use-global-var/scope.json @@ -1,6 +1,90 @@ { "type": "global", "variables": [ + { + "name": "ClassMemberDecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "DecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassDecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassMethodDecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassGetterDecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassSetterDecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassAccessorDecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassAccessorDecoratorTarget", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassAccessorDecoratorResult", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassFieldDecoratorContext", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ClassDecorator", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "PropertyDecorator", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "MethodDecorator", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "ParameterDecorator", + "identifiers": [], + "defs": [], + "references": [] + }, { "name": "Symbol", "identifiers": [], @@ -301,30 +385,6 @@ "defs": [], "references": [] }, - { - "name": "ClassDecorator", - "identifiers": [], - "defs": [], - "references": [] - }, - { - "name": "PropertyDecorator", - "identifiers": [], - "defs": [], - "references": [] - }, - { - "name": "MethodDecorator", - "identifiers": [], - "defs": [], - "references": [] - }, - { - "name": "ParameterDecorator", - "identifiers": [], - "defs": [], - "references": [] - }, { "name": "PromiseConstructorLike", "identifiers": [], diff --git a/test/fixtures/integrations/script-setup-with-typescript-eslint/consistent-type-imports/valid-type-import.vue b/test/fixtures/integrations/script-setup-with-typescript-eslint/consistent-type-imports/valid-type-import.vue new file mode 100644 index 00000000..c1c451fc --- /dev/null +++ b/test/fixtures/integrations/script-setup-with-typescript-eslint/consistent-type-imports/valid-type-import.vue @@ -0,0 +1,11 @@ + + + + diff --git a/typings/eslint-scope/index.d.ts b/typings/eslint-scope/index.d.ts index c3ae32b5..6d14afcf 100644 --- a/typings/eslint-scope/index.d.ts +++ b/typings/eslint-scope/index.d.ts @@ -75,8 +75,8 @@ export class Reference { public isWriteOnly(): boolean // For typescript-eslint - public isTypeReference: boolean - public isValueReference: boolean + public isTypeReference?: boolean + public isValueReference?: boolean } export declare const analyze: (