Skip to content

Commit

Permalink
Fix wrong scope reference type for <script setup lang=ts> (#181)
Browse files Browse the repository at this point in the history
* Fix wrong scope reference type for `<script setup lang=ts>`

* update

* update
  • Loading branch information
ota-meshi authored Mar 30, 2023
1 parent 5c3558c commit bb88c01
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 64 deletions.
4 changes: 4 additions & 0 deletions src/ast/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,10 @@ export interface Reference {
id: ESLintIdentifier
mode: "rw" | "r" | "w"
variable: Variable | null

// For typescript-eslint
isValueReference?: boolean
isTypeReference?: boolean
}

/**
Expand Down
19 changes: 16 additions & 3 deletions src/script-setup/scope-analyzer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type * as escopeTypes from "eslint-scope"
import type { ParserOptions } from "../common/parser-options"
import type {
Reference,
VAttribute,
VDirective,
VDocumentFragment,
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -198,7 +211,7 @@ function analyzeUsedInTemplateVariables(
for (const reference of node.references.filter(
(ref) => ref.variable == null,
)) {
markVariableAsUsed(reference.id.name)
markVariableAsUsed(reference)
}
}

Expand Down
53 changes: 33 additions & 20 deletions src/script/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)!
Expand Down
30 changes: 20 additions & 10 deletions src/script/scope-analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 })

Expand Down Expand Up @@ -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)
Expand Down
54 changes: 52 additions & 2 deletions test/fixtures/ast/parser-option-multiple-parsers-1/ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,9 @@
}
}
},
"mode": "r"
"mode": "r",
"isValueReference": true,
"isTypeReference": false
},
{
"id": {
Expand All @@ -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
}
]
},
Expand Down
Loading

0 comments on commit bb88c01

Please sign in to comment.