Repository: eslint/eslint
File: lib/languages/js/source-code/source-code.js (JavaScript)

Code before:
		 */
		if (index === this.text.length) {
			return {
				line: this.lines.length,
				column: this.lines.at(-1).length,
			};
		}

		/*
		 * To figure out which line index is on, determine the last place at which index could
		 * be inserted into lineStartIndices to keep the list sorted.
		 */
		const lineNumber =
			index >= this.lineStartIndices.at(-1)
				? this.lineStartIndices.length
				: findLineNumberBinarySearch(this.lineStartIndices, index);

		const loc = {
			line: lineNumber,
			column: index - this.lineStartIndices[lineNumber - 1],
		};

		/*
		 * Cache the result for future lookups.
		 */
		indexToLocCache.set(index, loc);

		return loc;
	}

	/**
	 * Converts a (line, column) pair into a range index.
	 * @param {Object} loc A line/column location
	 * @param {number} loc.line The line number of the location (1-indexed)
	 * @param {number} loc.column The column number of the location (0-indexed)
	 * @throws {TypeError|RangeError} If `loc` is not an object with a numeric
	 *   `line` and `column`, if the `line` is less than or equal to zero or
	 *   the line or column is out of the expected range.
	 * @returns {number} The range index of the location in the file.
	 * @public
	 */
	getIndexFromLoc(loc) {
		if (
			typeof loc !== "object" ||
			typeof loc.line !== "number" ||
			typeof loc.column !== "number"
		) {
			throw new TypeError(
				"Expected `loc` to be an object with numeric `line` and `column` properties.",
			);
		}

Review comment (bug):
I'm not sure if caching this data is necessary. This method is typically used to calculate the report location when it isn't the start or end of a node/token, but e.g., something inside, like a part of a string/regex literal, so I think it should rarely happen that this function is called multiple times with the same index. A danger of caching is that it now returns the same instance for the same index, so if a rule happens to modify this object, then other rules would get the wrong data.

Code after:
			index >= this.lineStartIndices.at(-1)
				? this.lineStartIndices.length
				: findLineNumberBinarySearch(this.lineStartIndices, index);

		return {
			line: lineNumber,
			column: index - this.lineStartIndices[lineNumber - 1],
		};
	}

	/**
	 * Converts a (line, column) pair into a range index.
	 * @param {Object} loc A line/column location
	 * @param {number} loc.line The line number of the location (1-indexed)
	 * @param {number} loc.column The column number of the location (0-indexed)
	 * @throws {TypeError|RangeError} If `loc` is not an object with a numeric
	 *   `line` and `column`, if the `line` is less than or equal to zero or
	 *   the line or column is out of the expected range.
	 * @returns {number} The range index of the location in the file.
	 * @public
	 */
	getIndexFromLoc(loc) {
		if (
			typeof loc !== "object" ||
			typeof loc.line !== "number" ||
			typeof loc.column !== "number"
		) {
			throw new TypeError(
				"Expected `loc` to be an object with numeric `line` and `column` properties.",
			);
		}

		if (loc.line <= 0) {
			throw new RangeError(
				`Line number out of range (line ${loc.line} requested). Line numbers should be 1-based.`,
			);
		}

		if (loc.line > this.lineStartIndices.length) {
			throw new RangeError(
				`Line number out of range (line ${loc.line} requested, but only ${this.lineStartIndices.length} lines present).`,
			);
		}

		const lineStartIndex = this.lineStartIndices[loc.line - 1];
		const lineEndIndex =
			loc.line === this.lineStartIndices.length
				? this.text.length
				: this.lineStartIndices[loc.line];
		const positionIndex = lineStartIndex + loc.column;