Change:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes/No <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.66.2 OS Version: Win11 (but presumably affects all OSs) See , Steps to Reproduce: the Docker extension installed, open the Dockerfile example from , or simply enter this into a Dockerfile: VSCode freezing up We are independently working on the issue where negative-valued semantic tokens are returned, but invalid token data from the language service should not be causing VSCode to freeze up.
/cc
I'm not very familiar with semantic tokens, so if there's more info you can add here feel free!
I've tried reproducing this in a simple VS Code extension but have not had much luck. I do not see anything obvious in the LSP client bridge code so I feel like it should be reproducible in a regular extension also...
Yes, agree. This should be reproducible in a VS Code extension also.
Thanks for the speedy fix!

Discussion:
private readonly _hashTable: HashTable; private _hasWarnedOverlappingTokens: boolean; <ins> private _hasWarnedInvalidLengthTokens: boolean; </ins> constructor( private readonly _legend: SemanticTokensLegend,

) { this._hashTable = new HashTable(); this._hasWarnedOverlappingTokens = false; <ins> this._hasWarnedInvalidLengthTokens = false; </ins> } public getMetadata(tokenTypeIndex: number, tokenModifierSet: number, languageId: string): number {

} } <ins> public warnInvalidLengthSemanticTokens(lineNumber: number, startColumn: number): void { if (!this._hasWarnedInvalidLengthTokens) { this._hasWarnedInvalidLengthTokens = true; console.warn(`Semantic token with invalid length detected at lineNumber ${lineNumber}, column ${startColumn}`); } } </ins> } const enum SemanticColoringConstants {

let destOffset = 0; let areaLine = 0; let prevLineNumber = 0; <del> let prevStartCharacter = 0; </del> let prevEndCharacter = 0; while (tokenIndex < tokenEndIndex) { const srcOffset = 5 * tokenIndex; const deltaLine = srcData[srcOffset]; const deltaCharacter = srcData[srcOffset + 1]; <del> // Casting both `lineNumber` and `startCharacter` here to uint32 using `|0` // to do checks below with the actual value that will be inserted in the Uint32Array result </del> <ins> // Casting both `lineNumber`, `startCharacter` and `endCharacter` here to uint32 using `|0` // to validate below with the actual values that will be inserted in the Uint32Array result </ins> const lineNumber = (lastLineNumber + deltaLine) | 0; const startCharacter = (deltaLine === 0 ? (lastStartCharacter + deltaCharacter) | 0 : deltaCharacter); const length = srcData[srcOffset + 2]; <ins> const endCharacter = (startCharacter + length) | 0; </ins> const tokenTypeIndex = srcData[srcOffset + 3]; const tokenModifierSet = srcData[srcOffset + 4]; <del> const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet, languageId); if (metadata !== SemanticTokensProviderStylingConstants.NO_STYLING) { if (areaLine === 0) { areaLine = lineNumber; } if (prevLineNumber === lineNumber && prevEndCharacter > startCharacter) { styling.warnOverlappingSemanticTokens(lineNumber, startCharacter + 1); if (prevStartCharacter < startCharacter) { // the previous token survives after the overlapping one destData[destOffset - 4 + 2] = startCharacter; } else { // the previous token is entirely covered by the overlapping one destOffset -= 4; </del> <ins> if (endCharacter <= startCharacter) { // this token is invalid (most likely a negative length casted to uint32) styling.warnInvalidLengthSemanticTokens(lineNumber, startCharacter + 1); } else if (prevLineNumber === lineNumber && prevEndCharacter > startCharacter) { // this token overlaps with the previous token styling.warnOverlappingSemanticTokens(lineNumber, startCharacter + 1); } else { const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet, languageId); if (metadata !== SemanticTokensProviderStylingConstants.NO_STYLING) { if (areaLine === 0) { areaLine = lineNumber; </ins> } <ins> destData[destOffset] = lineNumber - areaLine; destData[destOffset + 1] = startCharacter; destData[destOffset + 2] = endCharacter; destData[destOffset + 3] = metadata; destOffset += 4; prevLineNumber = lineNumber; prevEndCharacter = endCharacter; </ins> } <del> destData[destOffset] = lineNumber - areaLine; destData[destOffset + 1] = startCharacter; destData[destOffset + 2] = startCharacter + length; destData[destOffset + 3] = metadata; destOffset += 4; prevLineNumber = lineNumber; prevStartCharacter = startCharacter; prevEndCharacter = startCharacter + length; </del> } lastLineNumber = lineNumber;

1, 48, 53, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (6 << MetadataConsts.FOREGROUND_OFFSET)), 2, 12, 20, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (7 << MetadataConsts.FOREGROUND_OFFSET)), 2, 31, 36, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (8 << MetadataConsts.FOREGROUND_OFFSET)), <del> 2, 36, 41, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (9 << MetadataConsts.FOREGROUND_OFFSET)), </del> <ins> 2, 38, 39, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (9 << MetadataConsts.FOREGROUND_OFFSET)), ])); assert.deepStrictEqual(result.toString(), expected.toString()); }); test('issue #148651: VSCode UI process can hang if a semantic token with negative values is returned by language service', () => { const languageId = 'dockerfile'; disposables.add(languageService.registerLanguage({ id: languageId })); const legend = { tokenTypes: ['st0', 'st1', 'st2', 'st3', 'st4', 'st5', 'st6', 'st7', 'st8', 'st9'], tokenModifiers: ['stm0', 'stm1', 'stm2'] }; instantiationService.stub(IThemeService, <Partial<IThemeService>>{ getColorTheme() { return { getTokenStyleMetadata: (tokenType, tokenModifiers, languageId): ITokenStyle => { return { foreground: parseInt(tokenType.substr(2), 10), bold: undefined, underline: undefined, strikethrough: undefined, italic: undefined }; } }; } }); const styling = instantiationService.createInstance(SemanticTokensProviderStyling, legend); const badTokens = { data: new Uint32Array([ 0, 0, 3, 0, 0, 0, 4, 2, 2, 0, 0, 2, 3, 8, 0, 0, 3, 1, 9, 0, 0, 1, 1, 10, 0, 0, 1, 4, 8, 0, 0, 4, 4294967292, 2, 0, 0, 4294967292, 4294967294, 8, 0, 0, 4294967294, 1, 9, 0, 0, 1, 1, 10, 0, 0, 1, 3, 8, 0, 0, 3, 4294967291, 8, 0, 0, 4294967291, 1, 9, 0, 0, 1, 1, 10, 0, 0, 1, 4, 8, 0 ]) }; const result = toMultilineTokens2(badTokens, styling, languageId); const expected = SparseMultilineTokens.create(1, new Uint32Array([ 0, 4, 6, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (1 << MetadataConsts.FOREGROUND_OFFSET)), 0, 6, 9, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (2 << MetadataConsts.FOREGROUND_OFFSET)), 0, 9, 10, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (3 << MetadataConsts.FOREGROUND_OFFSET)), 0, 11, 15, (MetadataConsts.SEMANTIC_USE_FOREGROUND | (4 << MetadataConsts.FOREGROUND_OFFSET)), </ins> ])); assert.deepStrictEqual(result.toString(), expected.toString()); });