@zzzen/pyright-internal 1.2.0-dev.20261011 → 1.2.0-dev.20261101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/analyzer/binder.js +28 -9
  2. package/dist/analyzer/binder.js.map +1 -1
  3. package/dist/analyzer/checker.js +8 -3
  4. package/dist/analyzer/checker.js.map +1 -1
  5. package/dist/analyzer/codeFlowEngine.js +76 -30
  6. package/dist/analyzer/codeFlowEngine.js.map +1 -1
  7. package/dist/analyzer/dataClasses.js +14 -5
  8. package/dist/analyzer/dataClasses.js.map +1 -1
  9. package/dist/analyzer/decorators.d.ts +1 -1
  10. package/dist/analyzer/decorators.js +22 -2
  11. package/dist/analyzer/decorators.js.map +1 -1
  12. package/dist/analyzer/namedTuples.d.ts +2 -1
  13. package/dist/analyzer/namedTuples.js +63 -35
  14. package/dist/analyzer/namedTuples.js.map +1 -1
  15. package/dist/analyzer/operations.js +45 -1
  16. package/dist/analyzer/operations.js.map +1 -1
  17. package/dist/analyzer/overloadResultAdmission.js +115 -3
  18. package/dist/analyzer/overloadResultAdmission.js.map +1 -1
  19. package/dist/analyzer/overloadResultController.js +36 -10
  20. package/dist/analyzer/overloadResultController.js.map +1 -1
  21. package/dist/analyzer/overloadResultSelector.js +32 -12
  22. package/dist/analyzer/overloadResultSelector.js.map +1 -1
  23. package/dist/analyzer/parseTreeUtils.d.ts +3 -0
  24. package/dist/analyzer/parseTreeUtils.js +56 -0
  25. package/dist/analyzer/parseTreeUtils.js.map +1 -1
  26. package/dist/analyzer/patternMatching.js +67 -27
  27. package/dist/analyzer/patternMatching.js.map +1 -1
  28. package/dist/analyzer/program.d.ts +3 -0
  29. package/dist/analyzer/program.js +34 -9
  30. package/dist/analyzer/program.js.map +1 -1
  31. package/dist/analyzer/sentinel.js +7 -1
  32. package/dist/analyzer/sentinel.js.map +1 -1
  33. package/dist/analyzer/service.js +6 -2
  34. package/dist/analyzer/service.js.map +1 -1
  35. package/dist/analyzer/sourceEnumerator.d.ts +3 -1
  36. package/dist/analyzer/sourceEnumerator.js +30 -11
  37. package/dist/analyzer/sourceEnumerator.js.map +1 -1
  38. package/dist/analyzer/sourceFile.js +13 -6
  39. package/dist/analyzer/sourceFile.js.map +1 -1
  40. package/dist/analyzer/sourceMapper.d.ts +1 -0
  41. package/dist/analyzer/sourceMapper.js +10 -4
  42. package/dist/analyzer/sourceMapper.js.map +1 -1
  43. package/dist/analyzer/tupleComparison.d.ts +6 -0
  44. package/dist/analyzer/tupleComparison.js +731 -0
  45. package/dist/analyzer/tupleComparison.js.map +1 -0
  46. package/dist/analyzer/tuples.js +52 -10
  47. package/dist/analyzer/tuples.js.map +1 -1
  48. package/dist/analyzer/typeEvaluator.js +508 -63
  49. package/dist/analyzer/typeEvaluator.js.map +1 -1
  50. package/dist/analyzer/typeEvaluatorTypes.d.ts +1 -1
  51. package/dist/analyzer/typeEvaluatorTypes.js +2 -3
  52. package/dist/analyzer/typeEvaluatorTypes.js.map +1 -1
  53. package/dist/analyzer/typeGuards.d.ts +6 -1
  54. package/dist/analyzer/typeGuards.js +373 -72
  55. package/dist/analyzer/typeGuards.js.map +1 -1
  56. package/dist/analyzer/typeStubRenderer.d.ts +6 -1
  57. package/dist/analyzer/typeStubRenderer.js +19 -12
  58. package/dist/analyzer/typeStubRenderer.js.map +1 -1
  59. package/dist/analyzer/typeUtils.js +3 -0
  60. package/dist/analyzer/typeUtils.js.map +1 -1
  61. package/dist/analyzer/typedDicts.d.ts +2 -1
  62. package/dist/analyzer/typedDicts.js +49 -10
  63. package/dist/analyzer/typedDicts.js.map +1 -1
  64. package/dist/analyzer/types.d.ts +14 -0
  65. package/dist/analyzer/types.js +215 -10
  66. package/dist/analyzer/types.js.map +1 -1
  67. package/dist/common/configOptions.d.ts +1 -0
  68. package/dist/common/configOptions.js +9 -0
  69. package/dist/common/configOptions.js.map +1 -1
  70. package/dist/common/processUtils.d.ts +6 -0
  71. package/dist/common/processUtils.js +56 -1
  72. package/dist/common/processUtils.js.map +1 -1
  73. package/dist/common/textEditTracker.js +1 -1
  74. package/dist/common/textEditTracker.js.map +1 -1
  75. package/dist/common/windowsProcessTree.d.ts +2 -0
  76. package/dist/common/windowsProcessTree.js +23 -0
  77. package/dist/common/windowsProcessTree.js.map +1 -1
  78. package/dist/common/windowsProcessTree.json +273 -34
  79. package/dist/languageServerBase.d.ts +3 -0
  80. package/dist/languageServerBase.js +44 -12
  81. package/dist/languageServerBase.js.map +1 -1
  82. package/dist/languageService/callHierarchyProvider.js +6 -6
  83. package/dist/languageService/callHierarchyProvider.js.map +1 -1
  84. package/dist/languageService/referencesProvider.js +3 -3
  85. package/dist/languageService/referencesProvider.js.map +1 -1
  86. package/dist/languageService/renameProvider.js +1 -1
  87. package/dist/languageService/renameProvider.js.map +1 -1
  88. package/dist/languageService/symbolIndexer.js +2 -2
  89. package/dist/languageService/symbolIndexer.js.map +1 -1
  90. package/dist/localization/localize.d.ts +2 -0
  91. package/dist/localization/localize.js +2 -0
  92. package/dist/localization/localize.js.map +1 -1
  93. package/dist/localization/package.nls.en-us.json +8 -0
  94. package/dist/parser/parseTreeTokenReuse.d.ts +34 -0
  95. package/dist/parser/parseTreeTokenReuse.js +897 -0
  96. package/dist/parser/parseTreeTokenReuse.js.map +1 -0
  97. package/dist/parser/tokenizer.d.ts +10 -1
  98. package/dist/parser/tokenizer.js +184 -130
  99. package/dist/parser/tokenizer.js.map +1 -1
  100. package/dist/tests/checker.test.js +58 -0
  101. package/dist/tests/checker.test.js.map +1 -1
  102. package/dist/tests/config.test.js +126 -0
  103. package/dist/tests/config.test.js.map +1 -1
  104. package/dist/tests/fourSlashParser.test.js +35 -0
  105. package/dist/tests/fourSlashParser.test.js.map +1 -1
  106. package/dist/tests/fourslash/hover.typedDict.get.fourslash.js +11 -0
  107. package/dist/tests/fourslash/hover.typedDict.get.fourslash.js.map +1 -1
  108. package/dist/tests/harness/fourslash/fourSlashParser.js +14 -4
  109. package/dist/tests/harness/fourslash/fourSlashParser.js.map +1 -1
  110. package/dist/tests/languageServer.test.js +15 -0
  111. package/dist/tests/languageServer.test.js.map +1 -1
  112. package/dist/tests/lsp/languageServer.js +6 -6
  113. package/dist/tests/lsp/languageServer.js.map +1 -1
  114. package/dist/tests/mypyPrimerAnalysis.test.js +133 -0
  115. package/dist/tests/mypyPrimerAnalysis.test.js.map +1 -1
  116. package/dist/tests/overloadResultController.test.js +379 -1
  117. package/dist/tests/overloadResultController.test.js.map +1 -1
  118. package/dist/tests/signatureHelp.test.js +34 -0
  119. package/dist/tests/signatureHelp.test.js.map +1 -1
  120. package/dist/tests/sourceEnumeratorSymlink.test.js +98 -13
  121. package/dist/tests/sourceEnumeratorSymlink.test.js.map +1 -1
  122. package/dist/tests/sourceMapperUtils.test.js +143 -0
  123. package/dist/tests/sourceMapperUtils.test.js.map +1 -1
  124. package/dist/tests/typeEvaluator1.test.js +22 -3
  125. package/dist/tests/typeEvaluator1.test.js.map +1 -1
  126. package/dist/tests/typeEvaluator2.test.js +54 -1
  127. package/dist/tests/typeEvaluator2.test.js.map +1 -1
  128. package/dist/tests/typeEvaluator4.test.js +10 -3
  129. package/dist/tests/typeEvaluator4.test.js.map +1 -1
  130. package/dist/tests/typeEvaluator5.test.js +1 -1
  131. package/dist/tests/typeEvaluator6.test.js +55 -0
  132. package/dist/tests/typeEvaluator6.test.js.map +1 -1
  133. package/dist/tests/typeEvaluator7.test.js +30 -0
  134. package/dist/tests/typeEvaluator7.test.js.map +1 -1
  135. package/dist/tests/typeEvaluator8.test.js +1293 -2
  136. package/dist/tests/typeEvaluator8.test.js.map +1 -1
  137. package/dist/tests/types.test.js +55 -0
  138. package/dist/tests/types.test.js.map +1 -1
  139. package/dist/typeServer/programWrapper.js +8 -2
  140. package/dist/typeServer/programWrapper.js.map +1 -1
  141. package/dist/typeServer/server.d.ts +12 -2
  142. package/dist/typeServer/server.js +97 -0
  143. package/dist/typeServer/server.js.map +1 -1
  144. package/package.json +3 -3
@@ -9,6 +9,7 @@
9
9
  "{",
10
10
  " private const uint QueryAccess = 0x1000 | 0x100000;",
11
11
  " private const uint ControlAccess = QueryAccess | 0x0001 | 0x0800;",
12
+ " private const int ProcessIsTerminating = unchecked((int)0xC000010A);",
12
13
  " private IntPtr handle;",
13
14
  " private bool controlled;",
14
15
  " private bool suspended;",
@@ -51,6 +52,18 @@
51
52
  " public long CreationTimeUtcTicks;",
52
53
  " }",
53
54
  "",
55
+ " public sealed class CandidateOpenException : Win32Exception",
56
+ " {",
57
+ " public readonly int ProcessId;",
58
+ "",
59
+ " public CandidateOpenException(int error, int processId)",
60
+ " : base(error, \"Cannot pin candidate process \" + processId + \" (Win32 error \" + error + \": \" + new Win32Exception(error).Message + \")\")",
61
+ " {",
62
+ " ProcessId = processId;",
63
+ " if (error == 87 && processId > 0) Data[\"PylanceAbsentProcessId\"] = processId;",
64
+ " }",
65
+ " }",
66
+ "",
54
67
  " [DllImport(\"kernel32.dll\", SetLastError = true)]",
55
68
  " private static extern IntPtr OpenProcess(uint access, bool inherit, int pid);",
56
69
  " [DllImport(\"kernel32.dll\", SetLastError = true)]",
@@ -99,7 +112,7 @@
99
112
  " if (value == IntPtr.Zero)",
100
113
  " {",
101
114
  " int error = Marshal.GetLastWin32Error();",
102
- " throw new Win32Exception(error, \"Cannot pin candidate process \" + pid + \" (Win32 error \" + error + \": \" + new Win32Exception(error).Message + \")\");",
115
+ " throw new CandidateOpenException(error, pid);",
103
116
  " }",
104
117
  " return new PylanceOwnedWindowsProcess(value);",
105
118
  " }",
@@ -141,18 +154,28 @@
141
154
  " finally { if (value != IntPtr.Zero) CloseHandle(value); }",
142
155
  " }",
143
156
  "",
144
- " public void Suspend()",
157
+ " public enum SuspendResult { AlreadyExited, Suspended, ExitedDuringSuspend }",
158
+ "",
159
+ " public SuspendResult Suspend(int terminatingWaitMilliseconds)",
145
160
  " {",
146
- " if (HasExited) return;",
161
+ " if (HasExited) return SuspendResult.AlreadyExited;",
147
162
  " AcquireControl();",
148
- " CheckStatus(NtSuspendProcess(handle), \"suspend owned process \" + Id);",
163
+ " int status = NtSuspendProcess(handle);",
164
+ " if (status == ProcessIsTerminating)",
165
+ " {",
166
+ " if (!WaitForExit(terminatingWaitMilliseconds))",
167
+ " throw new InvalidOperationException(\"Owned process \" + Id + \" is terminating but has not exited; cleanup is incomplete.\");",
168
+ " return SuspendResult.ExitedDuringSuspend;",
169
+ " }",
170
+ " CheckStatus(status, \"suspend owned process \" + Id);",
149
171
  " suspended = true;",
172
+ " return SuspendResult.Suspended;",
150
173
  " }",
151
174
  "",
152
175
  " public void Resume()",
153
176
  " {",
154
177
  " if (!suspended) return;",
155
- " if (!HasExited) CheckStatus(NtResumeProcess(handle), \"resume owned process \" + Id);",
178
+ " if (!HasExited) CheckControlStatus(NtResumeProcess(handle), \"resume owned process \" + Id);",
156
179
  " suspended = false;",
157
180
  " }",
158
181
  "",
@@ -218,6 +241,14 @@
218
241
  " }",
219
242
  " }",
220
243
  "",
244
+ " private static bool CheckControlStatus(int status, string operation)",
245
+ " {",
246
+ " // Exit can start after HasExited; the tree must still wait for the retained handle to signal.",
247
+ " if (status == ProcessIsTerminating) return false;",
248
+ " CheckStatus(status, operation);",
249
+ " return true;",
250
+ " }",
251
+ "",
221
252
  " private static void CheckStatus(int status, string operation)",
222
253
  " {",
223
254
  " if (status < 0) throw new InvalidOperationException(\"Cannot \" + operation + \" (NTSTATUS 0x\" + status.ToString(\"X8\") + \").\");",
@@ -230,6 +261,99 @@
230
261
  "}"
231
262
  ],
232
263
  "functions": [
264
+ "function New-OwnedWindowsProcessLineageDiagnostics {",
265
+ " param(",
266
+ " [Parameter(Mandatory = $true)] [hashtable] $Known,",
267
+ " [Parameter(Mandatory = $true)] [object[]] $Hints,",
268
+ " [Parameter(Mandatory = $true)] $Parent,",
269
+ " [Parameter(Mandatory = $true)] [int] $Round,",
270
+ " [Parameter(Mandatory = $true)] [int] $HintIndex,",
271
+ " [Parameter(Mandatory = $true)] [int] $HintId,",
272
+ " [Parameter(Mandatory = $true)] [int] $HintParentId,",
273
+ " [Parameter(Mandatory = $true)] [long] $HintBirth,",
274
+ " [Parameter(Mandatory = $true)] [string] $Identity",
275
+ " )",
276
+ " $parentMatches = [System.Collections.Generic.List[object]]::new()",
277
+ " $parentMatchesTruncated = $false",
278
+ " for ($index = 0; $index -lt $Hints.Count; $index++) {",
279
+ " $entry = $Hints[$index]",
280
+ " if ([int]$entry.Id -ne $HintParentId) { continue }",
281
+ " if ($parentMatches.Count -ge 4) { $parentMatchesTruncated = $true; continue }",
282
+ " $parentMatches.Add([ordered]@{",
283
+ " snapshotIndex = $index",
284
+ " processId = [string][int]$entry.Id",
285
+ " parentProcessId = [string][int]$entry.ParentId",
286
+ " creationTimeUtcTicks = [string][long]$entry.CreationTimeUtcTicks",
287
+ " })",
288
+ " }",
289
+ " $chain = [System.Collections.Generic.List[object]]::new()",
290
+ " $cursor = $Parent",
291
+ " for ($depth = 0; $null -ne $cursor -and $depth -lt 16; $depth++) {",
292
+ " $chain.Add([ordered]@{",
293
+ " processId = [string][int]$cursor.Id",
294
+ " parentProcessId = [string][int]$cursor.ParentId",
295
+ " creationTimeUtcTicks = [string][long]$cursor.CreationTimeUtcTicks",
296
+ " acceptedSnapshotGeneration = [int]$cursor.SnapshotGeneration",
297
+ " acceptedSnapshotIndex = [int]$cursor.SnapshotIndex",
298
+ " nativeOpenOutcome = [string]$cursor.OpenOutcome",
299
+ " })",
300
+ " if (!$Known.ContainsKey([int]$cursor.ParentId)) { $cursor = $null; break }",
301
+ " $cursor = $Known[[int]$cursor.ParentId]",
302
+ " }",
303
+ " $chainTruncated = $null -ne $cursor",
304
+ " $root = @($Known.Values | Where-Object { $_.Depth -eq 0 })[0]",
305
+ " $generationMatches = $Parent.SnapshotGeneration -eq $Round",
306
+ " $frozenChildIdentityPresent = $Parent.AllowedChildren.ContainsKey($Identity)",
307
+ " $exactParentIdentityPresent = $false",
308
+ " foreach ($match in $parentMatches) {",
309
+ " if ($match.creationTimeUtcTicks -eq [string][long]$Parent.CreationTimeUtcTicks) {",
310
+ " $exactParentIdentityPresent = $true",
311
+ " break",
312
+ " }",
313
+ " }",
314
+ " $diagnostics = [ordered]@{",
315
+ " version = 1",
316
+ " root = [ordered]@{",
317
+ " processId = [string][int]$root.Id",
318
+ " creationTimeUtcTicks = [string][long]$root.CreationTimeUtcTicks",
319
+ " }",
320
+ " rejectedHint = [ordered]@{",
321
+ " processId = [string]$HintId",
322
+ " parentProcessId = [string]$HintParentId",
323
+ " creationTimeUtcTicks = [string]$HintBirth",
324
+ " snapshotGeneration = $Round",
325
+ " snapshotIndex = $HintIndex",
326
+ " nativeOpenOutcome = 'not-attempted-lineage-guard'",
327
+ " }",
328
+ " vanishedParent = [ordered]@{",
329
+ " processId = [string][int]$Parent.Id",
330
+ " parentProcessId = [string][int]$Parent.ParentId",
331
+ " creationTimeUtcTicks = [string][long]$Parent.CreationTimeUtcTicks",
332
+ " acceptedSnapshotGeneration = [int]$Parent.SnapshotGeneration",
333
+ " acceptedSnapshotIndex = [int]$Parent.SnapshotIndex",
334
+ " nativeOpenOutcome = [string]$Parent.OpenOutcome",
335
+ " }",
336
+ " currentSnapshot = [ordered]@{",
337
+ " generation = $Round",
338
+ " entryCount = $Hints.Count",
339
+ " exactVanishedParentIdentityPresent = $exactParentIdentityPresent",
340
+ " vanishedParentPidMatches = @($parentMatches.ToArray())",
341
+ " vanishedParentPidMatchesTruncated = $parentMatchesTruncated",
342
+ " }",
343
+ " failedOwnershipPredicate = [ordered]@{",
344
+ " expression = 'parentSnapshotGenerationMatches && frozenChildIdentityPresent'",
345
+ " parentSnapshotGenerationMatches = $generationMatches",
346
+ " frozenChildIdentityPresent = $frozenChildIdentityPresent",
347
+ " rejectedIdentity = $Identity",
348
+ " }",
349
+ " parentChainOrder = 'vanished-parent-to-root'",
350
+ " parentChain = @($chain.ToArray())",
351
+ " parentChainTruncated = $chainTruncated",
352
+ " }",
353
+ " $json = ConvertTo-Json -InputObject $diagnostics -Compress -Depth 6",
354
+ " return $json",
355
+ "}",
356
+ "",
233
357
  "function Stop-OwnedWindowsProcessTree {",
234
358
  " param(",
235
359
  " [Parameter(Mandatory = $true)] $Root,",
@@ -240,48 +364,163 @@
240
364
  " $watch = [System.Diagnostics.Stopwatch]::StartNew()",
241
365
  " $held = [System.Collections.Generic.List[object]]::new()",
242
366
  " $known = @{}",
367
+ " $missing = @{}",
243
368
  " $failure = $null",
244
369
  " $cleanupErrors = [System.Collections.Generic.List[string]]::new()",
245
370
  " try {",
246
371
  " if ($Root.Id -eq $PID) { throw 'Refusing to terminate the cleanup process.' }",
247
372
  " $retainedRoot = $Root.Retain()",
248
- " $rootEntry = [PSCustomObject]@{ Process = $retainedRoot; Depth = 0 }",
373
+ " $rootEntry = [PSCustomObject]@{",
374
+ " Process = $retainedRoot",
375
+ " Id = [int]$retainedRoot.Id",
376
+ " CreationTimeUtcTicks = [long]$retainedRoot.CreationTimeUtcTicks",
377
+ " Depth = 0",
378
+ " ParentId = [int]$retainedRoot.ParentId",
379
+ " SnapshotGeneration = -1",
380
+ " SnapshotIndex = -1",
381
+ " OpenOutcome = 'retained-root'",
382
+ " AllowedChildren = $null",
383
+ " }",
249
384
  " $held.Add($rootEntry)",
250
385
  " $known[[int]$retainedRoot.Id] = $rootEntry",
251
- " $retainedRoot.Suspend()",
386
+ " $remaining = [Math]::Max(1, $TimeoutMilliseconds - [int]$watch.ElapsedMilliseconds)",
387
+ " $null = $retainedRoot.Suspend($remaining)",
252
388
  " for ($round = 0; ; $round++) {",
253
- " if ($round -ge 64 -or $held.Count -gt 4096 -or $watch.ElapsedMilliseconds -ge $TimeoutMilliseconds) {",
389
+ " if ($round -ge 64 -or $known.Count -gt 4096 -or $watch.ElapsedMilliseconds -ge $TimeoutMilliseconds) {",
254
390
  " throw 'Owned process-tree discovery exceeded its bounded budget; cleanup is incomplete.'",
255
391
  " }",
392
+ " $hints = @(& $Snapshot)",
256
393
  " $found = $false",
257
- " foreach ($hint in (& $Snapshot)) {",
258
- " if ($known.ContainsKey([int]$hint.Id) -or !$known.ContainsKey([int]$hint.ParentId)) { continue }",
259
- " if ($watch.ElapsedMilliseconds -ge $TimeoutMilliseconds) { throw 'Owned process-tree discovery timed out; cleanup is incomplete.' }",
260
- " $parent = $known[[int]$hint.ParentId]",
261
- " # Exclude historical PID reuse before opening potentially inaccessible processes.",
262
- " if ($hint.CreationTimeUtcTicks -gt 0 -and $hint.CreationTimeUtcTicks -lt $parent.Process.CreationTimeUtcTicks) { continue }",
263
- " $candidate = & $OpenProcess ([int]$hint.Id)",
264
- " if ($null -eq $candidate) { throw \"Cannot pin candidate process $($hint.Id); cleanup is incomplete.\" }",
265
- " try {",
266
- " if ($candidate.Id -ne $hint.Id) { throw 'Pinned candidate identity does not match the snapshot.' }",
267
- " # A snapshot is only a hint; parent and birth must come from the pinned handle.",
268
- " if ($candidate.ParentId -ne $parent.Process.Id) { continue }",
269
- " if ($candidate.CreationTimeUtcTicks -lt $parent.Process.CreationTimeUtcTicks) { continue }",
270
- " if ($candidate.CreationTimeUtcTicks -eq $parent.Process.CreationTimeUtcTicks) {",
271
- " throw \"Ambiguous parent/child birth ordering for $($candidate.Id); cleanup is incomplete.\"",
394
+ " $madeProgress = $true",
395
+ " while ($madeProgress) {",
396
+ " $madeProgress = $false",
397
+ " $hintIndex = 0",
398
+ " foreach ($hint in $hints) {",
399
+ " $currentHintIndex = $hintIndex",
400
+ " $hintIndex++",
401
+ " $hintId = [int]$hint.Id",
402
+ " $hintParentId = [int]$hint.ParentId",
403
+ " $hintBirth = [long]$hint.CreationTimeUtcTicks",
404
+ " if ($known.ContainsKey($hintId)) {",
405
+ " $existing = $known[$hintId]",
406
+ " if ($null -eq $existing.Process -and $hintBirth -gt 0 -and $hintBirth -ne $existing.CreationTimeUtcTicks) {",
407
+ " throw \"Vanished process $hintId was reused during discovery; cleanup is incomplete.\"",
408
+ " }",
409
+ " continue",
410
+ " }",
411
+ " if (!$known.ContainsKey($hintParentId)) { continue }",
412
+ " if ($watch.ElapsedMilliseconds -ge $TimeoutMilliseconds) { throw 'Owned process-tree discovery timed out; cleanup is incomplete.' }",
413
+ " $parent = $known[$hintParentId]",
414
+ " if ($null -eq $parent.Process) {",
415
+ " $identity = \"$hintId`:$hintBirth\"",
416
+ " if ($parent.SnapshotGeneration -ne $round -or !$parent.AllowedChildren.ContainsKey($identity)) {",
417
+ " $lineageMessage = \"Cannot prove ownership of process $hintId through vanished parent $hintParentId; cleanup is incomplete.\"",
418
+ " try {",
419
+ " $lineageDiagnostics = New-OwnedWindowsProcessLineageDiagnostics -Known $known -Hints $hints -Parent $parent -Round $round -HintIndex $currentHintIndex -HintId $hintId -HintParentId $hintParentId -HintBirth $hintBirth -Identity $identity",
420
+ " [Console]::Error.WriteLine(\"OWNED_PROCESS_LINEAGE_DIAGNOSTICS $lineageDiagnostics\")",
421
+ " $lineageMessage += \" Ownership diagnostics: $lineageDiagnostics\"",
422
+ " } catch {",
423
+ " $lineageMessage += \" Ownership diagnostic capture failed: $($_.Exception.Message)\"",
424
+ " }",
425
+ " throw $lineageMessage",
426
+ " }",
272
427
  " }",
273
- " $parentExit = $parent.Process.ExitTimeUtcTicks",
274
- " if ($parentExit -ne 0 -and $candidate.CreationTimeUtcTicks -gt $parentExit) { continue }",
275
- " if ($candidate.Id -eq $PID) { throw 'Refusing to include the cleanup process in the owned tree.' }",
276
- " # Freeze each accepted parent before its next child snapshot, not an entire discovered tree.",
277
- " $entry = [PSCustomObject]@{ Process = $candidate; Depth = $parent.Depth + 1 }",
278
- " $held.Add($entry)",
279
- " $known[[int]$candidate.Id] = $entry",
428
+ " # Exclude historical PID reuse before opening potentially inaccessible processes.",
429
+ " if ($hintBirth -gt 0 -and $hintBirth -lt $parent.CreationTimeUtcTicks) { continue }",
280
430
  " $candidate = $null",
281
- " $entry.Process.Suspend()",
282
- " $found = $true",
283
- " } finally {",
284
- " if ($null -ne $candidate) { $candidate.Dispose() }",
431
+ " try {",
432
+ " try { $candidate = & $OpenProcess $hintId }",
433
+ " catch {",
434
+ " $openFailure = $_.Exception",
435
+ " while ($openFailure.InnerException) { $openFailure = $openFailure.InnerException }",
436
+ " $typedAbsent = $openFailure -is [PylanceOwnedWindowsProcess+CandidateOpenException] -and",
437
+ " $openFailure.NativeErrorCode -eq 87 -and $openFailure.ProcessId -eq $hintId",
438
+ " $taggedAbsent = $openFailure -is [System.ComponentModel.Win32Exception] -and",
439
+ " $openFailure.NativeErrorCode -eq 87 -and",
440
+ " $openFailure.Data['PylanceAbsentProcessId'] -eq $hintId",
441
+ " if (!$typedAbsent -and !$taggedAbsent) {",
442
+ " throw",
443
+ " }",
444
+ " if ($hintId -le 0 -or $hintBirth -le 0) {",
445
+ " throw \"Cannot prove the identity of vanished process $hintId; cleanup is incomplete.\"",
446
+ " }",
447
+ " if ($hintBirth -lt $parent.CreationTimeUtcTicks) { continue }",
448
+ " if ($hintBirth -eq $parent.CreationTimeUtcTicks) {",
449
+ " throw \"Ambiguous parent/child birth ordering for $hintId; cleanup is incomplete.\"",
450
+ " }",
451
+ " if ($null -ne $parent.Process) {",
452
+ " $parentExit = $parent.Process.ExitTimeUtcTicks",
453
+ " if ($parentExit -ne 0 -and $hintBirth -gt $parentExit) { continue }",
454
+ " }",
455
+ " if ($hintId -eq $PID) { throw 'Refusing to include the cleanup process in the owned tree.' }",
456
+ " $allowedChildren = @{}",
457
+ " foreach ($childHint in $hints) {",
458
+ " $childId = [int]$childHint.Id",
459
+ " $childBirth = [long]$childHint.CreationTimeUtcTicks",
460
+ " if ([int]$childHint.ParentId -eq $hintId -and $childBirth -gt $hintBirth) {",
461
+ " $allowedChildren[\"$childId`:$childBirth\"] = $true",
462
+ " }",
463
+ " }",
464
+ " $entry = [PSCustomObject]@{",
465
+ " Process = $null",
466
+ " Id = $hintId",
467
+ " CreationTimeUtcTicks = $hintBirth",
468
+ " Depth = $parent.Depth + 1",
469
+ " ParentId = $hintParentId",
470
+ " SnapshotGeneration = $round",
471
+ " SnapshotIndex = $currentHintIndex",
472
+ " OpenOutcome = 'candidate-open-error-87'",
473
+ " AllowedChildren = $allowedChildren",
474
+ " }",
475
+ " $known[$hintId] = $entry",
476
+ " $found = $true",
477
+ " $madeProgress = $true",
478
+ " continue",
479
+ " }",
480
+ " if ($null -eq $candidate) { throw \"Cannot pin candidate process $hintId; cleanup is incomplete.\" }",
481
+ " if ($candidate.Id -ne $hintId) { throw 'Pinned candidate identity does not match the snapshot.' }",
482
+ " # A pinned identity must match the exact native snapshot generation.",
483
+ " if ($hintBirth -gt 0 -and $candidate.CreationTimeUtcTicks -ne $hintBirth) {",
484
+ " throw \"Pinned candidate birth does not match the snapshot for $hintId; cleanup is incomplete.\"",
485
+ " }",
486
+ " if ($candidate.ParentId -ne $parent.Id) { continue }",
487
+ " if ($candidate.CreationTimeUtcTicks -lt $parent.CreationTimeUtcTicks) { continue }",
488
+ " if ($candidate.CreationTimeUtcTicks -eq $parent.CreationTimeUtcTicks) {",
489
+ " throw \"Ambiguous parent/child birth ordering for $($candidate.Id); cleanup is incomplete.\"",
490
+ " }",
491
+ " if ($null -ne $parent.Process) {",
492
+ " $parentExit = $parent.Process.ExitTimeUtcTicks",
493
+ " if ($parentExit -ne 0 -and $candidate.CreationTimeUtcTicks -gt $parentExit) { continue }",
494
+ " }",
495
+ " if ($candidate.Id -eq $PID) { throw 'Refusing to include the cleanup process in the owned tree.' }",
496
+ " # Freeze each accepted parent before its next child snapshot, not an entire discovered tree.",
497
+ " $entry = [PSCustomObject]@{",
498
+ " Process = $candidate",
499
+ " Id = [int]$candidate.Id",
500
+ " CreationTimeUtcTicks = [long]$candidate.CreationTimeUtcTicks",
501
+ " Depth = $parent.Depth + 1",
502
+ " ParentId = [int]$candidate.ParentId",
503
+ " SnapshotGeneration = -1",
504
+ " SnapshotIndex = $currentHintIndex",
505
+ " OpenOutcome = 'pinned'",
506
+ " AllowedChildren = $null",
507
+ " }",
508
+ " $held.Add($entry)",
509
+ " $known[$hintId] = $entry",
510
+ " $candidate = $null",
511
+ " $remaining = [Math]::Max(1, $TimeoutMilliseconds - [int]$watch.ElapsedMilliseconds)",
512
+ " $null = $entry.Process.Suspend($remaining)",
513
+ " $found = $true",
514
+ " $madeProgress = $true",
515
+ " } finally {",
516
+ " if ($null -ne $candidate) { $candidate.Dispose() }",
517
+ " }",
518
+ " }",
519
+ " }",
520
+ " # Missing candidates require another snapshot, not an assumed empty descendant branch.",
521
+ " foreach ($hint in $hints) {",
522
+ " if ($missing.ContainsKey([int]$hint.ParentId)) {",
523
+ " throw \"Cannot establish descendant ownership after candidate $($hint.ParentId) disappeared; cleanup is incomplete.\"",
285
524
  " }",
286
525
  " }",
287
526
  " if (!$found) { break }",
@@ -43,6 +43,7 @@ export declare abstract class LanguageServerBase implements LanguageServerInterf
43
43
  private _workspaceDiagnosticsReporter;
44
44
  private _workspaceDiagnosticsProgressReporter;
45
45
  private _workspaceDiagnosticsResolve;
46
+ private _workspaceDiagnosticsCancellationDisposable;
46
47
  protected isDisposed: boolean;
47
48
  protected client: ClientCapabilities;
48
49
  protected defaultClientConfig: any;
@@ -134,9 +135,11 @@ export declare abstract class LanguageServerBase implements LanguageServerInterf
134
135
  reporter: WorkDoneProgressReporter;
135
136
  source: AbstractCancellationTokenSource;
136
137
  }>;
138
+ protected getWorkspaceDiagnosticVersion(version: number | undefined): number | null;
137
139
  protected sendDiagnostics(params: PublishDiagnosticsParams[]): void;
138
140
  protected convertLspUriStringToUri(uri: string): Uri;
139
141
  protected addDynamicFeature(feature: DynamicFeature): void;
142
+ private _completeWorkspaceDiagnostics;
140
143
  private _getCompatibleMarkupKind;
141
144
  private _convertDiagnostics;
142
145
  }
@@ -233,9 +233,8 @@ class LanguageServerBase {
233
233
  this.console.level = serverSettings.logLevel ?? console_1.LogLevel.Info;
234
234
  this.dynamicFeatures.update(serverSettings);
235
235
  // If the workspace mode has changed, we may need to resolve the workspace diagnostics promise.
236
- if (serverSettings.openFilesOnly && this._workspaceDiagnosticsResolve) {
237
- this._workspaceDiagnosticsResolve({ items: [] });
238
- this._workspaceDiagnosticsResolve = undefined;
236
+ if (serverSettings.openFilesOnly) {
237
+ this._completeWorkspaceDiagnostics();
239
238
  }
240
239
  // Then use the updated settings to restart the service.
241
240
  this.updateOptionsAndRestartService(workspace, serverSettings);
@@ -774,9 +773,11 @@ class LanguageServerBase {
774
773
  return result;
775
774
  }
776
775
  async onWorkspaceDiagnostics(params, token, workDoneProgress, resultReporter) {
776
+ if (token.isCancellationRequested) {
777
+ return { items: [] };
778
+ }
777
779
  // Resolve any pending workspace diagnostics. We only allow one at a time.
778
- this._workspaceDiagnosticsResolve?.({ items: [] });
779
- this._workspaceDiagnosticsResolve = undefined;
780
+ this._completeWorkspaceDiagnostics();
780
781
  // Save the progress reporters and force a refresh of analysis.
781
782
  this._workspaceDiagnosticsProgressReporter = !(0, lspUtils_1.isNullProgressReporter)(workDoneProgress)
782
783
  ? wrapProgressReporter(workDoneProgress)
@@ -785,12 +786,21 @@ class LanguageServerBase {
785
786
  this.workspaceFactory.getNonDefaultWorkspaces().forEach((workspace) => {
786
787
  workspace.service.invalidateAndScheduleReanalysis(backgroundAnalysisProgram_1.InvalidatedReason.Reanalyzed);
787
788
  });
788
- return new Promise((resolve, reject) => {
789
- // We never resolve as this should be a continually occurring process. Scheduling analysis
790
- // should cause a new workspace diagnostic to be generated.
791
- // Save the resolve callback to be used during shutdown so that tests don't crash
792
- // on the unresolved promise for the workspace diagnostics.
789
+ return new Promise((resolve) => {
790
+ // Keep the request active until cancellation, replacement, configuration change, or shutdown.
793
791
  this._workspaceDiagnosticsResolve = resolve;
792
+ const cancellationDisposable = token.onCancellationRequested(() => {
793
+ this._completeWorkspaceDiagnostics(resolve);
794
+ });
795
+ if (this._workspaceDiagnosticsResolve === resolve) {
796
+ this._workspaceDiagnosticsCancellationDisposable = cancellationDisposable;
797
+ if (token.isCancellationRequested) {
798
+ this._completeWorkspaceDiagnostics(resolve);
799
+ }
800
+ }
801
+ else {
802
+ cancellationDisposable.dispose();
803
+ }
794
804
  });
795
805
  }
796
806
  onDidChangeWatchedFiles(params) {
@@ -849,7 +859,7 @@ class LanguageServerBase {
849
859
  }
850
860
  onShutdown(token) {
851
861
  // Shutdown remaining workspaces.
852
- this._workspaceDiagnosticsResolve?.({ items: [] });
862
+ this._completeWorkspaceDiagnostics();
853
863
  this.workspaceFactory.clear();
854
864
  // Stop tracking all open files.
855
865
  this.openFileMap.clear();
@@ -990,6 +1000,9 @@ class LanguageServerBase {
990
1000
  source: (0, cancellationUtils_1.CancelAfter)(this.serviceProvider.cancellationProvider(), token, serverInitiatedReporter.token),
991
1001
  };
992
1002
  }
1003
+ getWorkspaceDiagnosticVersion(version) {
1004
+ return version || null;
1005
+ }
993
1006
  sendDiagnostics(params) {
994
1007
  for (const param of params) {
995
1008
  if (param.diagnostics.length === 0) {
@@ -1002,7 +1015,14 @@ class LanguageServerBase {
1002
1015
  if (this._workspaceDiagnosticsReporter) {
1003
1016
  // Skip storing previous result ids, just send new results every time.
1004
1017
  this._workspaceDiagnosticsReporter.report({
1005
- items: [{ ...param, kind: 'full', version: param.version || null, items: param.diagnostics }],
1018
+ items: [
1019
+ {
1020
+ ...param,
1021
+ kind: 'full',
1022
+ version: this.getWorkspaceDiagnosticVersion(param.version),
1023
+ items: param.diagnostics,
1024
+ },
1025
+ ],
1006
1026
  });
1007
1027
  }
1008
1028
  else {
@@ -1017,6 +1037,18 @@ class LanguageServerBase {
1017
1037
  addDynamicFeature(feature) {
1018
1038
  this.dynamicFeatures.add(feature);
1019
1039
  }
1040
+ _completeWorkspaceDiagnostics(expectedResolve = this._workspaceDiagnosticsResolve) {
1041
+ if (!expectedResolve || this._workspaceDiagnosticsResolve !== expectedResolve) {
1042
+ return;
1043
+ }
1044
+ this._workspaceDiagnosticsResolve = undefined;
1045
+ this._workspaceDiagnosticsCancellationDisposable?.dispose();
1046
+ this._workspaceDiagnosticsCancellationDisposable = undefined;
1047
+ this._workspaceDiagnosticsReporter = undefined;
1048
+ this._workspaceDiagnosticsProgressReporter?.end();
1049
+ this._workspaceDiagnosticsProgressReporter = undefined;
1050
+ expectedResolve({ items: [] });
1051
+ }
1020
1052
  _getCompatibleMarkupKind(clientSupportedFormats) {
1021
1053
  const serverSupportedFormats = [vscode_languageserver_1.MarkupKind.PlainText, vscode_languageserver_1.MarkupKind.Markdown];
1022
1054
  for (const format of clientSupportedFormats ?? []) {