mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-11 19:27:35 -06:00
Use charCodeChecked/codePointChecked throughout scanner
This commit is contained in:
parent
cd566bad95
commit
3e0da149cd
@ -144,7 +144,8 @@
|
||||
"local/no-in-operator": "error",
|
||||
"local/debug-assert": "error",
|
||||
"local/no-keywords": "error",
|
||||
"local/jsdoc-format": "error"
|
||||
"local/jsdoc-format": "error",
|
||||
"local/bounds-check": "error"
|
||||
},
|
||||
"overrides": [
|
||||
// By default, the ESLint CLI only looks at .js files. But, it will also look at
|
||||
|
||||
598
scripts/eslint/rules/bounds-check.cjs
Normal file
598
scripts/eslint/rules/bounds-check.cjs
Normal file
@ -0,0 +1,598 @@
|
||||
/** @import { TSESTree } from "@typescript-eslint/types" */
|
||||
const { AST_NODE_TYPES } = require("@typescript-eslint/utils");
|
||||
const { createRule } = require("./utils.cjs");
|
||||
|
||||
module.exports = createRule({
|
||||
name: "bounds-check",
|
||||
meta: {
|
||||
docs: {
|
||||
description: ``,
|
||||
},
|
||||
messages: {
|
||||
codePointUncheckedError: `Use 'codePointChecked' instead of 'codePointUnchecked'`,
|
||||
codePointAtError: `Use 'codePointChecked' instead of 'text.codePointAt'`,
|
||||
charCodeUncheckedError: `Use 'charCodeChecked' instead of 'charCodeUnchecked'`,
|
||||
charCodeAtError: `Use 'charCodeChecked' instead of 'text.charCodeAt'`,
|
||||
},
|
||||
schema: [],
|
||||
type: "problem",
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
if (!context.filename.endsWith("scanner.ts")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee.type === AST_NODE_TYPES.MemberExpression &&
|
||||
node.callee.object.type === AST_NODE_TYPES.Identifier &&
|
||||
node.callee.object.name === "text" &&
|
||||
!node.callee.computed
|
||||
) {
|
||||
const name = node.callee.property.name;
|
||||
switch (name) {
|
||||
case "charCodeAt":
|
||||
case "codePointAt": {
|
||||
/** @type {TSESTree.Node | undefined} */
|
||||
let n = node.parent;
|
||||
while (n) {
|
||||
if (n.type === AST_NODE_TYPES.FunctionDeclaration && n.id?.name === "createScanner") {
|
||||
context.report({ messageId: `${name}Error`, node: node.callee });
|
||||
break;
|
||||
}
|
||||
n = n.parent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.callee.type === AST_NODE_TYPES.Identifier) {
|
||||
switch (node.callee.name) {
|
||||
case "codePointUnchecked":
|
||||
case "charCodeUnchecked": {
|
||||
// perform a rudimentary test to determine if there is a definite bounds check for this node
|
||||
if (!hasDefiniteBoundsCheck(node)) {
|
||||
context.report({ messageId: `${node.callee.name}Error`, node: node.callee });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {TSESTree.CallExpression} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasDefiniteBoundsCheck(node) {
|
||||
if (node.arguments.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let index;
|
||||
let offset = 0;
|
||||
const arg = node.arguments[0];
|
||||
switch (arg.type) {
|
||||
case AST_NODE_TYPES.Identifier: {
|
||||
// pos
|
||||
index = arg.name;
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.BinaryExpression: {
|
||||
switch (arg.operator) {
|
||||
case "+":{
|
||||
const { left, right } = arg;
|
||||
if (
|
||||
left.type === AST_NODE_TYPES.Identifier &&
|
||||
right.type === AST_NODE_TYPES.Literal &&
|
||||
typeof right.value === "number"
|
||||
) {
|
||||
// pos + 1
|
||||
index = left.name;
|
||||
offset = right.value;
|
||||
}
|
||||
else if (
|
||||
right.type === AST_NODE_TYPES.Identifier &&
|
||||
left.type === AST_NODE_TYPES.Literal &&
|
||||
typeof left.value === "number"
|
||||
) {
|
||||
// 1 + pos
|
||||
index = right.name;
|
||||
offset = left.value;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "-": {
|
||||
const { left, right } = arg;
|
||||
if (
|
||||
left.type === AST_NODE_TYPES.Identifier &&
|
||||
right.type === AST_NODE_TYPES.Literal &&
|
||||
typeof right.value === "number"
|
||||
) {
|
||||
// pos - 1
|
||||
index = left.name;
|
||||
offset = -right.value;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {TSESTree.Node} */
|
||||
let n = node;
|
||||
let negated = false;
|
||||
while (n.parent) {
|
||||
switch (n.parent.type) {
|
||||
case AST_NODE_TYPES.UnaryExpression: {
|
||||
if (n.parent.operator === "!") {
|
||||
negated = !negated;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.LogicalExpression: {
|
||||
switch (n.parent.operator) {
|
||||
case "&&": {
|
||||
const { left, right } = n.parent;
|
||||
switch (n) {
|
||||
case left: {
|
||||
break;
|
||||
}
|
||||
case right: {
|
||||
if (inBounds(left, index, offset, negated)) {
|
||||
return true;
|
||||
}
|
||||
if (containsSideEffect(left, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "||":
|
||||
case "??": {
|
||||
const { left, right } = n.parent;
|
||||
switch (n) {
|
||||
case left: {
|
||||
break;
|
||||
}
|
||||
case right: {
|
||||
if (inBounds(left, index, offset, !negated)) {
|
||||
return true;
|
||||
}
|
||||
if (containsSideEffect(left, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.ConditionalExpression: {
|
||||
const { test, consequent, alternate } = n.parent;
|
||||
switch (n) {
|
||||
case test: {
|
||||
break;
|
||||
}
|
||||
case consequent: {
|
||||
if (inBounds(test, index, offset, negated)) {
|
||||
return true;
|
||||
}
|
||||
if (containsSideEffect(test, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case alternate: {
|
||||
if (inBounds(test, index, offset, !negated)) {
|
||||
return true;
|
||||
}
|
||||
if (containsSideEffect(test, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.BinaryExpression:
|
||||
case AST_NODE_TYPES.AssignmentExpression: {
|
||||
const { left, right } = n.parent;
|
||||
switch (n) {
|
||||
case left: {
|
||||
break;
|
||||
}
|
||||
case right: {
|
||||
if (containsSideEffect(left, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.SequenceExpression: {
|
||||
const { expressions } = n.parent;
|
||||
const i = expressions.indexOf(/** @type {TSESTree.Expression} */(n));
|
||||
if (expressions.slice(0, i).some(n => containsSideEffect(n, index))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.CallExpression: {
|
||||
const args = n.parent.arguments;
|
||||
const i = args.indexOf(/** @type {*} */(n));
|
||||
if (args.slice(0, i).some(n => containsSideEffect(n, index))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.WhileStatement: {
|
||||
const { test, body } = n.parent;
|
||||
switch (n) {
|
||||
case test: {
|
||||
break;
|
||||
}
|
||||
case body: {
|
||||
if (inBounds(test, index, offset, negated)) {
|
||||
return true;
|
||||
}
|
||||
if (containsSideEffect(test, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.IfStatement: {
|
||||
const { test, consequent, alternate } = n.parent;
|
||||
switch (n) {
|
||||
case test: {
|
||||
break;
|
||||
}
|
||||
case consequent: {
|
||||
if (inBounds(test, index, offset, negated)) {
|
||||
return true;
|
||||
}
|
||||
if (containsSideEffect(test, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case alternate: {
|
||||
if (inBounds(test, index, offset, !negated)) {
|
||||
return true;
|
||||
}
|
||||
if (containsSideEffect(test, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.BlockStatement: {
|
||||
const [first] = n.parent.body;
|
||||
switch (n) {
|
||||
case first: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.ExpressionStatement: {
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.VariableDeclarator: {
|
||||
const { id, init } = n.parent;
|
||||
switch (n) {
|
||||
case /** @type {TSESTree.Node} */(id):
|
||||
break;
|
||||
case init:
|
||||
if (containsSideEffect(n, index)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.VariableDeclaration: {
|
||||
const declarations = /** @type {readonly TSESTree.Node[]} */(n.parent.declarations);
|
||||
const i = declarations.indexOf(n);
|
||||
if (declarations.slice(0, i).some(n => containsSideEffect(n, index))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
n = n.parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TSESTree.Node} node
|
||||
* @param {string} index
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function containsSideEffect(node, index) {
|
||||
/**
|
||||
* @param {TSESTree.Node} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function contains(node) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.PrivateIdentifier:
|
||||
case AST_NODE_TYPES.Literal:
|
||||
case AST_NODE_TYPES.Identifier: {
|
||||
return false;
|
||||
}
|
||||
case AST_NODE_TYPES.MemberExpression: {
|
||||
const { object, computed, property } = node;
|
||||
return contains(object) || computed && contains(property);
|
||||
}
|
||||
case AST_NODE_TYPES.UpdateExpression: {
|
||||
const { argument } = node;
|
||||
return argument.type === AST_NODE_TYPES.Identifier && (argument.name === index || argument.name === "end");
|
||||
}
|
||||
case AST_NODE_TYPES.AssignmentExpression: {
|
||||
const { left, right } = node;
|
||||
return left.type === AST_NODE_TYPES.Identifier && (left.name === index || left.name === "end") || contains(right);
|
||||
}
|
||||
case AST_NODE_TYPES.CallExpression: {
|
||||
const { callee, arguments: args } = node;
|
||||
if (callee.type === AST_NODE_TYPES.Identifier) {
|
||||
switch (callee.name) {
|
||||
case "codePointChecked":
|
||||
case "codePointUnchecked":
|
||||
case "charCodeChecked":
|
||||
case "charCodeUnchecked": {
|
||||
return args.some(contains);
|
||||
}
|
||||
}
|
||||
|
||||
// assumes functions named `is*` do not cause side effects
|
||||
if (/^is[A-Z]/.test(callee.name)) {
|
||||
return args.some(contains);
|
||||
}
|
||||
}
|
||||
break; // assume every other call introduces potential side effects
|
||||
}
|
||||
case AST_NODE_TYPES.LogicalExpression:
|
||||
case AST_NODE_TYPES.BinaryExpression: {
|
||||
const { left, right } = node;
|
||||
return contains(left) || contains(right);
|
||||
}
|
||||
case AST_NODE_TYPES.ConditionalExpression: {
|
||||
const { test, consequent, alternate } = node;
|
||||
return contains(test) || contains(consequent) || contains(alternate);
|
||||
}
|
||||
case AST_NODE_TYPES.UnaryExpression: {
|
||||
return contains(node.argument);
|
||||
}
|
||||
case AST_NODE_TYPES.SequenceExpression: {
|
||||
return node.expressions.some(contains);
|
||||
}
|
||||
case AST_NODE_TYPES.VariableDeclaration: {
|
||||
return node.declarations.some(contains);
|
||||
}
|
||||
case AST_NODE_TYPES.VariableDeclarator: {
|
||||
const { id, init } = node;
|
||||
return contains(id) || !!init && contains(init);
|
||||
}
|
||||
}
|
||||
return true; // assume everything else has potential side effects
|
||||
}
|
||||
|
||||
return contains(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {"in-bounds" | "out-of-bounds" | false} Bounds
|
||||
*
|
||||
* @param {TSESTree.Expression} node
|
||||
* @param {string} index
|
||||
* @param {number} offset
|
||||
* @param {boolean} negated
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function inBounds(node, index, offset, negated) {
|
||||
const result = checkBounds(node, index, offset);
|
||||
return result === (negated ? "out-of-bounds" : "in-bounds");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TSESTree.Expression} node
|
||||
* @param {string} index
|
||||
* @param {number} offset
|
||||
* @returns {Bounds}
|
||||
*/
|
||||
function checkBounds(node, index, offset) {
|
||||
/**
|
||||
* @param {TSESTree.Node} node
|
||||
* @returns {Bounds}
|
||||
*/
|
||||
function check(node) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.UnaryExpression: {
|
||||
const { operator, argument } = node;
|
||||
if (operator === "!") {
|
||||
const result = check(argument);
|
||||
if (result === "in-bounds") return "out-of-bounds";
|
||||
if (result === "out-of-bounds") return "in-bounds";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.LogicalExpression: {
|
||||
const { left, operator, right } = node;
|
||||
if (operator === "&&") {
|
||||
const first = check(left);
|
||||
if (first === "in-bounds") return "in-bounds";
|
||||
const second = check(right);
|
||||
if (second === "in-bounds") return "in-bounds";
|
||||
return first || second;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.BinaryExpression: {
|
||||
const { left, operator, right } = node;
|
||||
switch (operator) {
|
||||
case "<": {
|
||||
switch (true) {
|
||||
case isBound(left, index, offset, op.GE) && isBound(right, "end"):
|
||||
case isBound(left, index) && isBound(right, "end", -offset, op.LE):
|
||||
return "in-bounds";
|
||||
case isBound(left, "end", -offset, op.LE) && isBound(right, index):
|
||||
case isBound(left, "end") && isBound(right, index, offset, op.GE):
|
||||
return "out-of-bounds";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ">": {
|
||||
const { left, right } = node;
|
||||
switch (true) {
|
||||
case isBound(left, index, offset, op.GE) && isBound(right, "end"):
|
||||
case isBound(left, index) && isBound(right, "end", -offset, op.LE):
|
||||
return "out-of-bounds";
|
||||
case isBound(left, "end", -offset, op.LE) && isBound(right, index):
|
||||
case isBound(left, "end") && isBound(right, index, offset, op.GE):
|
||||
return "in-bounds";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "<=": {
|
||||
const { left, right } = node;
|
||||
switch (true) {
|
||||
case isBound(right, index, offset) && isBound(left, "end"):
|
||||
case isBound(right, index) && isBound(left, "end", -offset):
|
||||
return "out-of-bounds";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ">=": {
|
||||
const { left, right } = node;
|
||||
switch (true) {
|
||||
case isBound(left, index, offset) && isBound(right, "end"):
|
||||
case isBound(left, index) && isBound(right, "end", -offset):
|
||||
return "out-of-bounds";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_NODE_TYPES.SequenceExpression: {
|
||||
return check(node.expressions[node.expressions.length - 1]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (containsSideEffect(node, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return check(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TSESTree.Node} node
|
||||
* @param {string} name
|
||||
* @param {number} offset
|
||||
* @param {(a: number, b: number) => boolean} [cmp]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isBound(node, name, offset = 0, cmp = op.EQ) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.Identifier: {
|
||||
return node.name === name && offset === 0;
|
||||
}
|
||||
case AST_NODE_TYPES.BinaryExpression: {
|
||||
const { left, operator, right } = node;
|
||||
switch (operator) {
|
||||
case "+": {
|
||||
if (left.type === AST_NODE_TYPES.Identifier) {
|
||||
return left.name === name && right.type === AST_NODE_TYPES.Literal && typeof right.value === "number" && cmp(right.value, offset);
|
||||
}
|
||||
if (right.type === AST_NODE_TYPES.Identifier) {
|
||||
return right.name === name && left.type === AST_NODE_TYPES.Literal && typeof left.value === "number" && cmp(left.value, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "-": {
|
||||
if (left.type === AST_NODE_TYPES.Identifier) {
|
||||
switch (cmp) {
|
||||
case op.GE:
|
||||
cmp = op.LE;
|
||||
break;
|
||||
case op.LE:
|
||||
cmp = op.GE;
|
||||
break;
|
||||
}
|
||||
return left.name === name && right.type === AST_NODE_TYPES.Literal && typeof right.value === "number" && cmp(right.value, -offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const op = {
|
||||
/**
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
*/
|
||||
EQ(a, b) { return a === b; },
|
||||
|
||||
/**
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
*/
|
||||
LE(a, b) { return a <= b; },
|
||||
|
||||
/**
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
*/
|
||||
GE(a, b) { return a >= b; },
|
||||
};
|
||||
|
||||
203
scripts/eslint/tests/bounds-check.test.cjs
Normal file
203
scripts/eslint/tests/bounds-check.test.cjs
Normal file
@ -0,0 +1,203 @@
|
||||
const { RuleTester } = require("./support/RuleTester.cjs");
|
||||
const rule = require("../rules/bounds-check.cjs");
|
||||
|
||||
const ruleTester = new RuleTester({
|
||||
parserOptions: {
|
||||
warnOnUnsupportedTypeScriptVersion: false,
|
||||
},
|
||||
parser: require.resolve("@typescript-eslint/parser"),
|
||||
});
|
||||
|
||||
ruleTester.run("bounds-check", rule, {
|
||||
valid: [
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `codePointChecked(0)`,
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `charCodeChecked(0)`,
|
||||
},
|
||||
{
|
||||
filename: "foo.ts",
|
||||
code: `codePointUnchecked(0)`,
|
||||
},
|
||||
{
|
||||
filename: "foo.ts",
|
||||
code: `charCodeUnchecked(0)`,
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos < end && charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "!(pos >= end) && charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos + 1 < end && charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos >= end || charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos < end ? charCodeUnchecked(pos) : null"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos >= end ? null : charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos < end) charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos < end) {charCodeUnchecked(pos)}"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos >= end); else charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos >= end); else {charCodeUnchecked(pos)}"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "while (pos < end) charCodeUnchecked(pos)"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "while (pos < end) {charCodeUnchecked(pos)}"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "return pos >= 0 && pos < end ? codePointUnchecked(pos) : CharacterCodes.EOF;"
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `
|
||||
if (
|
||||
pos + 2 < end &&
|
||||
codePointUnchecked(pos + 1) === CharacterCodes.u &&
|
||||
codePointUnchecked(pos + 2) === CharacterCodes.openBrace
|
||||
) {}
|
||||
`
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `
|
||||
while (pos < end) {
|
||||
let ch = codePointUnchecked(pos);
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `
|
||||
while (pos < end && isWhiteSpaceSingleLine(charCodeUnchecked(pos)) && isWhiteSpaceSingleLine(charCodeUnchecked(pos))) {}`
|
||||
},
|
||||
],
|
||||
|
||||
invalid: [
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `codePointUnchecked(0)`,
|
||||
errors: [{ messageId: "codePointUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `charCodeUnchecked(0)`,
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `pos < end && charCodeUnchecked(pos + 1)`,
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `!(pos < end) && charCodeUnchecked(pos)`,
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `pos < end || charCodeUnchecked(pos)`,
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `pos - 1 < end && charCodeUnchecked(pos)`,
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: `pos < end + 1 && charCodeUnchecked(pos)`,
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos >= end ? charCodeUnchecked(pos) : null",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos < end ? null : charCodeUnchecked(pos)",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos >= end) charCodeUnchecked(pos)",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos >= end) {charCodeUnchecked(pos)}",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos < end); else charCodeUnchecked(pos)",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "if (pos < end); else {charCodeUnchecked(pos)}",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "while (pos >= end) charCodeUnchecked(pos)",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "while (pos >= end) {charCodeUnchecked(pos)}",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "pos < end && (pos++ || charCodeUnchecked(pos))",
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: "function createScanner() { text.charCodeAt(pos) }",
|
||||
errors: [{ messageId: "charCodeAtError" }],
|
||||
},
|
||||
{
|
||||
filename: "scanner.ts",
|
||||
code: String.raw`
|
||||
if (
|
||||
isRegularExpression && shouldEmitInvalidEscapeError && escapedValue >= 0xD800 && escapedValue <= 0xDBFF &&
|
||||
pos + 6 < end && text.substring(pos, pos + 2) === "\\u" && charCodeUnchecked(pos + 2) !== CharacterCodes.openBrace
|
||||
) {}
|
||||
`,
|
||||
errors: [{ messageId: "charCodeUncheckedError" }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -1106,7 +1106,8 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
* function does not perform bounds checks.
|
||||
*/
|
||||
function codePointUnchecked(pos: number) {
|
||||
return codePointAt(text, pos);
|
||||
// eslint-disable-next-line local/bounds-check -- intentionally unsafe call
|
||||
return text.codePointAt(pos)!; // Using `!` as we assume you have already checked bounds before calling.
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1123,6 +1124,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
* function does not perform bounds checks.
|
||||
*/
|
||||
function charCodeUnchecked(pos: number) {
|
||||
// eslint-disable-next-line local/bounds-check -- intentionally unsafe call
|
||||
return text.charCodeAt(pos);
|
||||
}
|
||||
|
||||
@ -1151,7 +1153,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
let isPreviousTokenSeparator = false;
|
||||
let result = "";
|
||||
while (true) {
|
||||
const ch = charCodeUnchecked(pos);
|
||||
const ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes._) {
|
||||
tokenFlags |= TokenFlags.ContainsSeparator;
|
||||
if (allowSeparator) {
|
||||
@ -1172,15 +1174,14 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
start = pos;
|
||||
continue;
|
||||
}
|
||||
if (isDigit(ch)) {
|
||||
allowSeparator = true;
|
||||
isPreviousTokenSeparator = false;
|
||||
pos++;
|
||||
continue;
|
||||
if (!isDigit(ch)) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
allowSeparator = true;
|
||||
isPreviousTokenSeparator = false;
|
||||
pos++;
|
||||
}
|
||||
if (charCodeUnchecked(pos - 1) === CharacterCodes._) {
|
||||
if (charCodeChecked(pos - 1) === CharacterCodes._) {
|
||||
tokenFlags |= TokenFlags.ContainsInvalidSeparator;
|
||||
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1);
|
||||
}
|
||||
@ -1210,9 +1211,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
function scanNumber(): SyntaxKind {
|
||||
let start = pos;
|
||||
let mainFragment: string;
|
||||
// eslint-disable-next-line local/bounds-check -- all call sites should perform bounds checks
|
||||
if (charCodeUnchecked(pos) === CharacterCodes._0) {
|
||||
pos++;
|
||||
if (charCodeUnchecked(pos) === CharacterCodes._) {
|
||||
if (charCodeChecked(pos) === CharacterCodes._) {
|
||||
tokenFlags |= TokenFlags.ContainsSeparator | TokenFlags.ContainsInvalidSeparator;
|
||||
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1);
|
||||
// treat it as a normal number literal
|
||||
@ -1246,15 +1248,17 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
let decimalFragment: string | undefined;
|
||||
let scientificFragment: string | undefined;
|
||||
if (charCodeUnchecked(pos) === CharacterCodes.dot) {
|
||||
if (charCodeChecked(pos) === CharacterCodes.dot) {
|
||||
pos++;
|
||||
decimalFragment = scanNumberFragment();
|
||||
}
|
||||
let end = pos;
|
||||
if (charCodeUnchecked(pos) === CharacterCodes.E || charCodeUnchecked(pos) === CharacterCodes.e) {
|
||||
let ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes.E || ch === CharacterCodes.e) {
|
||||
pos++;
|
||||
tokenFlags |= TokenFlags.Scientific;
|
||||
if (charCodeUnchecked(pos) === CharacterCodes.plus || charCodeUnchecked(pos) === CharacterCodes.minus) pos++;
|
||||
ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes.plus || ch === CharacterCodes.minus) pos++;
|
||||
const preNumericPart = pos;
|
||||
const finalFragment = scanNumberFragment();
|
||||
if (!finalFragment) {
|
||||
@ -1301,7 +1305,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
|
||||
function checkForIdentifierStartAfterNumericLiteral(numericStart: number, isScientific?: boolean) {
|
||||
if (!isIdentifierStart(codePointUnchecked(pos), languageVersion)) {
|
||||
if (!isIdentifierStart(codePointChecked(pos), languageVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1325,10 +1329,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
function scanDigits(): boolean {
|
||||
const start = pos;
|
||||
let isOctal = true;
|
||||
while (isDigit(charCodeChecked(pos))) {
|
||||
if (!isOctalDigit(charCodeUnchecked(pos))) {
|
||||
isOctal = false;
|
||||
}
|
||||
while (true) {
|
||||
const ch = charCodeChecked(pos);
|
||||
if (!isDigit(ch)) break;
|
||||
isOctal &&= isOctalDigit(ch);
|
||||
pos++;
|
||||
}
|
||||
tokenValue = text.substring(start, pos);
|
||||
@ -1357,7 +1361,8 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
let allowSeparator = false;
|
||||
let isPreviousTokenSeparator = false;
|
||||
while (valueChars.length < minCount || scanAsManyAsPossible) {
|
||||
let ch = charCodeUnchecked(pos);
|
||||
let ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes.EOF) break;
|
||||
if (canHaveSeparators && ch === CharacterCodes._) {
|
||||
tokenFlags |= TokenFlags.ContainsSeparator;
|
||||
if (allowSeparator) {
|
||||
@ -1390,25 +1395,26 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
if (valueChars.length < minCount) {
|
||||
valueChars = [];
|
||||
}
|
||||
if (charCodeUnchecked(pos - 1) === CharacterCodes._) {
|
||||
if (charCodeChecked(pos - 1) === CharacterCodes._) {
|
||||
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1);
|
||||
}
|
||||
return String.fromCharCode(...valueChars);
|
||||
}
|
||||
|
||||
function scanString(jsxAttributeString = false): string {
|
||||
// eslint-disable-next-line -- all callers should perform bounds check
|
||||
const quote = charCodeUnchecked(pos);
|
||||
pos++;
|
||||
let result = "";
|
||||
let start = pos;
|
||||
while (true) {
|
||||
if (pos >= end) {
|
||||
const ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes.EOF) {
|
||||
result += text.substring(start, pos);
|
||||
tokenFlags |= TokenFlags.Unterminated;
|
||||
error(Diagnostics.Unterminated_string_literal);
|
||||
break;
|
||||
}
|
||||
const ch = charCodeUnchecked(pos);
|
||||
if (ch === quote) {
|
||||
result += text.substring(start, pos);
|
||||
pos++;
|
||||
@ -1437,6 +1443,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
* a literal component of a TemplateExpression.
|
||||
*/
|
||||
function scanTemplateAndSetTokenValue(shouldEmitInvalidEscapeError: boolean): SyntaxKind {
|
||||
// eslint-disable-next-line local/bounds-check -- all callers should have performed bounds check
|
||||
const startedWithBacktick = charCodeUnchecked(pos) === CharacterCodes.backtick;
|
||||
|
||||
pos++;
|
||||
@ -1445,7 +1452,9 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
let resultingToken: SyntaxKind;
|
||||
|
||||
while (true) {
|
||||
if (pos >= end) {
|
||||
const ch = charCodeChecked(pos);
|
||||
|
||||
if (ch === CharacterCodes.EOF) {
|
||||
contents += text.substring(start, pos);
|
||||
tokenFlags |= TokenFlags.Unterminated;
|
||||
error(Diagnostics.Unterminated_template_literal);
|
||||
@ -1453,10 +1462,8 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
break;
|
||||
}
|
||||
|
||||
const currChar = charCodeUnchecked(pos);
|
||||
|
||||
// '`'
|
||||
if (currChar === CharacterCodes.backtick) {
|
||||
if (ch === CharacterCodes.backtick) {
|
||||
contents += text.substring(start, pos);
|
||||
pos++;
|
||||
resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
|
||||
@ -1464,7 +1471,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
|
||||
// '${'
|
||||
if (currChar === CharacterCodes.$ && pos + 1 < end && charCodeUnchecked(pos + 1) === CharacterCodes.openBrace) {
|
||||
if (ch === CharacterCodes.$ && charCodeChecked(pos + 1) === CharacterCodes.openBrace) {
|
||||
contents += text.substring(start, pos);
|
||||
pos += 2;
|
||||
resultingToken = startedWithBacktick ? SyntaxKind.TemplateHead : SyntaxKind.TemplateMiddle;
|
||||
@ -1472,7 +1479,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
|
||||
// Escape character
|
||||
if (currChar === CharacterCodes.backslash) {
|
||||
if (ch === CharacterCodes.backslash) {
|
||||
contents += text.substring(start, pos);
|
||||
contents += scanEscapeSequence(shouldEmitInvalidEscapeError, /*isRegularExpression*/ false);
|
||||
start = pos;
|
||||
@ -1481,11 +1488,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
|
||||
// Speculated ECMAScript 6 Spec 11.8.6.1:
|
||||
// <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for Template Values
|
||||
if (currChar === CharacterCodes.carriageReturn) {
|
||||
if (ch === CharacterCodes.carriageReturn) {
|
||||
contents += text.substring(start, pos);
|
||||
pos++;
|
||||
|
||||
if (pos < end && charCodeUnchecked(pos) === CharacterCodes.lineFeed) {
|
||||
if (charCodeChecked(pos) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
@ -1520,17 +1527,17 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
function scanEscapeSequence(shouldEmitInvalidEscapeError: boolean, isRegularExpression: boolean | "annex-b"): string {
|
||||
const start = pos;
|
||||
pos++;
|
||||
if (pos >= end) {
|
||||
const ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes.EOF) {
|
||||
error(Diagnostics.Unexpected_end_of_text);
|
||||
return "";
|
||||
}
|
||||
const ch = charCodeUnchecked(pos);
|
||||
pos++;
|
||||
switch (ch) {
|
||||
case CharacterCodes._0:
|
||||
// Although '0' preceding any digit is treated as LegacyOctalEscapeSequence,
|
||||
// '\08' should separately be interpreted as '\0' + '8'.
|
||||
if (pos >= end || !isDigit(charCodeUnchecked(pos))) {
|
||||
if (!isDigit(charCodeChecked(pos))) {
|
||||
return "\0";
|
||||
}
|
||||
// '\01', '\011'
|
||||
@ -1539,7 +1546,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
case CharacterCodes._2:
|
||||
case CharacterCodes._3:
|
||||
// '\1', '\17', '\177'
|
||||
if (pos < end && isOctalDigit(charCodeUnchecked(pos))) {
|
||||
if (isOctalDigit(charCodeChecked(pos))) {
|
||||
pos++;
|
||||
}
|
||||
// '\17', '\177'
|
||||
@ -1549,7 +1556,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
case CharacterCodes._6:
|
||||
case CharacterCodes._7:
|
||||
// '\4', '\47' but not '\477'
|
||||
if (pos < end && isOctalDigit(charCodeUnchecked(pos))) {
|
||||
if (isOctalDigit(charCodeChecked(pos))) {
|
||||
pos++;
|
||||
}
|
||||
// '\47'
|
||||
@ -1590,7 +1597,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
case CharacterCodes.u:
|
||||
if (
|
||||
(!isRegularExpression || shouldEmitInvalidEscapeError) &&
|
||||
pos < end && charCodeUnchecked(pos) === CharacterCodes.openBrace
|
||||
charCodeChecked(pos) === CharacterCodes.openBrace
|
||||
) {
|
||||
// '\u{DDDDDD}'
|
||||
pos -= 2;
|
||||
@ -1598,7 +1605,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
// '\uDDDD'
|
||||
for (; pos < start + 6; pos++) {
|
||||
if (!(pos < end && isHexDigit(charCodeUnchecked(pos)))) {
|
||||
if (!isHexDigit(charCodeChecked(pos))) {
|
||||
tokenFlags |= TokenFlags.ContainsInvalidEscape;
|
||||
if (isRegularExpression || shouldEmitInvalidEscapeError) {
|
||||
error(Diagnostics.Hexadecimal_digit_expected);
|
||||
@ -1611,6 +1618,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
const escapedValueString = String.fromCharCode(escapedValue);
|
||||
if (
|
||||
isRegularExpression && shouldEmitInvalidEscapeError && escapedValue >= 0xD800 && escapedValue <= 0xDBFF &&
|
||||
// eslint-disable-next-line local/bounds-check -- bounds checked before invocation
|
||||
pos + 6 < end && text.substring(pos, pos + 2) === "\\u" && charCodeUnchecked(pos + 2) !== CharacterCodes.openBrace
|
||||
) {
|
||||
// For regular expressions in Unicode mode, \u HexLeadSurrogate \u HexTrailSurrogate is treated as a single character
|
||||
@ -1619,6 +1627,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
const nextStart = pos;
|
||||
let nextPos = pos + 2;
|
||||
for (; nextPos < nextStart + 6; nextPos++) {
|
||||
// eslint-disable-next-line local/bounds-check -- bounds checked before invocation
|
||||
if (!isHexDigit(charCodeUnchecked(pos))) {
|
||||
// leave the error to the next call
|
||||
return escapedValueString;
|
||||
@ -1727,7 +1736,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
|
||||
function peekExtendedUnicodeEscape(): number {
|
||||
if (codePointUnchecked(pos + 1) === CharacterCodes.u && codePointUnchecked(pos + 2) === CharacterCodes.openBrace) {
|
||||
if (
|
||||
pos + 2 < end &&
|
||||
codePointUnchecked(pos + 1) === CharacterCodes.u &&
|
||||
codePointUnchecked(pos + 2) === CharacterCodes.openBrace
|
||||
) {
|
||||
const start = pos;
|
||||
pos += 3;
|
||||
const escapedValueString = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false);
|
||||
@ -1794,7 +1807,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
let separatorAllowed = false;
|
||||
let isPreviousTokenSeparator = false;
|
||||
while (true) {
|
||||
const ch = charCodeUnchecked(pos);
|
||||
const ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes.EOF) {
|
||||
break;
|
||||
}
|
||||
// Numeric separators are allowed anywhere within a numeric literal, except not at the beginning, or following another separator
|
||||
if (ch === CharacterCodes._) {
|
||||
tokenFlags |= TokenFlags.ContainsSeparator;
|
||||
@ -1819,7 +1835,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
pos++;
|
||||
isPreviousTokenSeparator = false;
|
||||
}
|
||||
if (charCodeUnchecked(pos - 1) === CharacterCodes._) {
|
||||
if (charCodeChecked(pos - 1) === CharacterCodes._) {
|
||||
// Literal ends with underscore - not allowed
|
||||
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1);
|
||||
}
|
||||
@ -1827,7 +1843,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
|
||||
function checkBigIntSuffix(): SyntaxKind {
|
||||
if (charCodeUnchecked(pos) === CharacterCodes.n) {
|
||||
if (charCodeChecked(pos) === CharacterCodes.n) {
|
||||
tokenValue += "n";
|
||||
// Use base 10 instead of base 2 or base 8 for shorter literals
|
||||
if (tokenFlags & TokenFlags.BinaryOrOctalSpecifier) {
|
||||
@ -1854,11 +1870,12 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
let asteriskSeen = false;
|
||||
while (true) {
|
||||
tokenStart = pos;
|
||||
if (pos >= end) {
|
||||
|
||||
const ch = codePointChecked(pos);
|
||||
if (ch === CharacterCodes.EOF) {
|
||||
return token = SyntaxKind.EndOfFileToken;
|
||||
}
|
||||
|
||||
const ch = codePointUnchecked(pos);
|
||||
if (pos === 0) {
|
||||
// Special handling for shebang
|
||||
if (ch === CharacterCodes.hash && isShebangTrivia(text, pos)) {
|
||||
@ -1923,8 +1940,8 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
return token = SyntaxKind.WhitespaceTrivia;
|
||||
}
|
||||
case CharacterCodes.exclamation:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.ExclamationEqualsEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.ExclamationEqualsToken;
|
||||
@ -1938,19 +1955,19 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
case CharacterCodes.backtick:
|
||||
return token = scanTemplateAndSetTokenValue(/*shouldEmitInvalidEscapeError*/ false);
|
||||
case CharacterCodes.percent:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.PercentEqualsToken;
|
||||
}
|
||||
pos++;
|
||||
return token = SyntaxKind.PercentToken;
|
||||
case CharacterCodes.ampersand:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.ampersand) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.ampersand) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.AmpersandAmpersandEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.AmpersandAmpersandToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.AmpersandEqualsToken;
|
||||
}
|
||||
pos++;
|
||||
@ -1962,11 +1979,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
pos++;
|
||||
return token = SyntaxKind.CloseParenToken;
|
||||
case CharacterCodes.asterisk:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.AsteriskEqualsToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.asterisk) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.asterisk) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.AsteriskAsteriskEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.AsteriskAsteriskToken;
|
||||
@ -1979,10 +1996,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
return token = SyntaxKind.AsteriskToken;
|
||||
case CharacterCodes.plus:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.plus) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.plus) {
|
||||
return pos += 2, token = SyntaxKind.PlusPlusToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.PlusEqualsToken;
|
||||
}
|
||||
pos++;
|
||||
@ -1991,27 +2008,27 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
pos++;
|
||||
return token = SyntaxKind.CommaToken;
|
||||
case CharacterCodes.minus:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.minus) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.minus) {
|
||||
return pos += 2, token = SyntaxKind.MinusMinusToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.MinusEqualsToken;
|
||||
}
|
||||
pos++;
|
||||
return token = SyntaxKind.MinusToken;
|
||||
case CharacterCodes.dot:
|
||||
if (isDigit(charCodeUnchecked(pos + 1))) {
|
||||
if (isDigit(charCodeChecked(pos + 1))) {
|
||||
scanNumber();
|
||||
return token = SyntaxKind.NumericLiteral;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.dot && charCodeUnchecked(pos + 2) === CharacterCodes.dot) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.dot && charCodeChecked(pos + 2) === CharacterCodes.dot) {
|
||||
return pos += 3, token = SyntaxKind.DotDotDotToken;
|
||||
}
|
||||
pos++;
|
||||
return token = SyntaxKind.DotToken;
|
||||
case CharacterCodes.slash:
|
||||
// Single-line comment
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.slash) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
|
||||
while (pos < end) {
|
||||
@ -2036,16 +2053,16 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
}
|
||||
// Multi-line comment
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.asterisk) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.asterisk) {
|
||||
pos += 2;
|
||||
const isJSDoc = charCodeUnchecked(pos) === CharacterCodes.asterisk && charCodeUnchecked(pos + 1) !== CharacterCodes.slash;
|
||||
const isJSDoc = charCodeChecked(pos) === CharacterCodes.asterisk && charCodeChecked(pos + 1) !== CharacterCodes.slash;
|
||||
|
||||
let commentClosed = false;
|
||||
let lastLineStart = tokenStart;
|
||||
while (pos < end) {
|
||||
const ch = charCodeUnchecked(pos);
|
||||
|
||||
if (ch === CharacterCodes.asterisk && charCodeUnchecked(pos + 1) === CharacterCodes.slash) {
|
||||
if (ch === CharacterCodes.asterisk && charCodeChecked(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
commentClosed = true;
|
||||
break;
|
||||
@ -2080,7 +2097,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
}
|
||||
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.SlashEqualsToken;
|
||||
}
|
||||
|
||||
@ -2149,19 +2166,19 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
}
|
||||
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.lessThan) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.lessThan) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.LessThanLessThanEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.LessThanLessThanToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.LessThanEqualsToken;
|
||||
}
|
||||
if (
|
||||
languageVariant === LanguageVariant.JSX &&
|
||||
charCodeUnchecked(pos + 1) === CharacterCodes.slash &&
|
||||
charCodeUnchecked(pos + 2) !== CharacterCodes.asterisk
|
||||
charCodeChecked(pos + 1) === CharacterCodes.slash &&
|
||||
charCodeChecked(pos + 2) !== CharacterCodes.asterisk
|
||||
) {
|
||||
return pos += 2, token = SyntaxKind.LessThanSlashToken;
|
||||
}
|
||||
@ -2178,13 +2195,13 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
}
|
||||
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.EqualsEqualsEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.EqualsEqualsToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.greaterThan) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.greaterThan) {
|
||||
return pos += 2, token = SyntaxKind.EqualsGreaterThanToken;
|
||||
}
|
||||
pos++;
|
||||
@ -2203,11 +2220,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
pos++;
|
||||
return token = SyntaxKind.GreaterThanToken;
|
||||
case CharacterCodes.question:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.dot && !isDigit(charCodeUnchecked(pos + 2))) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.dot && !isDigit(charCodeChecked(pos + 2))) {
|
||||
return pos += 2, token = SyntaxKind.QuestionDotToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.question) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.question) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.QuestionQuestionEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.QuestionQuestionToken;
|
||||
@ -2221,7 +2238,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
pos++;
|
||||
return token = SyntaxKind.CloseBracketToken;
|
||||
case CharacterCodes.caret:
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.CaretEqualsToken;
|
||||
}
|
||||
pos++;
|
||||
@ -2240,13 +2257,13 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
}
|
||||
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.bar) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.bar) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.BarBarEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.BarBarToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.BarEqualsToken;
|
||||
}
|
||||
pos++;
|
||||
@ -2285,7 +2302,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
return token = SyntaxKind.Unknown;
|
||||
}
|
||||
|
||||
const charAfterHash = codePointUnchecked(pos + 1);
|
||||
const charAfterHash = codePointChecked(pos + 1);
|
||||
if (charAfterHash === CharacterCodes.backslash) {
|
||||
pos++;
|
||||
const extendedCookedChar = peekExtendedUnicodeEscape();
|
||||
@ -2370,7 +2387,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
Debug.assert(token === SyntaxKind.Unknown, "'reScanInvalidIdentifier' should only be called when the current token is 'SyntaxKind.Unknown'.");
|
||||
pos = tokenStart = fullStartPos;
|
||||
tokenFlags = 0;
|
||||
const ch = codePointUnchecked(pos);
|
||||
const ch = codePointChecked(pos);
|
||||
const identifierKind = scanIdentifier(ch, ScriptTarget.ESNext);
|
||||
if (identifierKind) {
|
||||
return token = identifierKind;
|
||||
@ -2394,20 +2411,20 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
|
||||
function reScanGreaterToken(): SyntaxKind {
|
||||
if (token === SyntaxKind.GreaterThanToken) {
|
||||
if (charCodeUnchecked(pos) === CharacterCodes.greaterThan) {
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.greaterThan) {
|
||||
if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos) === CharacterCodes.greaterThan) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.greaterThan) {
|
||||
if (charCodeChecked(pos + 2) === CharacterCodes.equals) {
|
||||
return pos += 3, token = SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken;
|
||||
}
|
||||
return pos += 2, token = SyntaxKind.GreaterThanGreaterThanGreaterThanToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.GreaterThanGreaterThanEqualsToken;
|
||||
}
|
||||
pos++;
|
||||
return token = SyntaxKind.GreaterThanGreaterThanToken;
|
||||
}
|
||||
if (charCodeUnchecked(pos) === CharacterCodes.equals) {
|
||||
if (charCodeChecked(pos) === CharacterCodes.equals) {
|
||||
pos++;
|
||||
return token = SyntaxKind.GreaterThanEqualsToken;
|
||||
}
|
||||
@ -2436,13 +2453,13 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
while (true) {
|
||||
// If we reach the end of a file, or hit a newline, then this is an unterminated
|
||||
// regex. Report error and return what we have so far.
|
||||
if (p >= end) {
|
||||
const ch = charCodeChecked(p);
|
||||
if (ch === CharacterCodes.EOF) {
|
||||
tokenFlags |= TokenFlags.Unterminated;
|
||||
error(Diagnostics.Unterminated_regular_expression_literal);
|
||||
break;
|
||||
}
|
||||
|
||||
const ch = charCodeUnchecked(p);
|
||||
if (isLineBreak(ch)) {
|
||||
tokenFlags |= TokenFlags.Unterminated;
|
||||
error(Diagnostics.Unterminated_regular_expression_literal);
|
||||
@ -2792,7 +2809,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
// | CharacterEscape
|
||||
// | 'k<' RegExpIdentifierName '>'
|
||||
function scanAtomEscape() {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.backslash);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.backslash);
|
||||
switch (charCodeChecked(pos)) {
|
||||
case CharacterCodes.k:
|
||||
pos++;
|
||||
@ -2822,7 +2839,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
|
||||
// DecimalEscape ::= [1-9] [0-9]*
|
||||
function scanDecimalEscape(): boolean {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.backslash);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.backslash);
|
||||
const ch = charCodeChecked(pos);
|
||||
if (ch >= CharacterCodes._1 && ch <= CharacterCodes._9) {
|
||||
const start = pos;
|
||||
@ -2841,7 +2858,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
// | '^' | '$' | '/' | '\' | '.' | '*' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '|'
|
||||
// | [~UnicodeMode] (any other non-identifier characters)
|
||||
function scanCharacterEscape(atomEscape: boolean): string {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.backslash);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.backslash);
|
||||
let ch = charCodeChecked(pos);
|
||||
switch (ch) {
|
||||
case CharacterCodes.c:
|
||||
@ -2892,7 +2909,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
|
||||
function scanGroupName(isReference: boolean) {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.lessThan);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.lessThan);
|
||||
tokenStart = pos;
|
||||
scanIdentifier(codePointChecked(pos), languageVersion);
|
||||
if (pos === tokenStart) {
|
||||
@ -2918,7 +2935,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
|
||||
// ClassRanges ::= '^'? (ClassAtom ('-' ClassAtom)?)*
|
||||
function scanClassRanges() {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.openBracket);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.openBracket);
|
||||
if (charCodeChecked(pos) === CharacterCodes.caret) {
|
||||
// character complement
|
||||
pos++;
|
||||
@ -2977,7 +2994,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
// ClassSubtraction ::= ClassSetOperand ('--' ClassSetOperand)+
|
||||
// ClassSetRange ::= ClassSetCharacter '-' ClassSetCharacter
|
||||
function scanClassSetExpression() {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.openBracket);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.openBracket);
|
||||
let isCharacterComplement = false;
|
||||
if (charCodeChecked(pos) === CharacterCodes.caret) {
|
||||
pos++;
|
||||
@ -3221,7 +3238,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
|
||||
// ClassStringDisjunctionContents ::= ClassSetCharacter* ('|' ClassSetCharacter*)*
|
||||
function scanClassStringDisjunctionContents() {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.openBrace);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.openBrace);
|
||||
let characterCount = 0;
|
||||
while (true) {
|
||||
const ch = charCodeChecked(pos);
|
||||
@ -3361,7 +3378,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
// | 'd' | 'D' | 's' | 'S' | 'w' | 'W'
|
||||
// | [+UnicodeMode] ('P' | 'p') '{' UnicodePropertyValueExpression '}'
|
||||
function scanCharacterClassEscape(): boolean {
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.backslash);
|
||||
Debug.assertEqual(charCodeChecked(pos - 1), CharacterCodes.backslash);
|
||||
let isCharacterComplement = false;
|
||||
const start = pos - 1;
|
||||
const ch = charCodeChecked(pos);
|
||||
@ -3581,13 +3598,13 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
function scanJsxToken(allowMultilineJsxText = true): JsxTokenSyntaxKind {
|
||||
fullStartPos = tokenStart = pos;
|
||||
|
||||
if (pos >= end) {
|
||||
let char = charCodeChecked(pos);
|
||||
if (char === CharacterCodes.EOF) {
|
||||
return token = SyntaxKind.EndOfFileToken;
|
||||
}
|
||||
|
||||
let char = charCodeUnchecked(pos);
|
||||
if (char === CharacterCodes.lessThan) {
|
||||
if (charCodeUnchecked(pos + 1) === CharacterCodes.slash) {
|
||||
if (charCodeChecked(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
return token = SyntaxKind.LessThanSlashToken;
|
||||
}
|
||||
@ -3680,7 +3697,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
function scanJsxAttributeValue(): SyntaxKind {
|
||||
fullStartPos = pos;
|
||||
|
||||
switch (charCodeUnchecked(pos)) {
|
||||
switch (charCodeChecked(pos)) {
|
||||
case CharacterCodes.doubleQuote:
|
||||
case CharacterCodes.singleQuote:
|
||||
tokenValue = scanString(/*jsxAttributeString*/ true);
|
||||
@ -3702,13 +3719,14 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
if (pos >= end) {
|
||||
return token = SyntaxKind.EndOfFileToken;
|
||||
}
|
||||
for (let ch = charCodeUnchecked(pos); pos < end && (!isLineBreak(ch) && ch !== CharacterCodes.backtick); ch = codePointUnchecked(++pos)) {
|
||||
for (let ch = charCodeChecked(pos); ch !== CharacterCodes.EOF && (!isLineBreak(ch) && ch !== CharacterCodes.backtick); ch = codePointChecked(++pos)) {
|
||||
if (!inBackticks) {
|
||||
if (ch === CharacterCodes.openBrace) {
|
||||
break;
|
||||
}
|
||||
else if (
|
||||
ch === CharacterCodes.at
|
||||
// eslint-disable-next-line local/bounds-check -- bounds checked before invocation
|
||||
&& pos - 1 >= 0 && isWhiteSpaceSingleLine(charCodeUnchecked(pos - 1))
|
||||
&& !(pos + 1 < end && isWhiteSpaceLike(charCodeUnchecked(pos + 1)))
|
||||
) {
|
||||
@ -3727,11 +3745,12 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
function scanJsDocToken(): JSDocSyntaxKind {
|
||||
fullStartPos = tokenStart = pos;
|
||||
tokenFlags = TokenFlags.None;
|
||||
if (pos >= end) {
|
||||
|
||||
const ch = codePointChecked(pos);
|
||||
if (ch === CharacterCodes.EOF) {
|
||||
return token = SyntaxKind.EndOfFileToken;
|
||||
}
|
||||
|
||||
const ch = codePointUnchecked(pos);
|
||||
pos += charSize(ch);
|
||||
switch (ch) {
|
||||
case CharacterCodes.tab:
|
||||
@ -3745,7 +3764,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
case CharacterCodes.at:
|
||||
return token = SyntaxKind.AtToken;
|
||||
case CharacterCodes.carriageReturn:
|
||||
if (charCodeUnchecked(pos) === CharacterCodes.lineFeed) {
|
||||
if (charCodeChecked(pos) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
// falls through
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user