Skip to content
This repository was archived by the owner on Jan 19, 2019. It is now read-only.

sourceCode.getTokenAfter works incorrectly on our AST #122

Closed
flying-sheep opened this issue Dec 6, 2016 · 5 comments
Closed

sourceCode.getTokenAfter works incorrectly on our AST #122

flying-sheep opened this issue Dec 6, 2016 · 5 comments
Labels

Comments

@flying-sheep
Copy link
Contributor

flying-sheep commented Dec 6, 2016

Test:

const p = require('typescript-eslint-parser')
const { SourceCode, linter } = require('eslint')

const code = `
export default class Reader extends React.Component<{}, {}> {
	render() {
		return (
			<div className="flex-transparent">
				<nav></nav>
			</div>
		)
	}
}
`
const ast = p.parse(code, { ecmaFeatures: { jsx: 'react' }, tokens: true, comment: true })

const sc = new SourceCode(code, ast)

const messages = linter.verify(sc, {
	rules: {
		'no-extra-semi': 2,
	},
}, { filename: 'Reader.tsx' })

console.log(messages)

error:

/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/rules/no-extra-semi.js:51
                token.type === "Punctuator" && token.value !== "}";
                     ^

TypeError: Cannot read property 'type' of undefined
    at checkForPartOfClassBody (/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/rules/no-extra-semi.js:51:22)
    at EventEmitter.MethodDefinition (/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/rules/no-extra-semi.js:100:17)
    at emitOne (events.js:96:13)
    at EventEmitter.emit (events.js:188:7)
    at NodeEventGenerator.enterNode (/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/util/node-event-generator.js:40:22)
    at CodePathAnalyzer.enterNode (/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js:608:23)
    at CommentEventGenerator.enterNode (/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/util/comment-event-generator.js:97:23)
    at Controller.enter (/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/eslint.js:925:36)
    at Controller.__execute (/home/phil/Dev/JS/electro-pub/node_modules/estraverse/estraverse.js:397:31)
    at Controller.traverse (/home/phil/Dev/JS/electro-pub/node_modules/estraverse/estraverse.js:501:28)
@flying-sheep
Copy link
Contributor Author

flying-sheep commented Dec 6, 2016

more info: for this specific rule, those visitors get called:

ClassBody(node) {
	checkForPartOfClassBody(sourceCode.getFirstToken(node, 1)); // 0 is `{`.
},
MethodDefinition(node) {
	 checkForPartOfClassBody(sourceCode.getTokenAfter(node));
},

at one point, this happens

  1. MethodDefinition(node) is called
  2. sourceCode.getTokenAfter(node) is called and returns undefined
  3. checkForPartOfClassBody(undefined) is called and breaks

apparently, there should always be a token after a MethodDefinition?

@flying-sheep
Copy link
Contributor Author

OK, this code tells me what we need to know:

const tsp = require('typescript-eslint-parser')
const esp = require('espree')
const { SourceCode, linter } = require('eslint')

const code = `
export default class Reader {
	render() {
		return (
			<div className="flex-transparent">
				<nav></nav>
			</div>
		)
	}
}
`

const configs = [
	[esp, { ecmaFeatures: { jsx: true }, tokens: true, comment: true, loc: true, range: true, ecmaVersion: 6, sourceType: 'module' }],
	[tsp, { ecmaFeatures: { jsx: 'react' }, tokens: true, comment: true }],
]

for (const [parser, options] of configs) {
	const ast = parser.parse(code, options)

	const sc = new SourceCode(code, ast)
	
	const method = sc.ast.body[0].declaration.body.body[0]
	console.log(sc.getTokenAfter(method))

	const messages = linter.verify(sc, {
		rules: {
			'no-extra-semi': 2,
		},
	}, { filename: 'Reader.tsx' })

	console.log(messages)
}

the output is:

for espree:

Token {
  type: 'Punctuator',
  value: '}',
  start: 125,
  end: 126,
  loc:
   SourceLocation {
     start: Position { line: 10, column: 0 },
     end: Position { line: 10, column: 1 } },
  range: [ 125, 126 ] }
[]

for this parser:

undefined
/home/phil/Dev/JS/electro-pub/node_modules/eslint/lib/rules/no-extra-semi.js:51
                token.type === "Punctuator" && token.value !== "}";
...

i.e. somehow the AST this parser creates doesn’t allow eslint’s getTokenAfter to retrieve the closing brace of the class declaration.

@flying-sheep flying-sheep changed the title sourceCode.getFirstToken may not return “undefined” sourceCode.getTokenAfter works incorrectly on our AST Dec 6, 2016
@JamesHenry
Copy link
Member

JamesHenry commented Dec 13, 2016

This is another duplicate of #70.

The problem is with the whitespace between the JSX nodes, amending it to the following causes the source to be parsed without error:

return (
  <div className="flex-transparent"><nav></nav></div>
)

(I do realise that totally sucks 😄 I will try and get a fix merged into the TypeScript compiler as soon as possible)

@flying-sheep
Copy link
Contributor Author

aww, i invested so much detective work into the code, and then it turned out a tiny bit more in the issue tracker would have been sufficient 😢

thanks 😆

@JamesHenry
Copy link
Member

It happens! 😄

Thank you for your digging regardless @flying-sheep - it is so helpful when users are able to provide detailed context, and simple examples.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants