From 2221be118701cbe454707e1f1d94871c9939218b Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Fri, 2 Oct 2026 16:13:10 -0400 Subject: [PATCH] Don't shrink the import path range again when cloning The ImportStatement constructor narrows the path token's range to exclude the quotes. A clone copies the already-narrowed token, so the constructor narrowed it a second time. Only narrow a token whose range still includes the quotes. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/parser/AstNode.spec.ts | 11 ++++++++++- src/parser/Statement.ts | 9 +++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/parser/AstNode.spec.ts b/src/parser/AstNode.spec.ts index 87ccc4d93..e68226a9d 100644 --- a/src/parser/AstNode.spec.ts +++ b/src/parser/AstNode.spec.ts @@ -7,7 +7,7 @@ import type { AAIndexedMemberExpression, AALiteralExpression, AAMemberExpression import { expectZeroDiagnostics } from '../testHelpers.spec'; import { tempDir, rootDir, outDir } from '../testHelpers.spec'; import { isAAIndexedMemberExpression, isAALiteralExpression, isAAMemberExpression, isAnnotationExpression, isArrayLiteralExpression, isAssignmentStatement, isBinaryExpression, isBlock, isCallExpression, isCallfuncExpression, isCatchStatement, isClassStatement, isConstStatement, isDimStatement, isDottedGetExpression, isDottedSetStatement, isEnumMemberStatement, isEnumStatement, isExpressionStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isGroupingExpression, isIfStatement, isIncrementStatement, isIndexedGetExpression, isIndexedSetStatement, isInterfaceFieldStatement, isInterfaceMethodStatement, isInterfaceStatement, isMethodStatement, isNamespaceStatement, isNewExpression, isNullCoalescingExpression, isPrintStatement, isReturnStatement, isTaggedTemplateStringExpression, isTemplateStringExpression, isTemplateStringQuasiExpression, isTernaryExpression, isThrowStatement, isTryCatchStatement, isTypecastExpression, isUnaryExpression, isVariableExpression, isLiteralExpression, isWhileStatement, isXmlAttributeGetExpression } from '../astUtils/reflection'; -import type { ClassStatement, FunctionStatement, InterfaceFieldStatement, InterfaceMethodStatement, MethodStatement, InterfaceStatement, CatchStatement, ThrowStatement, EnumStatement, EnumMemberStatement, ConstStatement, Block, PrintStatement, DimStatement, ForStatement, WhileStatement, IndexedSetStatement, NamespaceStatement, TryCatchStatement, DottedSetStatement, ExpressionStatement } from './Statement'; +import type { ImportStatement, ClassStatement, FunctionStatement, InterfaceFieldStatement, InterfaceMethodStatement, MethodStatement, InterfaceStatement, CatchStatement, ThrowStatement, EnumStatement, EnumMemberStatement, ConstStatement, Block, PrintStatement, DimStatement, ForStatement, WhileStatement, IndexedSetStatement, NamespaceStatement, TryCatchStatement, DottedSetStatement, ExpressionStatement } from './Statement'; import { AssignmentStatement, EmptyStatement } from './Statement'; import { ParseMode, Parser } from './Parser'; import type { AstNode } from './AstNode'; @@ -1205,6 +1205,15 @@ describe('AstNode', () => { testClone(original); }); + it('does not shrink the ImportStatement path range again when cloning', () => { + const original = Parser.parse('import "Something.brs"').ast.statements[0] as ImportStatement; + //the path range excludes the quotes + expect(util.getLocation(original.tokens.path).range).to.eql(util.createRange(0, 8, 0, 21)); + const clone = original.clone(); + expect(util.getLocation(clone.tokens.path).range).to.eql(util.createRange(0, 8, 0, 21)); + expect(util.getLocation(clone.clone().tokens.path).range).to.eql(util.createRange(0, 8, 0, 21)); + }); + it('clones BinaryExpression', () => { const original = Parser.parse(` diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts index f740e789e..848fe2480 100644 --- a/src/parser/Statement.ts +++ b/src/parser/Statement.ts @@ -2099,10 +2099,11 @@ export class ImportStatement extends Statement implements TypedefProvider { if (this.tokens.path) { //remove quotes this.filePath = this.tokens.path.text.replace(/"/g, ''); - if (this.tokens.path.source) { - //adjust the range to exclude the quotes - this.tokens.path.pos++; - this.tokens.path.end--; + //adjust the range to exclude the quotes (but only if that hasn't already been done, such as for a cloned token) + const path = this.tokens.path; + if (path.source && path.text.startsWith('"') && path.end - path.pos === path.text.length) { + path.pos++; + path.end--; } } }