Skip to content

Commit 43932bf

Browse files
committed
Fix: improve error messages
1 parent 775ce57 commit 43932bf

File tree

10 files changed

+1562
-104
lines changed

10 files changed

+1562
-104
lines changed

src/common/location-calculator.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,21 @@ export class LocationCalculator {
8383
/**
8484
* Calculate the location of the given index.
8585
* @param index The index to calculate their location.
86-
* @param lineTerminators The list of the offset of line terminators.
87-
* @returns The location of the offset.
86+
* @returns The location of the index.
8887
*/
8988
getLocation(index: number): Location {
9089
return this._getLocation(this.baseOffset + index)
9190
}
9291

92+
/**
93+
* Calculate the offset of the given index.
94+
* @param index The index to calculate their location.
95+
* @returns The offset of the index.
96+
*/
97+
getOffsetWithGap(index: number): number {
98+
return this.baseOffset + index + this._getGap(index)
99+
}
100+
93101
/**
94102
* Modify the location information of the given node with using the base offset and gaps of this calculator.
95103
* @param node The node to modify their location.

src/script/index.ts

Lines changed: 131 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ function throwEmptyError(locationCalculator: LocationCalculator, expected: strin
125125
throw err
126126
}
127127

128+
/**
129+
* Throw syntax error of outside of code.
130+
* @param locationCalculator The location calculator to get line/column.
131+
*/
132+
function throwErrorAsAdjustingOutsideOfCode(err: any, code: string, locationCalculator: LocationCalculator): never {
133+
if (ParseError.isParseError(err)) {
134+
const endOffset = locationCalculator.getOffsetWithGap(code.length)
135+
if (err.index >= endOffset) {
136+
err.message = "Unexpected end of expression."
137+
}
138+
}
139+
140+
throw err
141+
}
142+
128143
/**
129144
* Parse the given source code.
130145
*
@@ -165,7 +180,6 @@ export interface ExpressionParseResult {
165180
*/
166181
export type ESLintCustomParserResult = ESLintProgram | ESLintExtendedProgram
167182

168-
169183
/**
170184
* Parse the given source code.
171185
*
@@ -237,24 +251,29 @@ export function parseExpression(code: string, locationCalculator: LocationCalcul
237251
debug("[script] parse expression: \"(%s)\"", code)
238252

239253
if (code.trim() === "") {
240-
throwEmptyError(locationCalculator, "an expression")
254+
return throwEmptyError(locationCalculator, "an expression")
241255
}
242256

243-
const ast = parseScriptFragment(
244-
`(${code})`,
245-
locationCalculator.getSubCalculatorAfter(-1),
246-
parserOptions
247-
).ast
248-
const references = analyzeExternalReferences(ast, parserOptions)
249-
const expression = (ast.body[0] as ESLintExpressionStatement).expression
250-
const tokens = ast.tokens || []
251-
const comments = ast.comments || []
252-
253-
// Remvoe parens.
254-
tokens.shift()
255-
tokens.pop()
256-
257-
return {expression, tokens, comments, references, variables: []}
257+
try {
258+
const ast = parseScriptFragment(
259+
`(${code})`,
260+
locationCalculator.getSubCalculatorAfter(-1),
261+
parserOptions
262+
).ast
263+
const references = analyzeExternalReferences(ast, parserOptions)
264+
const expression = (ast.body[0] as ESLintExpressionStatement).expression
265+
const tokens = ast.tokens || []
266+
const comments = ast.comments || []
267+
268+
// Remvoe parens.
269+
tokens.shift()
270+
tokens.pop()
271+
272+
return {expression, tokens, comments, references, variables: []}
273+
}
274+
catch (err) {
275+
return throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator)
276+
}
258277
}
259278

260279
/**
@@ -272,61 +291,66 @@ export function parseVForExpression(code: string, locationCalculator: LocationCa
272291
throwEmptyError(locationCalculator, "'<alias> in <expression>'")
273292
}
274293

275-
const replaced = processedCode !== code
276-
const ast = parseScriptFragment(
277-
`for(let ${processedCode});`,
278-
locationCalculator.getSubCalculatorAfter(-8),
279-
parserOptions
280-
).ast
281-
const tokens = ast.tokens || []
282-
const comments = ast.comments || []
283-
const scope = analyzeVariablesAndExternalReferences(ast, parserOptions)
284-
const references = scope.references
285-
const variables = scope.variables
286-
const statement = ast.body[0] as (ESLintForInStatement | ESLintForOfStatement)
287-
const left = normalizeLeft(statement.left, replaced)
288-
const right = statement.right
289-
const firstToken = tokens[3] || statement.left
290-
const lastToken = tokens[tokens.length - 3] || statement.right
291-
const expression: VForExpression = {
292-
type: "VForExpression",
293-
range: [firstToken.range[0], lastToken.range[1]],
294-
loc: {start: firstToken.loc.start, end: lastToken.loc.end},
295-
parent: DUMMY_PARENT,
296-
left,
297-
right,
298-
}
299-
300-
// Modify parent.
301-
for (const l of left) {
302-
if (l != null) {
303-
l.parent = expression
294+
try {
295+
const replaced = processedCode !== code
296+
const ast = parseScriptFragment(
297+
`for(let ${processedCode});`,
298+
locationCalculator.getSubCalculatorAfter(-8),
299+
parserOptions
300+
).ast
301+
const tokens = ast.tokens || []
302+
const comments = ast.comments || []
303+
const scope = analyzeVariablesAndExternalReferences(ast, parserOptions)
304+
const references = scope.references
305+
const variables = scope.variables
306+
const statement = ast.body[0] as (ESLintForInStatement | ESLintForOfStatement)
307+
const left = normalizeLeft(statement.left, replaced)
308+
const right = statement.right
309+
const firstToken = tokens[3] || statement.left
310+
const lastToken = tokens[tokens.length - 3] || statement.right
311+
const expression: VForExpression = {
312+
type: "VForExpression",
313+
range: [firstToken.range[0], lastToken.range[1]],
314+
loc: {start: firstToken.loc.start, end: lastToken.loc.end},
315+
parent: DUMMY_PARENT,
316+
left,
317+
right,
304318
}
305-
}
306-
right.parent = expression
307-
308-
// Remvoe `for` `(` `let` `)` `;`.
309-
tokens.shift()
310-
tokens.shift()
311-
tokens.shift()
312-
tokens.pop()
313-
tokens.pop()
314319

315-
// Restore parentheses from array brackets.
316-
if (replaced) {
317-
const closeOffset = statement.left.range[1] - 1
318-
const open = tokens[0]
319-
const close = tokens.find(t => t.range[0] === closeOffset)
320-
321-
if (open != null) {
322-
open.value = "("
320+
// Modify parent.
321+
for (const l of left) {
322+
if (l != null) {
323+
l.parent = expression
324+
}
323325
}
324-
if (close != null) {
325-
close.value = ")"
326+
right.parent = expression
327+
328+
// Remvoe `for` `(` `let` `)` `;`.
329+
tokens.shift()
330+
tokens.shift()
331+
tokens.shift()
332+
tokens.pop()
333+
tokens.pop()
334+
335+
// Restore parentheses from array brackets.
336+
if (replaced) {
337+
const closeOffset = statement.left.range[1] - 1
338+
const open = tokens[0]
339+
const close = tokens.find(t => t.range[0] === closeOffset)
340+
341+
if (open != null) {
342+
open.value = "("
343+
}
344+
if (close != null) {
345+
close.value = ")"
346+
}
326347
}
327-
}
328348

329-
return {expression, tokens, comments, references, variables}
349+
return {expression, tokens, comments, references, variables}
350+
}
351+
catch (err) {
352+
return throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator)
353+
}
330354
}
331355

332356
/**
@@ -343,43 +367,48 @@ export function parseVOnExpression(code: string, locationCalculator: LocationCal
343367
throwEmptyError(locationCalculator, "statements")
344368
}
345369

346-
const ast = parseScriptFragment(
347-
`{${code}}`,
348-
locationCalculator.getSubCalculatorAfter(-1),
349-
parserOptions
350-
).ast
351-
const references = analyzeExternalReferences(ast, parserOptions)
352-
const block = ast.body[0] as ESLintBlockStatement
353-
const body = block.body
354-
const first = lodash.first(body)
355-
const last = lodash.last(body)
356-
const expression: VOnExpression = {
357-
type: "VOnExpression",
358-
range: [
359-
(first != null) ? first.range[0] : block.range[0] + 1,
360-
(last != null) ? last.range[1] : block.range[1] - 1,
361-
],
362-
loc: {
363-
start: (first != null) ? first.loc.start : locationCalculator.getLocation(1),
364-
end: (last != null) ? last.loc.end : locationCalculator.getLocation(code.length + 1),
365-
},
366-
parent: DUMMY_PARENT,
367-
body,
368-
}
369-
const tokens = ast.tokens || []
370-
const comments = ast.comments || []
370+
try {
371+
const ast = parseScriptFragment(
372+
`{${code}}`,
373+
locationCalculator.getSubCalculatorAfter(-1),
374+
parserOptions
375+
).ast
376+
const references = analyzeExternalReferences(ast, parserOptions)
377+
const block = ast.body[0] as ESLintBlockStatement
378+
const body = block.body
379+
const first = lodash.first(body)
380+
const last = lodash.last(body)
381+
const expression: VOnExpression = {
382+
type: "VOnExpression",
383+
range: [
384+
(first != null) ? first.range[0] : block.range[0] + 1,
385+
(last != null) ? last.range[1] : block.range[1] - 1,
386+
],
387+
loc: {
388+
start: (first != null) ? first.loc.start : locationCalculator.getLocation(1),
389+
end: (last != null) ? last.loc.end : locationCalculator.getLocation(code.length + 1),
390+
},
391+
parent: DUMMY_PARENT,
392+
body,
393+
}
394+
const tokens = ast.tokens || []
395+
const comments = ast.comments || []
371396

372-
// Modify parent.
373-
for (const b of body) {
374-
b.parent = expression
375-
}
397+
// Modify parent.
398+
for (const b of body) {
399+
b.parent = expression
400+
}
376401

377-
// Remvoe braces.
378-
tokens.shift()
379-
tokens.pop()
402+
// Remvoe braces.
403+
tokens.shift()
404+
tokens.pop()
380405

381-
// Remove $event: https://vuejs.org/v2/api/#v-on
382-
removeByName(references, "$event")
406+
// Remove $event: https://vuejs.org/v2/api/#v-on
407+
removeByName(references, "$event")
383408

384-
return {expression, tokens, comments, references, variables: []}
409+
return {expression, tokens, comments, references, variables: []}
410+
}
411+
catch (err) {
412+
return throwErrorAsAdjustingOutsideOfCode(err, code, locationCalculator)
413+
}
385414
}

0 commit comments

Comments
 (0)