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

Commit 55e15cd

Browse files
committed
Fix: Allow comment scanner to rescan tokens (fixes #216)
Template strings and regex patterns need to be rescaned to not scan tokens as comments
1 parent dd57f81 commit 55e15cd

39 files changed

+2039
-121
lines changed

Diff for: lib/ast-converter.js

+153-21
Original file line numberDiff line numberDiff line change
@@ -191,20 +191,6 @@ function getLocFor(start, end, ast) {
191191
*/
192192
function getLoc(nodeOrToken, ast) {
193193
return getLocFor(nodeOrToken.getStart(), nodeOrToken.end, ast);
194-
// var start = nodeOrToken.getStart(),
195-
// startLoc = ast.getLineAndCharacterOfPosition(start),
196-
// endLoc = ast.getLineAndCharacterOfPosition(nodeOrToken.end);
197-
198-
// return {
199-
// start: {
200-
// line: startLoc.line + 1,
201-
// column: startLoc.character
202-
// },
203-
// end: {
204-
// line: endLoc.line + 1,
205-
// column: endLoc.character
206-
// }
207-
// };
208194
}
209195

210196
/**
@@ -451,6 +437,157 @@ function convertTokens(ast) {
451437
return result;
452438
}
453439

440+
441+
/**
442+
* Converts a TypeScript comment to an Esprima comment.
443+
* @param {boolean} block True if it's a block comment, false if not.
444+
* @param {string} text The text of the comment.
445+
* @param {int} start The index at which the comment starts.
446+
* @param {int} end The index at which the comment ends.
447+
* @param {Location} startLoc The location at which the comment starts.
448+
* @param {Location} endLoc The location at which the comment ends.
449+
* @returns {Object} The comment object.
450+
* @private
451+
*/
452+
function convertTypeScriptCommentToEsprimaComment(block, text, start, end, startLoc, endLoc) {
453+
var comment = {
454+
type: block ? "Block" : "Line",
455+
value: text
456+
};
457+
458+
if (typeof start === "number") {
459+
comment.range = [start, end];
460+
}
461+
462+
if (typeof startLoc === "object") {
463+
comment.loc = {
464+
start: startLoc,
465+
end: endLoc
466+
};
467+
}
468+
469+
return comment;
470+
}
471+
472+
/**
473+
* Convert comment from TypeScript Triva Scanner.
474+
* @param {Object} triviaScanner TS Scanner
475+
* @param {Object} ast the AST object
476+
* @param {string} code TypeScript code
477+
* @returns {ESTreeComment} the converted ESTreeComment
478+
* @private
479+
*/
480+
function getCommentFromTriviaScanner(triviaScanner, ast, code) {
481+
var kind = triviaScanner.getToken();
482+
var isBlock = (kind === ts.SyntaxKind.MultiLineCommentTrivia);
483+
var range = {
484+
pos: triviaScanner.getTokenPos(),
485+
end: triviaScanner.getTextPos(),
486+
kind: triviaScanner.getToken()
487+
};
488+
489+
var comment = code.substring(range.pos, range.end);
490+
var text = comment.replace("//", "").replace("/*", "").replace("*/", "");
491+
var loc = getLocFor(range.pos, range.end, ast);
492+
493+
var esprimaComment = convertTypeScriptCommentToEsprimaComment(isBlock, text, range.pos, range.end, loc.start, loc.end);
494+
495+
return esprimaComment;
496+
}
497+
498+
499+
/**
500+
* Get container token node between range
501+
* @param {Object} ast the AST object
502+
* @param {int} start The index at which the comment starts.
503+
* @param {int} end The index at which the comment ends.
504+
* @returns {TSToken} typescript container token
505+
* @private
506+
*/
507+
function getNodeContainer(ast, start, end) {
508+
var container = null;
509+
510+
/**
511+
* @param {TSNode} node the TSNode
512+
* @returns {undefined}
513+
*/
514+
function walk(node) {
515+
var nodeStart = node.pos;
516+
var nodeEnd = node.end;
517+
518+
if (start >= nodeStart && end <= nodeEnd) {
519+
if (isToken(node)) {
520+
container = node;
521+
} else {
522+
node.getChildren().forEach(walk);
523+
}
524+
}
525+
}
526+
walk(ast);
527+
528+
return container;
529+
}
530+
531+
/**
532+
* Convert all comments for the given AST.
533+
* @param {Object} ast the AST object
534+
* @param {string} code the TypeScript code
535+
* @returns {ESTreeComment[]} the converted ESTreeComment
536+
* @private
537+
*/
538+
function convertComments(ast, code) {
539+
var comments = [];
540+
541+
/**
542+
* Create a TypeScript Scanner, with skipTrivia set to false so that
543+
* we can parse the comments
544+
*/
545+
var triviaScanner = ts.createScanner(ast.languageVersion, false, 0, code);
546+
547+
var kind = triviaScanner.scan();
548+
while (kind !== ts.SyntaxKind.EndOfFileToken) {
549+
var start = triviaScanner.getTokenPos();
550+
var end = triviaScanner.getTextPos();
551+
552+
var container = null;
553+
switch (kind) {
554+
case ts.SyntaxKind.SingleLineCommentTrivia:
555+
case ts.SyntaxKind.MultiLineCommentTrivia:
556+
var comment = getCommentFromTriviaScanner(triviaScanner, ast, code);
557+
558+
comments.push(comment);
559+
break;
560+
case ts.SyntaxKind.CloseBraceToken:
561+
container = getNodeContainer(ast, start, end);
562+
563+
if (
564+
container.kind === ts.SyntaxKind.TemplateMiddle
565+
|| container.kind === ts.SyntaxKind.TemplateTail
566+
) {
567+
kind = triviaScanner.reScanTemplateToken();
568+
continue;
569+
}
570+
break;
571+
case ts.SyntaxKind.SlashToken:
572+
case ts.SyntaxKind.SlashEqualsToken:
573+
container = getNodeContainer(ast, start, end);
574+
575+
if (
576+
container.kind === ts.SyntaxKind.RegularExpressionLiteral
577+
) {
578+
kind = triviaScanner.reScanSlashToken();
579+
continue;
580+
}
581+
break;
582+
default:
583+
break;
584+
}
585+
kind = triviaScanner.scan();
586+
}
587+
588+
return comments;
589+
}
590+
454591
//------------------------------------------------------------------------------
455592
// Public
456593
//------------------------------------------------------------------------------
@@ -2084,13 +2221,8 @@ module.exports = function(ast, extra) {
20842221
estree.tokens = convertTokens(ast);
20852222
}
20862223

2087-
/**
2088-
* Add the comment nodes to the AST (that were parsed separately in parser.js)
2089-
* TODO: Track the progress of https://github.com/eslint/eslint/issues/6724
2090-
* regarding ESLint itself becoming responsible for attributing comment nodes
2091-
*/
2092-
if (extra.comment || extra.attachComment) {
2093-
estree.comments = extra.comments || [];
2224+
if (extra.comment) {
2225+
estree.comments = convertComments(ast, extra.code);
20942226
}
20952227

20962228
return estree;

Diff for: parser.js

+2-95
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
var astNodeTypes = require("./lib/ast-node-types"),
1212
ts = require("typescript"),
13+
convert = require("./lib/ast-converter"),
1314
semver = require("semver");
1415

1516
var SUPPORTED_TYPESCRIPT_VERSIONS = require("./package.json").devDependencies.typescript;
@@ -51,61 +52,6 @@ function resetExtra() {
5152
};
5253
}
5354

54-
/**
55-
* Converts a TypeScript comment to an Esprima comment.
56-
* @param {boolean} block True if it's a block comment, false if not.
57-
* @param {string} text The text of the comment.
58-
* @param {int} start The index at which the comment starts.
59-
* @param {int} end The index at which the comment ends.
60-
* @param {Location} startLoc The location at which the comment starts.
61-
* @param {Location} endLoc The location at which the comment ends.
62-
* @returns {Object} The comment object.
63-
* @private
64-
*/
65-
function convertTypeScriptCommentToEsprimaComment(block, text, start, end, startLoc, endLoc) {
66-
var comment = {
67-
type: block ? "Block" : "Line",
68-
value: text
69-
};
70-
71-
if (typeof start === "number") {
72-
comment.range = [start, end];
73-
}
74-
75-
if (typeof startLoc === "object") {
76-
comment.loc = {
77-
start: startLoc,
78-
end: endLoc
79-
};
80-
}
81-
82-
return comment;
83-
}
84-
85-
/**
86-
* Returns line and column data for the given start and end positions,
87-
* for the given AST
88-
* @param {Object} start start data
89-
* @param {Object} end end data
90-
* @param {Object} ast the AST object
91-
* @returns {Object} the loc data
92-
*/
93-
function getLocFor(start, end, ast) {
94-
var startLoc = ast.getLineAndCharacterOfPosition(start),
95-
endLoc = ast.getLineAndCharacterOfPosition(end);
96-
97-
return {
98-
start: {
99-
line: startLoc.line + 1,
100-
column: startLoc.character
101-
},
102-
end: {
103-
line: endLoc.line + 1,
104-
column: endLoc.character
105-
}
106-
};
107-
}
108-
10955
//------------------------------------------------------------------------------
11056
// Parser
11157
//------------------------------------------------------------------------------
@@ -130,7 +76,6 @@ function parse(code, options) {
13076
if (typeof options !== "undefined") {
13177
extra.range = (typeof options.range === "boolean") && options.range;
13278
extra.loc = (typeof options.loc === "boolean") && options.loc;
133-
extra.attachComment = (typeof options.attachComment === "boolean") && options.attachComment;
13479

13580
if (extra.loc && options.source !== null && options.source !== undefined) {
13681
extra.source = toString(options.source);
@@ -146,10 +91,6 @@ function parse(code, options) {
14691
if (typeof options.tolerant === "boolean" && options.tolerant) {
14792
extra.errors = [];
14893
}
149-
if (extra.attachComment) {
150-
extra.range = true;
151-
extra.comments = [];
152-
}
15394

15495
if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
15596
// pass through jsx option
@@ -201,41 +142,7 @@ function parse(code, options) {
201142

202143
var ast = program.getSourceFile(FILENAME);
203144

204-
if (extra.attachComment || extra.comment) {
205-
/**
206-
* Create a TypeScript Scanner, with skipTrivia set to false so that
207-
* we can parse the comments
208-
*/
209-
var triviaScanner = ts.createScanner(ast.languageVersion, false, 0, code);
210-
211-
var kind = triviaScanner.scan();
212-
while (kind !== ts.SyntaxKind.EndOfFileToken) {
213-
if (kind !== ts.SyntaxKind.SingleLineCommentTrivia && kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
214-
kind = triviaScanner.scan();
215-
continue;
216-
}
217-
218-
var isBlock = (kind === ts.SyntaxKind.MultiLineCommentTrivia);
219-
var range = {
220-
pos: triviaScanner.getTokenPos(),
221-
end: triviaScanner.getTextPos(),
222-
kind: triviaScanner.getToken()
223-
};
224-
225-
var comment = code.substring(range.pos, range.end);
226-
var text = comment.replace("//", "").replace("/*", "").replace("*/", "");
227-
var loc = getLocFor(range.pos, range.end, ast);
228-
229-
var esprimaComment = convertTypeScriptCommentToEsprimaComment(isBlock, text, range.pos, range.end, loc.start, loc.end);
230-
extra.comments.push(esprimaComment);
231-
232-
kind = triviaScanner.scan();
233-
}
234-
235-
}
236-
237-
var convert = require("./lib/ast-converter");
238-
145+
extra.code = code;
239146
return convert(ast, extra);
240147
}
241148

0 commit comments

Comments
 (0)