diff --git a/package-lock.json b/package-lock.json index 33a944dd..1a8eeb18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@microsoft/powerquery-parser", - "version": "0.19.0", + "version": "0.19.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@microsoft/powerquery-parser", - "version": "0.19.0", + "version": "0.19.1", "license": "MIT", "dependencies": { "grapheme-splitter": "^1.0.4", diff --git a/package.json b/package.json index 508067bb..791a1dc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/powerquery-parser", - "version": "0.19.0", + "version": "0.19.1", "description": "A parser for the Power Query/M formula language.", "author": "Microsoft", "license": "MIT", diff --git a/src/powerquery-parser/common/arrayUtils.ts b/src/powerquery-parser/common/arrayUtils.ts index c6fce9ef..cef69e1d 100644 --- a/src/powerquery-parser/common/arrayUtils.ts +++ b/src/powerquery-parser/common/arrayUtils.ts @@ -17,7 +17,11 @@ export function all( } export function assertGet(collection: ReadonlyArray, index: number, message?: string, details?: object): T { - return Assert.asDefined(collection[index], message, details); + return Assert.asDefined( + collection[index], + message ?? "index out of bounds", + details ?? { index, collectionLength: collection.length }, + ); } export function assertIncludes(collection: ReadonlyArray, element: T, message?: string, details?: object): void { diff --git a/src/powerquery-parser/common/stringUtils.ts b/src/powerquery-parser/common/stringUtils.ts index 79380b8d..4bf5dcea 100644 --- a/src/powerquery-parser/common/stringUtils.ts +++ b/src/powerquery-parser/common/stringUtils.ts @@ -5,6 +5,14 @@ import GraphemeSplitter = require("grapheme-splitter"); import { Assert, CommonError, Pattern } from "."; +export function assertGet(text: string, index: number, message?: string, details?: object): string { + return Assert.asDefined( + text[index], + message ?? "index out of bounds", + details ?? { index, textLength: text.length }, + ); +} + export const graphemeSplitter: GraphemeSplitter = new GraphemeSplitter(); export interface FoundQuotes { @@ -162,7 +170,7 @@ export function findQuotes(text: string, indexStart: number): FoundQuotes | unde } export function newlineKindAt(text: string, index: number): NewlineKind | undefined { - const chr1: string = text[index]; + const chr1: string | undefined = text[index]; switch (chr1) { case `\u000d`: { @@ -199,7 +207,7 @@ export function isHex(text: string): boolean { } export function getSign(text: string): [boolean, number] { - let char: string = text[0]; + let char: string | undefined = text[0]; let charOffset: number = 0; let isPositive: boolean = true; diff --git a/src/powerquery-parser/language/identifierUtils.ts b/src/powerquery-parser/language/identifierUtils.ts index f17d94ca..ea87f40f 100644 --- a/src/powerquery-parser/language/identifierUtils.ts +++ b/src/powerquery-parser/language/identifierUtils.ts @@ -271,7 +271,7 @@ function getGeneralizedIdentifierLength(text: string, index: number): number | u let continueMatching: boolean = true; while (continueMatching) { - const currentChr: string = text[index]; + const currentChr: string | undefined = text[index]; if (currentChr === " ") { index += 1; diff --git a/src/powerquery-parser/language/type/typeUtils/factories.ts b/src/powerquery-parser/language/type/typeUtils/factories.ts index e5a4b0a5..9403ba36 100644 --- a/src/powerquery-parser/language/type/typeUtils/factories.ts +++ b/src/powerquery-parser/language/type/typeUtils/factories.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { Assert, CommonError, StringUtils } from "../../../common"; +import { ArrayUtils, Assert, CommonError, StringUtils } from "../../../common"; import { PrimitiveTypeConstantMap, primitiveTypeMapKey } from "./primitive"; import { Trace, TraceManager } from "../../../common/trace"; import { simplify } from "./simplify"; @@ -31,7 +31,7 @@ export function anyUnion( if (simplified.length === 1) { trace.exit(); - return simplified[0]; + return ArrayUtils.assertGet(simplified, 0); } const result: Type.AnyUnion = { diff --git a/src/powerquery-parser/language/type/typeUtils/isCompatible.ts b/src/powerquery-parser/language/type/typeUtils/isCompatible.ts index 9c7ecf5a..09821c55 100644 --- a/src/powerquery-parser/language/type/typeUtils/isCompatible.ts +++ b/src/powerquery-parser/language/type/typeUtils/isCompatible.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { Assert, CommonError, MapUtils } from "../../../common"; +import { ArrayUtils, Assert, CommonError, MapUtils } from "../../../common"; import { isEqualFunctionSignature, isEqualType } from "./isEqualType"; import { isFieldSpecificationList, isFunctionSignature } from "./isType"; import { Trace, TraceManager } from "../../../common/trace"; @@ -777,7 +777,7 @@ function isCompatibleDefinedListOrDefinedListType - isEqualType(leftType, rightElements[index]), + isEqualType(leftType, ArrayUtils.assertGet(rightElements, index)), ), ); } @@ -160,7 +160,7 @@ export function isEqualDefinedListType(left: Type.DefinedListType, right: Type.D return ArrayUtils.all( left.itemTypes.map((leftType: Type.TPowerQueryType, index: number) => - isEqualType(leftType, rightElements[index]), + isEqualType(leftType, ArrayUtils.assertGet(rightElements, index)), ), ); } @@ -224,8 +224,8 @@ export function isEqualFunctionParameters( const numParameters: number = left.length; for (let index: number = 0; index < numParameters; index += 1) { - const nthLeft: Type.FunctionParameter = left[index]; - const nthRight: Type.FunctionParameter = right[index]; + const nthLeft: Type.FunctionParameter = ArrayUtils.assertGet(left, index); + const nthRight: Type.FunctionParameter = ArrayUtils.assertGet(right, index); if (!isEqualFunctionParameter(nthLeft, nthRight)) { return false; diff --git a/src/powerquery-parser/language/type/typeUtils/typeCheck.ts b/src/powerquery-parser/language/type/typeUtils/typeCheck.ts index 46f21c7a..231d1189 100644 --- a/src/powerquery-parser/language/type/typeUtils/typeCheck.ts +++ b/src/powerquery-parser/language/type/typeUtils/typeCheck.ts @@ -127,7 +127,7 @@ export function typeCheckInvocation( for (let index: number = 0; index < numParameters; index += 1) { const arg: Type.TPowerQueryType | undefined = args[index]; - const parameter: Type.FunctionParameter = parameters[index]; + const parameter: Type.FunctionParameter = ArrayUtils.assertGet(parameters, index); if (isCompatibleWithFunctionParameter(arg, parameter)) { validArgs.push(index); @@ -257,8 +257,8 @@ function typeCheckGenericNumber< const mismatches: Map> = new Map(); for (let index: number = 0; index < upperBound; index += 1) { - const element: Value = valueElements[index]; - const schemaItemType: Schema = schemaItemTypes[index]; + const element: Value = ArrayUtils.assertGet(valueElements, index); + const schemaItemType: Schema = ArrayUtils.assertGet(schemaItemTypes, index); if (comparer(element, schemaItemType, traceManager, trace.id)) { validIndices.push(index); diff --git a/src/powerquery-parser/language/type/typeUtils/typeUtils.ts b/src/powerquery-parser/language/type/typeUtils/typeUtils.ts index 4fbbc42f..53c2cfe9 100644 --- a/src/powerquery-parser/language/type/typeUtils/typeUtils.ts +++ b/src/powerquery-parser/language/type/typeUtils/typeUtils.ts @@ -4,7 +4,7 @@ import { Ast, AstUtils } from "../.."; import { NodeIdMap, NodeIdMapUtils, ParseContext, XorNode, XorNodeKind } from "../../../parser"; import { Trace, TraceManager } from "../../../common/trace"; -import { Assert } from "../../../common"; +import { ArrayUtils, Assert } from "../../../common"; import { isCompatible } from "./isCompatible"; import { isEqualType } from "./isEqualType"; import { primitiveType } from "./factories"; @@ -91,7 +91,7 @@ export function isValidInvocation( const numParameters: number = parameters.length; for (let index: number = 1; index < numParameters; index += 1) { - const parameter: Type.FunctionParameter = Assert.asDefined(parameters[index]); + const parameter: Type.FunctionParameter = ArrayUtils.assertGet(parameters, index); const argType: Type.TPowerQueryType | undefined = args[index]; if (argType !== undefined) { diff --git a/src/powerquery-parser/lexer/lexer.ts b/src/powerquery-parser/lexer/lexer.ts index aa69d603..e9c557ca 100644 --- a/src/powerquery-parser/lexer/lexer.ts +++ b/src/powerquery-parser/lexer/lexer.ts @@ -148,8 +148,8 @@ export function equalLines(leftLines: ReadonlyArray, rightLines: Readonly const numLines: number = leftLines.length; for (let lineIndex: number = 0; lineIndex < numLines; lineIndex += 1) { - const left: TLine = leftLines[lineIndex]; - const right: TLine = rightLines[lineIndex]; + const left: TLine = ArrayUtils.assertGet(leftLines, lineIndex); + const right: TLine = ArrayUtils.assertGet(rightLines, lineIndex); const leftTokens: ReadonlyArray = left.tokens; const rightTokens: ReadonlyArray = right.tokens; @@ -168,7 +168,7 @@ export function equalLines(leftLines: ReadonlyArray, rightLines: Readonly const numTokens: number = leftTokens.length; for (let tokenIndex: number = 0; tokenIndex < numTokens; tokenIndex += 1) { - if (!equalTokens(leftTokens[tokenIndex], rightTokens[tokenIndex])) { + if (!equalTokens(ArrayUtils.assertGet(leftTokens, tokenIndex), ArrayUtils.assertGet(rightTokens, tokenIndex))) { return false; } } @@ -265,7 +265,7 @@ function splitOnLineTerminators(startingText: string): SplitLine[] { let indexWasExpanded: boolean = false; for (const lineTerminator of lineTerminators) { - const splitLine: SplitLine = lines[index]; + const splitLine: SplitLine = ArrayUtils.assertGet(lines, index); const text: string = splitLine.text; if (text.indexOf(lineTerminator) !== -1) { @@ -276,7 +276,7 @@ function splitOnLineTerminators(startingText: string): SplitLine[] { lineTerminator, })); - split[split.length - 1].lineTerminator = splitLine.lineTerminator; + ArrayUtils.assertGet(split, split.length - 1).lineTerminator = splitLine.lineTerminator; lines = [...lines.slice(0, index), ...split, ...lines.slice(index + 1)]; } @@ -287,7 +287,7 @@ function splitOnLineTerminators(startingText: string): SplitLine[] { } } - lines[lines.length - 1].lineTerminator = ""; + ArrayUtils.assertGet(lines, lines.length - 1).lineTerminator = ""; return lines; } @@ -356,7 +356,7 @@ function updateLine(state: State, lineNumber: number, text: string): State { throw error; } - const line: TLine = state.lines[lineNumber]; + const line: TLine = ArrayUtils.assertGet(state.lines, lineNumber); const range: Range = rangeFrom(line, lineNumber); return updateRange(state, range, text); @@ -374,14 +374,14 @@ function updateRange(state: State, range: Range, text: string): State { const splitLines: SplitLine[] = splitOnLineTerminators(text); const rangeStart: RangePosition = range.start; - const lineStart: TLine = state.lines[rangeStart.lineNumber]; + const lineStart: TLine = ArrayUtils.assertGet(state.lines, rangeStart.lineNumber); const textPrefix: string = lineStart.text.substring(0, rangeStart.lineCodeUnit); - splitLines[0].text = textPrefix + splitLines[0].text; + ArrayUtils.assertGet(splitLines, 0).text = textPrefix + ArrayUtils.assertGet(splitLines, 0).text; const rangeEnd: RangePosition = range.end; - const lineEnd: TLine = state.lines[rangeEnd.lineNumber]; + const lineEnd: TLine = ArrayUtils.assertGet(state.lines, rangeEnd.lineNumber); const textSuffix: string = lineEnd.text.substr(rangeEnd.lineCodeUnit); - const lastSplitLine: SplitLine = splitLines[splitLines.length - 1]; + const lastSplitLine: SplitLine = ArrayUtils.assertGet(splitLines, splitLines.length - 1); lastSplitLine.text = lastSplitLine.text + textSuffix; // make sure we have a line terminator @@ -400,7 +400,7 @@ function updateRange(state: State, range: Range, text: string): State { const lines: ReadonlyArray = [ ...state.lines.slice(0, rangeStart.lineNumber), ...newLines, - ...retokenizeLines(state, rangeEnd.lineNumber + 1, newLines[newLines.length - 1].lineModeEnd), + ...retokenizeLines(state, rangeEnd.lineNumber + 1, ArrayUtils.assertGet(newLines, newLines.length - 1).lineModeEnd), ]; return { @@ -485,7 +485,7 @@ function retokenizeLines(state: State, lineNumber: number, previousLineModeEnd: const retokenizedLines: TLine[] = []; - if (previousLineModeEnd !== lines[lineNumber].lineModeStart) { + if (previousLineModeEnd !== ArrayUtils.assertGet(lines, lineNumber).lineModeStart) { let currentLine: TLine | undefined = lines[lineNumber]; while (currentLine) { @@ -793,7 +793,7 @@ function tokenizeTextLiteralContentOrEnd(line: TLine, currentPosition: number): function tokenizeDefault(line: TLine, lineNumber: number, positionStart: number, locale: string): LineModeAlteringRead { const text: string = line.text; - const chr1: string = text[positionStart]; + const chr1: string = StringUtils.assertGet(text, positionStart); let token: Token.LineToken; let lineMode: LineMode = LineMode.Default; @@ -855,7 +855,7 @@ function tokenizeDefault(line: TLine, lineNumber: number, positionStart: number, } else if ("1" <= chr2 && chr2 <= "9") { token = readNumericLiteral(text, lineNumber, positionStart, locale); } else if (chr2 === ".") { - const chr3: string = text[positionStart + 2]; + const chr3: string | undefined = text[positionStart + 2]; if (chr3 === ".") { token = readConstant(Token.LineTokenKind.Ellipsis, text, positionStart, 3); @@ -1321,8 +1321,8 @@ function testBadRangeError(state: State, range: Range): LexError.BadRangeError | const rangeStart: RangePosition = range.start; const rangeEnd: RangePosition = range.end; - const lineStart: TLine = lines[rangeStart.lineNumber]; - const lineEnd: TLine = lines[rangeEnd.lineNumber]; + const lineStart: TLine = ArrayUtils.assertGet(lines, rangeStart.lineNumber); + const lineEnd: TLine = ArrayUtils.assertGet(lines, rangeEnd.lineNumber); if (rangeStart.lineCodeUnit > lineStart.text.length) { kind = LexError.BadRangeKind.LineCodeUnitStart_GreaterThan_LineLength; diff --git a/src/powerquery-parser/lexer/lexerSnapshot.ts b/src/powerquery-parser/lexer/lexerSnapshot.ts index e3bd8754..81c95298 100644 --- a/src/powerquery-parser/lexer/lexerSnapshot.ts +++ b/src/powerquery-parser/lexer/lexerSnapshot.ts @@ -135,7 +135,7 @@ function createSnapshot(state: Lexer.State): LexerSnapshot { while (flatIndex < numFlatTokens) { state.cancellationToken?.throwIfCancelled(); - const flatToken: FlatLineToken = flatTokens[flatIndex]; + const flatToken: FlatLineToken = ArrayUtils.assertGet(flatTokens, flatIndex); const lineTokenKind: Token.LineTokenKind = flatToken.kind; switch (lineTokenKind) { @@ -553,7 +553,7 @@ function collectWhileContent( while (flatIndex < numTokens) { cancellationToken?.throwIfCancelled(); - const token: FlatLineToken = flatTokens[flatIndex]; + const token: FlatLineToken = ArrayUtils.assertGet(flatTokens, flatIndex); if (token.kind !== contentKind) { break; diff --git a/src/powerquery-parser/parser/context/contextUtils.ts b/src/powerquery-parser/parser/context/contextUtils.ts index 28908f73..ada19796 100644 --- a/src/powerquery-parser/parser/context/contextUtils.ts +++ b/src/powerquery-parser/parser/context/contextUtils.ts @@ -335,7 +335,7 @@ export function deleteContext(state: ParseContext.State, nodeId: number): ParseC // Not a leaf node. if (childIds !== undefined) { ArrayUtils.assertNonZeroLength(childIds); - const childId: number = childIds[0]; + const childId: number = ArrayUtils.assertGet(childIds, 0); // Not a leaf node, is the Root node. // Promote the child to the root if it's a Context node. diff --git a/src/powerquery-parser/parser/disambiguation/disambiguationUtils.ts b/src/powerquery-parser/parser/disambiguation/disambiguationUtils.ts index f195c0a4..51acc1d2 100644 --- a/src/powerquery-parser/parser/disambiguation/disambiguationUtils.ts +++ b/src/powerquery-parser/parser/disambiguation/disambiguationUtils.ts @@ -213,7 +213,7 @@ export async function disambiguateParenthesis( let offsetTokenIndex: number = initialTokenIndex + 1; while (offsetTokenIndex < totalTokens) { - const offsetTokenKind: Token.TokenKind = tokens[offsetTokenIndex].kind; + const offsetTokenKind: Token.TokenKind = ArrayUtils.assertGet(tokens, offsetTokenIndex).kind; if (offsetTokenKind === Token.TokenKind.LeftParenthesis) { nestedDepth += 1; @@ -305,7 +305,7 @@ export function disambiguateBracket( offsetTokenIndex += 1; while (offsetTokenIndex < totalTokens) { - offsetTokenKind = tokens[offsetTokenIndex].kind; + offsetTokenKind = ArrayUtils.assertGet(tokens, offsetTokenIndex).kind; if (offsetTokenKind === Token.TokenKind.Equal) { result = BracketDisambiguation.RecordExpression; @@ -472,7 +472,7 @@ function unsafeMoveTo(state: ParseState, tokenIndex: number): void { state.tokenIndex = tokenIndex; if (tokenIndex < tokens.length) { - state.currentToken = tokens[tokenIndex]; + state.currentToken = ArrayUtils.assertGet(tokens, tokenIndex); state.currentTokenKind = state.currentToken.kind; } else { state.currentToken = undefined; diff --git a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapIterator.ts b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapIterator.ts index 4d485c37..8ef64687 100644 --- a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapIterator.ts +++ b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapIterator.ts @@ -3,7 +3,7 @@ import { Ast, Constant, IdentifierUtils } from "../../language"; import { NodeIdMap, NodeIdMapUtils, TXorNode, XorNodeKind, XorNodeUtils } from "."; -import { Assert } from "../../common"; +import { ArrayUtils, Assert } from "../../common"; import { parameterIdentifier } from "./nodeIdMapUtils"; import { XorNode } from "./xorNode"; @@ -128,7 +128,7 @@ export function nthSiblingXor( return undefined; } - return NodeIdMapUtils.xor(nodeIdMapCollection, childIds[attributeIndex]); + return NodeIdMapUtils.xor(nodeIdMapCollection, ArrayUtils.assertGet(childIds, attributeIndex)); } // ------------------------------------------ diff --git a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/idUtils.ts b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/idUtils.ts index 0850794c..ae6fa3ff 100644 --- a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/idUtils.ts +++ b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/idUtils.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { MapUtils, TypeScriptUtils } from "../../../common"; +import { ArrayUtils, MapUtils, TypeScriptUtils } from "../../../common"; import { Trace, TraceConstant, TraceManager } from "../../../common/trace"; import { Ast } from "../../../language"; import { Collection } from "../nodeIdMap"; @@ -63,8 +63,8 @@ export function recalculateIds( const newIdByOldId: Map = new Map(); for (let index: number = 0; index < numIds; index += 1) { - const oldId: number = encounteredIds[index]; - const newId: number = sortedIds[index]; + const oldId: number = ArrayUtils.assertGet(encounteredIds, index); + const newId: number = ArrayUtils.assertGet(sortedIds, index); if (oldId !== newId) { newIdByOldId.set(oldId, newId); diff --git a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/leafSelectors.ts b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/leafSelectors.ts index f8ee17ef..8fe88b63 100644 --- a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/leafSelectors.ts +++ b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/leafSelectors.ts @@ -3,7 +3,7 @@ import { AstNodeById, Collection } from "../nodeIdMap"; import { NodeIdMap, XorNodeUtils } from ".."; -import { Assert } from "../../../common"; +import { ArrayUtils, Assert } from "../../../common"; import { Ast } from "../../../language"; import { TXorNode } from "../xorNode"; import { xor } from "./commonSelectors"; @@ -35,7 +35,7 @@ export function leftMostXor(nodeIdMapCollection: Collection, nodeId: number): TX let childIds: ReadonlyArray | undefined = nodeIdMapCollection.childIdsById.get(currentNodeId); while (childIds?.length) { - currentNodeId = childIds[0]; + currentNodeId = ArrayUtils.assertGet(childIds, 0); childIds = nodeIdMapCollection.childIdsById.get(currentNodeId); } diff --git a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/nodeIdMapUtils.ts b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/nodeIdMapUtils.ts index d7a92f13..8c1fe021 100644 --- a/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/nodeIdMapUtils.ts +++ b/src/powerquery-parser/parser/nodeIdMap/nodeIdMapUtils/nodeIdMapUtils.ts @@ -4,7 +4,7 @@ import { Ast, Token } from "../../../language"; import { Collection, CollectionValidation, IdsByNodeKind, NodeSummary } from "../nodeIdMap"; import { TXorNode, XorNodeKind, XorNodeTokenRange } from "../xorNode"; -import { Assert } from "../../../common"; +import { ArrayUtils, Assert } from "../../../common"; import { ParseContext } from "../../context"; import { rightMostLeaf } from "./leafSelectors"; @@ -56,7 +56,7 @@ export function hasParsedToken(nodeIdMapCollection: Collection, nodeId: number): } // There might be a child under here. else if (numChildren === 1) { - const childId: number = childIds[0]; + const childId: number = ArrayUtils.assertGet(childIds, 0); // We know it's an Ast Node, therefore something was parsed. if (nodeIdMapCollection.astNodeById.has(childId)) { diff --git a/src/powerquery-parser/parser/parseState/parseStateUtils.ts b/src/powerquery-parser/parser/parseState/parseStateUtils.ts index 80320754..435fbf73 100644 --- a/src/powerquery-parser/parser/parseState/parseStateUtils.ts +++ b/src/powerquery-parser/parser/parseState/parseStateUtils.ts @@ -162,7 +162,7 @@ export function isOnTokenKind( export function isOnConstantKind(state: ParseState, constantKind: Constant.TConstant): boolean { if (isOnTokenKind(state, Token.TokenKind.Identifier)) { - const currentToken: Token.Token = state.lexerSnapshot.tokens[state.tokenIndex]; + const currentToken: Token.Token = ArrayUtils.assertGet(state.lexerSnapshot.tokens, state.tokenIndex); if (currentToken?.data === undefined) { const details: { currentToken: Token.Token } = { currentToken }; @@ -283,7 +283,7 @@ export function assertGetContextNodeMetadata(state: ParseState): ContextNodeMeta // inclusive token index const tokenIndexEnd: number = state.tokenIndex - 1; - const tokenEnd: Token.Token = Assert.asDefined(state.lexerSnapshot.tokens[tokenIndexEnd]); + const tokenEnd: Token.Token = ArrayUtils.assertGet(state.lexerSnapshot.tokens, tokenIndexEnd); const tokenRange: Token.TokenRange = { tokenIndexStart: currentContextNode.tokenIndexStart, diff --git a/src/powerquery-parser/parser/parsers/naiveParseSteps.ts b/src/powerquery-parser/parser/parsers/naiveParseSteps.ts index 7f182ebb..a5c543c3 100644 --- a/src/powerquery-parser/parser/parsers/naiveParseSteps.ts +++ b/src/powerquery-parser/parser/parsers/naiveParseSteps.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { Assert, CommonError, Result, ResultUtils } from "../../common"; +import { ArrayUtils, Assert, CommonError, Result, ResultUtils } from "../../common"; import { Ast, AstUtils, Comment, Constant, ConstantUtils, IdentifierUtils, Token } from "../../language"; import { Disambiguation, DisambiguationUtils } from "../disambiguation"; import { NaiveParseSteps, ParseError } from ".."; @@ -122,8 +122,8 @@ export async function readGeneralizedIdentifier( const lexerSnapshot: LexerSnapshot = state.lexerSnapshot; const tokens: ReadonlyArray = lexerSnapshot.tokens; - const contiguousIdentifierStartIndex: number = tokens[tokenRangeStartIndex].positionStart.codeUnit; - const contiguousIdentifierEndIndex: number = tokens[tokenRangeEndIndex - 1].positionEnd.codeUnit; + const contiguousIdentifierStartIndex: number = ArrayUtils.assertGet(tokens, tokenRangeStartIndex).positionStart.codeUnit; + const contiguousIdentifierEndIndex: number = ArrayUtils.assertGet(tokens, tokenRangeEndIndex - 1).positionEnd.codeUnit; const literal: string = lexerSnapshot.text.slice(contiguousIdentifierStartIndex, contiguousIdentifierEndIndex); const literalKind: IdentifierUtils.IdentifierKind = IdentifierUtils.getIdentifierKind(literal, { @@ -2806,7 +2806,7 @@ async function tryReadPrimitiveType( let primitiveTypeKind: Constant.PrimitiveTypeConstant; if (ParseStateUtils.isOnTokenKind(state, TokenKind.Identifier)) { - const currentTokenData: string = state.lexerSnapshot.tokens[state.tokenIndex].data; + const currentTokenData: string = ArrayUtils.assertGet(state.lexerSnapshot.tokens, state.tokenIndex).data; switch (currentTokenData) { case Constant.PrimitiveTypeConstant.Action: @@ -3602,7 +3602,7 @@ export function readToken(state: ParseState): string { tokensLength: tokens.length, }); - const data: string = tokens[state.tokenIndex].data; + const data: string = ArrayUtils.assertGet(tokens, state.tokenIndex).data; state.tokenIndex += 1; if (state.tokenIndex === tokens.length) { @@ -3615,7 +3615,7 @@ export function readToken(state: ParseState): string { // So, for now when a IParseState is Eof when currentTokenKind === undefined. state.currentTokenKind = undefined; } else { - state.currentToken = tokens[state.tokenIndex]; + state.currentToken = ArrayUtils.assertGet(tokens, state.tokenIndex); state.currentTokenKind = state.currentToken.kind; } @@ -3867,7 +3867,7 @@ function testCatchFunction( if ( parameters.length > 1 || - (parameters.length === 1 && parameters[0].node.parameterType) || + (parameters.length === 1 && ArrayUtils.assertGet(parameters, 0).node.parameterType) || catchFunction.functionReturnType ) { const tokenStart: Token.Token = Assert.asDefined( diff --git a/src/test/libraryTest/language/typeUtils/typeCheck.test.ts b/src/test/libraryTest/language/typeUtils/typeCheck.test.ts index 158c157e..a0d0f04e 100644 --- a/src/test/libraryTest/language/typeUtils/typeCheck.test.ts +++ b/src/test/libraryTest/language/typeUtils/typeCheck.test.ts @@ -7,6 +7,7 @@ import { expect } from "chai"; import { CheckedDefinedList, CheckedInvocation } from "../../../../powerquery-parser/language/type/typeUtils"; import { Type, TypeUtils } from "../../../../powerquery-parser/language"; import { Language } from "../../../.."; +import { ArrayUtils } from "../../../../powerquery-parser/common"; import { NoOpTraceManagerInstance } from "../../../../powerquery-parser/common/trace"; import { OrderedMap } from "../../../../powerquery-parser"; @@ -300,7 +301,7 @@ describe(`TypeUtils.typeCheck`, () => { 0, { actual: args[0], - expected: definedFunction.parameters[0], + expected: ArrayUtils.assertGet(definedFunction.parameters, 0), }, ], ]), @@ -359,7 +360,7 @@ describe(`TypeUtils.typeCheck`, () => { const expected: TypeUtils.CheckedInvocation = { valid: [], - invalid: new Map([[0, { actual: args[0], expected: definedFunction.parameters[0] }]]), + invalid: new Map([[0, { actual: args[0], expected: ArrayUtils.assertGet(definedFunction.parameters, 0) }]]), extraneous: [], missing: [], }; @@ -387,7 +388,7 @@ describe(`TypeUtils.typeCheck`, () => { const expected: TypeUtils.CheckedInvocation = { valid: [], - invalid: new Map([[0, { actual: args[0], expected: definedFunction.parameters[0] }]]), + invalid: new Map([[0, { actual: args[0], expected: ArrayUtils.assertGet(definedFunction.parameters, 0) }]]), extraneous: [], missing: [], }; @@ -429,14 +430,14 @@ describe(`TypeUtils.typeCheck`, () => { 0, { actual: args[0], - expected: definedFunction.parameters[0], + expected: ArrayUtils.assertGet(definedFunction.parameters, 0), }, ], [ 1, { actual: args[1], - expected: definedFunction.parameters[1], + expected: ArrayUtils.assertGet(definedFunction.parameters, 1), }, ], ]), diff --git a/src/test/libraryTest/language/typeUtils/typeUtils.test.ts b/src/test/libraryTest/language/typeUtils/typeUtils.test.ts index 59bd9413..2e21c37d 100644 --- a/src/test/libraryTest/language/typeUtils/typeUtils.test.ts +++ b/src/test/libraryTest/language/typeUtils/typeUtils.test.ts @@ -7,6 +7,7 @@ import { expect } from "chai"; import { Type, TypeUtils } from "../../../../powerquery-parser/language"; import { NoOpTraceManagerInstance } from "../../../../powerquery-parser/common/trace"; import { OrderedMap } from "../../../../powerquery-parser"; +import { ArrayUtils } from "../../../../powerquery-parser/common"; interface AbridgedType { readonly kind: Type.TypeKind; @@ -137,7 +138,7 @@ describe(`TypeUtils`, () => { expect(simplified.length).to.equal(1); - const actual: AbridgedType = typeToAbridged(simplified[0]); + const actual: AbridgedType = typeToAbridged(ArrayUtils.assertGet(simplified, 0)); const expected: AbridgedType = TypeUtils.primitiveType(false, Type.TypeKind.Record); expect(actual).deep.equal(expected); }); @@ -209,7 +210,7 @@ describe(`TypeUtils`, () => { expect(simplified.length).to.equal(1); - const actual: AbridgedType = typeToAbridged(simplified[0]); + const actual: AbridgedType = typeToAbridged(ArrayUtils.assertGet(simplified, 0)); const expected: AbridgedType = typeToAbridged(Type.AnyInstance); expect(actual).deep.equal(expected); }); diff --git a/src/test/libraryTest/lexer/lexError.test.ts b/src/test/libraryTest/lexer/lexError.test.ts index dea5ebd8..c8e1cd48 100644 --- a/src/test/libraryTest/lexer/lexError.test.ts +++ b/src/test/libraryTest/lexer/lexError.test.ts @@ -5,6 +5,7 @@ import "mocha"; import { expect } from "chai"; import { DefaultSettings, Lexer, ResultUtils } from "../../.."; +import { ArrayUtils } from "../../../powerquery-parser/common"; function assertBadLineNumberKind(lineNumber: number, expectedKind: Lexer.LexError.BadLineNumberKind): void { const triedLex: Lexer.TriedLex = Lexer.tryLex(DefaultSettings, `foo`); @@ -36,7 +37,7 @@ function assertExpectedKind(text: string, expectedKind: Lexer.LexError.ExpectedK const state: Lexer.State = triedLex.value; expect(state.lines.length).to.equal(1); - const line: Lexer.TLine = state.lines[0]; + const line: Lexer.TLine = ArrayUtils.assertGet(state.lines, 0); if (!Lexer.isErrorLine(line)) { throw new Error(`AssertFailed: Lexer.isErrorLine(line): ${JSON.stringify(line)}`); diff --git a/src/test/libraryTest/lexer/lexIncremental.test.ts b/src/test/libraryTest/lexer/lexIncremental.test.ts index a5955ee6..b0fd6b2e 100644 --- a/src/test/libraryTest/lexer/lexIncremental.test.ts +++ b/src/test/libraryTest/lexer/lexIncremental.test.ts @@ -5,6 +5,7 @@ import "mocha"; import { expect } from "chai"; import { Lexer, ResultUtils } from "../../.."; +import { ArrayUtils } from "../../../powerquery-parser/common"; import { assertGetLexOk } from "../../testUtils/lexTestUtils"; const LINE_TERMINATOR: string = `\n`; @@ -81,7 +82,7 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foobar`, "X", range); expect(state.lines.length).to.equal(1); - expect(state.lines[0].text).to.equal("Xfoobar"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("Xfoobar"); }); it(`foobar -> fooXbar`, () => { @@ -98,7 +99,7 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foobar`, "X", range); expect(state.lines.length).to.equal(1); - expect(state.lines[0].text).to.equal("fooXbar"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("fooXbar"); }); it(`foobar -> Xoobar`, () => { @@ -115,7 +116,7 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foobar`, "X", range); expect(state.lines.length).to.equal(1); - expect(state.lines[0].text).to.equal("Xoobar"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("Xoobar"); }); it(`foobar -> X`, () => { @@ -132,7 +133,7 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foobar`, "X", range); expect(state.lines.length).to.equal(1); - expect(state.lines[0].text).to.equal("X"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("X"); }); it(`foo\\nbar -> X`, () => { @@ -149,7 +150,7 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foo\nbar`, "X", range); expect(state.lines.length).to.equal(1); - expect(state.lines[0].text).to.equal("X"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("X"); }); it(`foo\\nbar -> fXr`, () => { @@ -166,7 +167,7 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foo\nbar`, "X", range); expect(state.lines.length).to.equal(1); - expect(state.lines[0].text).to.equal("fXr"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("fXr"); }); it(`foo\\nbar\\baz -> foo\\nX\\nbaz`, () => { @@ -183,9 +184,9 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foo\nbar\nbaz`, "X", range); expect(state.lines.length).to.equal(3); - expect(state.lines[0].text).to.equal("foo"); - expect(state.lines[1].text).to.equal("X"); - expect(state.lines[2].text).to.equal("baz"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("foo"); + expect(ArrayUtils.assertGet(state.lines, 1).text).to.equal("X"); + expect(ArrayUtils.assertGet(state.lines, 2).text).to.equal("baz"); }); it(`foo\\nbar\\baz -> foo\\nbXr\\nbaz`, () => { @@ -202,9 +203,9 @@ describe(`Lexer.Incremental`, () => { const state: Lexer.State = assertGetLexerUpdateRangeOk(`foo\nbar\nbaz`, "X", range); expect(state.lines.length).to.equal(3); - expect(state.lines[0].text).to.equal("foo"); - expect(state.lines[1].text).to.equal("bXr"); - expect(state.lines[2].text).to.equal("baz"); + expect(ArrayUtils.assertGet(state.lines, 0).text).to.equal("foo"); + expect(ArrayUtils.assertGet(state.lines, 1).text).to.equal("bXr"); + expect(ArrayUtils.assertGet(state.lines, 2).text).to.equal("baz"); }); it(`lineTerminator maintained on single line change`, () => { diff --git a/src/test/libraryTest/lexer/lexerSnapshotCache.test.ts b/src/test/libraryTest/lexer/lexerSnapshotCache.test.ts index c0635698..0fe9ef33 100644 --- a/src/test/libraryTest/lexer/lexerSnapshotCache.test.ts +++ b/src/test/libraryTest/lexer/lexerSnapshotCache.test.ts @@ -5,6 +5,7 @@ import "mocha"; import { expect } from "chai"; import { DefaultSettings, Language, Lexer, ResultUtils, StringUtils } from "../../.."; +import { ArrayUtils } from "../../../powerquery-parser/common"; function assertGetLexerSnapshot(text: string): Lexer.LexerSnapshot { const triedLex: Lexer.TriedLex = Lexer.tryLex(DefaultSettings, text); @@ -80,7 +81,7 @@ describe("LexerSnapshot.graphemePositionStartFrom cache", () => { describe("cache hit consistency", () => { it("repeated calls return identical results", () => { const snapshot: Lexer.LexerSnapshot = assertGetLexerSnapshot("let x = 1"); - const token: Language.Token.Token = snapshot.tokens[0]; + const token: Language.Token.Token = ArrayUtils.assertGet(snapshot.tokens, 0); const first: StringUtils.GraphemePosition = snapshot.graphemePositionStartFrom(token); const second: StringUtils.GraphemePosition = snapshot.graphemePositionStartFrom(token); @@ -104,7 +105,7 @@ describe("LexerSnapshot.graphemePositionStartFrom cache", () => { // Column numbers should be increasing for (let i: number = 1; i < positions.length; i += 1) { - expect(positions[i].columnNumber).to.be.greaterThan(positions[i - 1].columnNumber); + expect(ArrayUtils.assertGet(positions, i).columnNumber).to.be.greaterThan(ArrayUtils.assertGet(positions, i - 1).columnNumber); } }); @@ -116,10 +117,10 @@ describe("LexerSnapshot.graphemePositionStartFrom cache", () => { snapshot.graphemePositionStartFrom(token), ); - expect(positions[0].lineNumber).to.equal(0); - expect(positions[1].lineNumber).to.equal(1); - expect(positions[2].lineNumber).to.equal(2); - expect(positions[3].lineNumber).to.equal(2); + expect(ArrayUtils.assertGet(positions, 0).lineNumber).to.equal(0); + expect(ArrayUtils.assertGet(positions, 1).lineNumber).to.equal(1); + expect(ArrayUtils.assertGet(positions, 2).lineNumber).to.equal(2); + expect(ArrayUtils.assertGet(positions, 3).lineNumber).to.equal(2); }); }); }); diff --git a/src/test/libraryTest/lexer/retokenizeLineNumbers.test.ts b/src/test/libraryTest/lexer/retokenizeLineNumbers.test.ts index c4613b4d..2b8432f3 100644 --- a/src/test/libraryTest/lexer/retokenizeLineNumbers.test.ts +++ b/src/test/libraryTest/lexer/retokenizeLineNumbers.test.ts @@ -5,6 +5,7 @@ import "mocha"; import { expect } from "chai"; import { Lexer, ResultUtils } from "../../.."; +import { ArrayUtils } from "../../../powerquery-parser/common"; import { assertGetLexOk } from "../../testUtils/lexTestUtils"; describe("Lexer.retokenizeLines line numbers", () => { @@ -30,9 +31,9 @@ describe("Lexer.retokenizeLines line numbers", () => { expect(updated.lines.length).to.equal(3, "expected 3 lines after update"); // All lines should now be in Default mode since line 0 is a complete string - expect(updated.lines[0].lineModeEnd).to.equal(Lexer.LineMode.Default, "line 0 should end in Default mode"); - expect(updated.lines[1].lineModeStart).to.equal(Lexer.LineMode.Default, "line 1 should start in Default mode"); - expect(updated.lines[2].lineModeStart).to.equal(Lexer.LineMode.Default, "line 2 should start in Default mode"); + expect(ArrayUtils.assertGet(updated.lines, 0).lineModeEnd).to.equal(Lexer.LineMode.Default, "line 0 should end in Default mode"); + expect(ArrayUtils.assertGet(updated.lines, 1).lineModeStart).to.equal(Lexer.LineMode.Default, "line 1 should start in Default mode"); + expect(ArrayUtils.assertGet(updated.lines, 2).lineModeStart).to.equal(Lexer.LineMode.Default, "line 2 should start in Default mode"); // Now snapshot to get token positions and verify line numbers are correct. const triedSnapshot: Lexer.TriedLexerSnapshot = Lexer.trySnapshot(updated); diff --git a/src/test/libraryTest/parser/identifierContextKind.test.ts b/src/test/libraryTest/parser/identifierContextKind.test.ts index 2facda06..597380d5 100644 --- a/src/test/libraryTest/parser/identifierContextKind.test.ts +++ b/src/test/libraryTest/parser/identifierContextKind.test.ts @@ -4,6 +4,7 @@ import "mocha"; import { expect } from "chai"; +import { ArrayUtils } from "../../../powerquery-parser/common"; import { NodeIdMap, NodeIdMapUtils, ParseOk } from "../../../powerquery-parser/parser"; import { AssertTestUtils } from "../../testUtils"; import { Ast } from "../../../powerquery-parser/language"; @@ -28,7 +29,7 @@ function assertGetIdentifierByLiteral(parseOk: ParseOk, identifierLiteral: strin if (matches.length === 0) { throw new Error(`could not find the following identifier in the ast: ${identifierLiteral}`); } else if (matches.length === 1) { - return matches[0]; + return ArrayUtils.assertGet(matches, 0); } else { throw new Error(`found multiple instances of the following identifier: ${identifierLiteral}`); } diff --git a/src/test/libraryTest/parser/parseNodeIdMapUtils.test.ts b/src/test/libraryTest/parser/parseNodeIdMapUtils.test.ts index aaabe790..7254234e 100644 --- a/src/test/libraryTest/parser/parseNodeIdMapUtils.test.ts +++ b/src/test/libraryTest/parser/parseNodeIdMapUtils.test.ts @@ -4,7 +4,7 @@ import "mocha"; import { expect } from "chai"; -import { Assert, Language, MapUtils, Parser } from "../../../powerquery-parser"; +import { ArrayUtils, Assert, Language, MapUtils, Parser } from "../../../powerquery-parser"; import { DefaultSettings, Task } from "../../.."; import { FieldSpecificationKeyValuePair, @@ -34,7 +34,7 @@ describe("nodeIdMapIterator", () => { expect(fieldSpecificationListIds.size).to.equal(1); - const fieldSpecificationListId: number = Assert.asDefined([...fieldSpecificationListIds.values()][0]); + const fieldSpecificationListId: number = ArrayUtils.assertGet([...fieldSpecificationListIds.values()], 0); const fieldSpecificationList: TXorNode = NodeIdMapUtils.assertXor( parseOk.nodeIdMapCollection, @@ -46,11 +46,11 @@ describe("nodeIdMapIterator", () => { expect(fieldSpecificationKeyValuePairs.length).to.equal(2); - const firstKeyValuePair: FieldSpecificationKeyValuePair = fieldSpecificationKeyValuePairs[0]; + const firstKeyValuePair: FieldSpecificationKeyValuePair = ArrayUtils.assertGet(fieldSpecificationKeyValuePairs, 0); expect(firstKeyValuePair.optional).to.equal(undefined); expect(firstKeyValuePair.normalizedKeyLiteral).to.equal("foo"); - const secondKeyValuePair: FieldSpecificationKeyValuePair = fieldSpecificationKeyValuePairs[1]; + const secondKeyValuePair: FieldSpecificationKeyValuePair = ArrayUtils.assertGet(fieldSpecificationKeyValuePairs, 1); expect(Boolean(secondKeyValuePair.optional)).to.equal(true); expect(secondKeyValuePair.normalizedKeyLiteral).to.equal("bar"); }); @@ -67,7 +67,7 @@ describe("nodeIdMapIterator", () => { expect(functionExpressionIds.size).to.equal(1); - const functionExpressionId: number = Assert.asDefined([...functionExpressionIds.values()][0]); + const functionExpressionId: number = ArrayUtils.assertGet([...functionExpressionIds.values()], 0); const functionExpression: TXorNode = NodeIdMapUtils.assertXor( parseOk.nodeIdMapCollection, @@ -82,12 +82,12 @@ describe("nodeIdMapIterator", () => { expect(parameters.length).to.equal(2); const firstParameter: Ast.TParameter = Language.AstUtils.assertAsNodeKind( - XorNodeUtils.assertAst(parameters[0]), + XorNodeUtils.assertAst(ArrayUtils.assertGet(parameters, 0)), Ast.NodeKind.Parameter, ); const secondParameter: Ast.TParameter = Language.AstUtils.assertAsNodeKind( - XorNodeUtils.assertAst(parameters[1]), + XorNodeUtils.assertAst(ArrayUtils.assertGet(parameters, 1)), Ast.NodeKind.Parameter, ); @@ -106,7 +106,7 @@ describe("nodeIdMapIterator", () => { expect(functionExpressionIds.size).to.equal(1); - const functionExpressionId: number = Assert.asDefined([...functionExpressionIds.values()][0]); + const functionExpressionId: number = ArrayUtils.assertGet([...functionExpressionIds.values()], 0); const functionExpression: TXorNode = NodeIdMapUtils.assertXor( parseError.state.contextState.nodeIdMapCollection, @@ -121,12 +121,12 @@ describe("nodeIdMapIterator", () => { expect(parameters.length).to.equal(2); const firstParameter: Ast.TParameter = Language.AstUtils.assertAsNodeKind( - XorNodeUtils.assertAst(parameters[0]), + XorNodeUtils.assertAst(ArrayUtils.assertGet(parameters, 0)), Ast.NodeKind.Parameter, ); const secondParameter: Ast.TParameter = Language.AstUtils.assertAsNodeKind( - XorNodeUtils.assertAst(parameters[1]), + XorNodeUtils.assertAst(ArrayUtils.assertGet(parameters, 1)), Ast.NodeKind.Parameter, ); @@ -147,7 +147,7 @@ describe("nodeIdMapIterator", () => { expect(functionExpressionIds.size).to.equal(1); - const functionExpressionId: number = Assert.asDefined([...functionExpressionIds.values()][0]); + const functionExpressionId: number = ArrayUtils.assertGet([...functionExpressionIds.values()], 0); const functionExpression: TXorNode = NodeIdMapUtils.assertXor( parseOk.nodeIdMapCollection, @@ -173,7 +173,7 @@ describe("nodeIdMapIterator", () => { expect(functionExpressionIds.size).to.equal(1); - const functionExpressionId: number = Assert.asDefined([...functionExpressionIds.values()][0]); + const functionExpressionId: number = ArrayUtils.assertGet([...functionExpressionIds.values()], 0); const functionExpression: TXorNode = NodeIdMapUtils.assertXor( parseError.state.contextState.nodeIdMapCollection, @@ -201,7 +201,7 @@ describe("nodeIdMapIterator", () => { expect(recordIds.size).to.equal(1); - const recordId: number = Assert.asDefined([...recordIds.values()][0]); + const recordId: number = ArrayUtils.assertGet([...recordIds.values()], 0); const record: TXorNode = NodeIdMapUtils.assertXor(parseOk.nodeIdMapCollection, recordId); const recordKeyValuePairs: ReadonlyArray = NodeIdMapIterator.iterRecord( @@ -211,7 +211,7 @@ describe("nodeIdMapIterator", () => { expect(recordKeyValuePairs.length).to.equal(1); - const keyValuePair: RecordKeyValuePair = recordKeyValuePairs[0]; + const keyValuePair: RecordKeyValuePair = ArrayUtils.assertGet(recordKeyValuePairs, 0); expect(keyValuePair.normalizedKeyLiteral).to.equal("foo"); }); }); @@ -227,7 +227,7 @@ describe("nodeIdMapIterator", () => { expect(recordTypeIds.size).to.equal(1); - const recordTypeId: number = Assert.asDefined([...recordTypeIds.values()][0]); + const recordTypeId: number = ArrayUtils.assertGet([...recordTypeIds.values()], 0); const recordType: TXorNode = NodeIdMapUtils.assertXor(parseOk.nodeIdMapCollection, recordTypeId); const fieldSpecificationKeyValuePairs: ReadonlyArray = @@ -235,11 +235,11 @@ describe("nodeIdMapIterator", () => { expect(fieldSpecificationKeyValuePairs.length).to.equal(2); - const firstKeyValuePair: FieldSpecificationKeyValuePair = fieldSpecificationKeyValuePairs[0]; + const firstKeyValuePair: FieldSpecificationKeyValuePair = ArrayUtils.assertGet(fieldSpecificationKeyValuePairs, 0); expect(firstKeyValuePair.optional).to.equal(undefined); expect(firstKeyValuePair.normalizedKeyLiteral).to.equal("foo"); - const secondKeyValuePair: FieldSpecificationKeyValuePair = fieldSpecificationKeyValuePairs[1]; + const secondKeyValuePair: FieldSpecificationKeyValuePair = ArrayUtils.assertGet(fieldSpecificationKeyValuePairs, 1); expect(Boolean(secondKeyValuePair.optional)).to.equal(true); expect(secondKeyValuePair.normalizedKeyLiteral).to.equal("bar"); }); diff --git a/src/test/libraryTest/parser/typeDirective.test.ts b/src/test/libraryTest/parser/typeDirective.test.ts index 5beaec5b..fef3c3a3 100644 --- a/src/test/libraryTest/parser/typeDirective.test.ts +++ b/src/test/libraryTest/parser/typeDirective.test.ts @@ -6,6 +6,7 @@ import { expect } from "chai"; import * as AssertTestUtils from "../../testUtils/assertTestUtils"; import { DefaultSettings, Language } from "../../../powerquery-parser"; +import { ArrayUtils } from "../../../powerquery-parser/common"; type ParseOk = Awaited>; @@ -21,7 +22,7 @@ in ); const letExpression: Language.Ast.LetExpression = parseOk.ast as Language.Ast.LetExpression; - const variable: Language.Ast.IdentifierPairedExpression = letExpression.variableList.elements[0].node; + const variable: Language.Ast.IdentifierPairedExpression = ArrayUtils.assertGet(letExpression.variableList.elements, 0).node; expect(variable.precedingDirectives).to.equal(undefined); }); @@ -40,7 +41,7 @@ in ); const letExpression: Language.Ast.LetExpression = parseOk.ast as Language.Ast.LetExpression; - const variable: Language.Ast.IdentifierPairedExpression = letExpression.variableList.elements[0].node; + const variable: Language.Ast.IdentifierPairedExpression = ArrayUtils.assertGet(letExpression.variableList.elements, 0).node; expect(variable.precedingDirectives).to.not.equal(undefined); @@ -61,7 +62,7 @@ shared Value = [];`, ); const section: Language.Ast.Section = parseOk.ast as Language.Ast.Section; - const sectionMember: Language.Ast.SectionMember = section.sectionMembers.elements[0]; + const sectionMember: Language.Ast.SectionMember = ArrayUtils.assertGet(section.sectionMembers.elements, 0); expect( sectionMember.precedingDirectives?.map((directive: Language.Comment.TDirective) => directive.value), @@ -82,7 +83,7 @@ in ); const letExpression: Language.Ast.LetExpression = parseOk.ast as Language.Ast.LetExpression; - const variable: Language.Ast.IdentifierPairedExpression = letExpression.variableList.elements[0].node; + const variable: Language.Ast.IdentifierPairedExpression = ArrayUtils.assertGet(letExpression.variableList.elements, 0).node; expect( variable.precedingDirectives?.map((directive: Language.Comment.TDirective) => directive.value), @@ -104,7 +105,7 @@ in ); const letExpression: Language.Ast.LetExpression = parseOk.ast as Language.Ast.LetExpression; - const variable: Language.Ast.IdentifierPairedExpression = letExpression.variableList.elements[0].node; + const variable: Language.Ast.IdentifierPairedExpression = ArrayUtils.assertGet(letExpression.variableList.elements, 0).node; expect(variable.precedingDirectives).to.equal(undefined); }); diff --git a/src/test/libraryTest/tokenizer/tokenizerIncremental.test.ts b/src/test/libraryTest/tokenizer/tokenizerIncremental.test.ts index 1a34288e..d7e6d946 100644 --- a/src/test/libraryTest/tokenizer/tokenizerIncremental.test.ts +++ b/src/test/libraryTest/tokenizer/tokenizerIncremental.test.ts @@ -5,6 +5,7 @@ import "mocha"; import { expect } from "chai"; import { DefaultSettings, Lexer, ResultUtils } from "../../.."; +import { ArrayUtils } from "../../../powerquery-parser/common"; import { ILineTokens, IState, IToken, Tokenizer } from "../../testUtils/tokenizerTestUtils"; const tokenizer: Tokenizer = new Tokenizer("\n"); @@ -74,16 +75,18 @@ class MockDocument { if (startingIndex === 0 || this.lineEndStates[startingIndex - 1] === undefined) { state = this.tokenizer.getInitialState(); } else { - state = this.lineEndStates[startingIndex - 1]; + state = ArrayUtils.assertGet(this.lineEndStates, startingIndex - 1); } for (let index: number = startingIndex; index < this.lines.length; index += 1) { - const result: ILineTokens = tokenizer.tokenize(this.lines[index], state); + const result: ILineTokens = tokenizer.tokenize(ArrayUtils.assertGet(this.lines, index), state); this.lineTokens[index] = result.tokens; tokenizedLineCount += 1; // If the new end state matches the previous state, we can stop tokenizing - if (result.endState.equals(this.lineEndStates[index])) { + const previousEndState: IState | undefined = this.lineEndStates[index]; + + if (previousEndState !== undefined && result.endState.equals(previousEndState)) { break; } @@ -158,14 +161,14 @@ describe("MockDocument validation", () => { describe("Incremental updates", () => { it("Re-parse with no change", () => { const document: MockDocument = new MockDocument(ORIGINAL_QUERY); - const originalLine: string = document.lines[2]; + const originalLine: string = ArrayUtils.assertGet(document.lines, 2); const count: number = document.applyChangeAndTokenize(originalLine, 2); expect(count).equals(1, "we should not have tokenized more than one line"); }); it("Re-parse with simple change", () => { const document: MockDocument = new MockDocument(ORIGINAL_QUERY); - const modified: string = document.lines[2].replace("source", "source123"); + const modified: string = ArrayUtils.assertGet(document.lines, 2).replace("source", "source123"); const count: number = document.applyChangeAndTokenize(modified, 2); expect(count).equals(1, "we should not have tokenized more than one line"); }); @@ -173,12 +176,12 @@ describe("Incremental updates", () => { it("Re-parse with unterminated string", () => { const lineNumber: number = 4; const document: MockDocument = new MockDocument(ORIGINAL_QUERY); - const modified: string = document.lines[lineNumber].replace(`"text",`, `"text`); + const modified: string = ArrayUtils.assertGet(document.lines, lineNumber).replace(`"text",`, `"text`); const count: number = document.applyChangeAndTokenize(modified, lineNumber); expect(count).equals(document.lines.length - lineNumber, "remaining lines should have been tokenized"); for (let index: number = lineNumber + 1; index < document.lineTokens.length; index += 1) { - const lineTokens: ReadonlyArray = document.lineTokens[index]; + const lineTokens: ReadonlyArray = ArrayUtils.assertGet(document.lineTokens, index); lineTokens.forEach((token: IToken) => { expect(token.scopes).equals("TextContent", "expecting remaining tokens to be strings"); @@ -189,12 +192,12 @@ describe("Incremental updates", () => { it("Re-parse with unterminated block comment", () => { const lineNumber: number = 3; const document: MockDocument = new MockDocument(ORIGINAL_QUERY); - const modified: string = document.lines[lineNumber].replace(`rce),`, `rce), /* my open comment`); + const modified: string = ArrayUtils.assertGet(document.lines, lineNumber).replace(`rce),`, `rce), /* my open comment`); const count: number = document.applyChangeAndTokenize(modified, lineNumber); expect(count).equals(document.lines.length - lineNumber, "remaining lines should have been tokenized"); for (let index: number = lineNumber + 1; index < document.lineTokens.length; index += 1) { - const lineTokens: ReadonlyArray = document.lineTokens[index]; + const lineTokens: ReadonlyArray = ArrayUtils.assertGet(document.lineTokens, index); lineTokens.forEach((token: IToken) => { expect(token.scopes).equals("MultilineCommentContent", "expecting remaining tokens to be comments"); diff --git a/src/test/libraryTest/tokenizer/tokenizerSimple.test.ts b/src/test/libraryTest/tokenizer/tokenizerSimple.test.ts index 1f27a07a..34b7e2c2 100644 --- a/src/test/libraryTest/tokenizer/tokenizerSimple.test.ts +++ b/src/test/libraryTest/tokenizer/tokenizerSimple.test.ts @@ -5,6 +5,7 @@ import "mocha"; import { expect } from "chai"; import { ILineTokens, IState, IToken, Tokenizer, TokenizerState } from "../../testUtils/tokenizerTestUtils"; +import { ArrayUtils } from "../../../powerquery-parser/common"; const tokenizer: Tokenizer = new Tokenizer(`\n`); const initialState: TokenizerState = tokenizer.getInitialState() as TokenizerState; @@ -16,14 +17,14 @@ function tokenizeLines(query: string, expectedTokenCounts: number[]): void { expect(lines.length).equals(expectedTokenCounts.length); for (let index: number = 0; index < lines.length; index += 1) { - const r: ILineTokens = tokenizer.tokenize(lines[index], state); + const r: ILineTokens = tokenizer.tokenize(ArrayUtils.assertGet(lines, index), state); expect(!state.equals(r.endState), `state should have changed.`); - expect(r.tokens.length).equals(expectedTokenCounts[index], `unexpected token count`); + expect(r.tokens.length).equals(ArrayUtils.assertGet(expectedTokenCounts, index), `unexpected token count`); state = r.endState as TokenizerState; r.tokens.forEach((token: IToken) => { - expect(token.startIndex).is.lessThan(lines[index].length); + expect(token.startIndex).is.lessThan(ArrayUtils.assertGet(lines, index).length); }); } } diff --git a/src/test/testUtils/tokenizerTestUtils.ts b/src/test/testUtils/tokenizerTestUtils.ts index 35ddb0cc..4d133520 100644 --- a/src/test/testUtils/tokenizerTestUtils.ts +++ b/src/test/testUtils/tokenizerTestUtils.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { ArrayUtils } from "../../powerquery-parser/common"; import { DefaultLocale, Language, ResultUtils } from "../../powerquery-parser"; import { Lexer } from "../.."; @@ -49,7 +50,7 @@ export class Tokenizer implements TokensProvider { const newLexerState: Lexer.State = triedLex.value; return { - tokens: newLexerState.lines[newLexerState.lines.length - 1].tokens.map(Tokenizer.ITokenFrom), + tokens: ArrayUtils.assertGet(newLexerState.lines, newLexerState.lines.length - 1).tokens.map(Tokenizer.ITokenFrom), endState: new TokenizerState(newLexerState), }; } @@ -82,8 +83,8 @@ export class TokenizerState implements IState { } // Compare last line state. - const leftLastLine: Lexer.TLine = this.lexerState.lines[this.lexerState.lines.length - 1]; - const rightLastLine: Lexer.TLine = rightLexerState.lines[rightLexerState.lines.length - 1]; + const leftLastLine: Lexer.TLine = ArrayUtils.assertGet(this.lexerState.lines, this.lexerState.lines.length - 1); + const rightLastLine: Lexer.TLine = ArrayUtils.assertGet(rightLexerState.lines, rightLexerState.lines.length - 1); return leftLastLine.lineModeEnd === rightLastLine.lineModeEnd; } diff --git a/tsconfig.json b/tsconfig.json index 420611f3..190674bd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, + "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, "outDir": "lib",