@esportsplus/typescript 0.32.0 → 0.33.1

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 (68) hide show
  1. package/README.md +34 -49
  2. package/build/cli/tsc.d.ts +3 -2
  3. package/build/cli/tsc.js +15 -97
  4. package/build/{probe → guard}/adapter.d.ts +1 -2
  5. package/build/{probe → guard}/adapter.js +1 -4
  6. package/build/{probe → guard}/async/channel.d.ts +1 -1
  7. package/build/{probe → guard}/async/channel.js +11 -191
  8. package/build/{probe → guard}/async/value.d.ts +1 -2
  9. package/build/{probe → guard}/async/value.js +1 -4
  10. package/build/guard/channels.d.ts +4 -0
  11. package/build/guard/channels.js +13 -0
  12. package/build/{probe → guard}/exceptions/channel.d.ts +3 -2
  13. package/build/{probe → guard}/exceptions/channel.js +167 -107
  14. package/build/guard/exceptions/derive.d.ts +49 -0
  15. package/build/guard/exceptions/derive.js +478 -0
  16. package/build/guard/exceptions/resolve-js.d.ts +2 -0
  17. package/build/guard/exceptions/resolve-js.js +26 -0
  18. package/build/{probe → guard}/exceptions/value.d.ts +3 -2
  19. package/build/{probe → guard}/exceptions/value.js +12 -4
  20. package/build/{probe → guard}/kernel/analyze.d.ts +2 -1
  21. package/build/guard/kernel/analyze.js +84 -0
  22. package/build/guard/kernel/ast.d.ts +13 -0
  23. package/build/{probe → guard}/kernel/ast.js +22 -15
  24. package/build/guard/kernel/config.js +93 -0
  25. package/build/guard/kernel/fixpoint.d.ts +6 -0
  26. package/build/{probe → guard}/kernel/fixpoint.js +8 -48
  27. package/build/guard/kernel/graph.d.ts +4 -0
  28. package/build/guard/kernel/graph.js +268 -0
  29. package/build/{probe → guard}/kernel/ids.d.ts +2 -3
  30. package/build/{probe → guard}/kernel/ids.js +1 -6
  31. package/build/{probe → guard}/kernel/types.d.ts +3 -32
  32. package/build/{probe → guard}/overlay/base/async.jsonc +1 -1
  33. package/build/{probe → guard}/overlay/base/exceptions.jsonc +16 -3
  34. package/build/{probe → guard}/overlay/base/resources.jsonc +7 -8
  35. package/build/{probe → guard}/overlay/load.d.ts +3 -7
  36. package/build/{probe → guard}/overlay/load.js +37 -120
  37. package/build/{probe → guard}/resources/channel.d.ts +1 -1
  38. package/build/{probe → guard}/resources/channel.js +81 -159
  39. package/build/{probe → guard}/resources/value.d.ts +1 -2
  40. package/build/{probe → guard}/resources/value.js +1 -14
  41. package/build/lsp/diagnostics.d.ts +2 -2
  42. package/build/lsp/diagnostics.js +2 -2
  43. package/build/lsp/server.js +41 -3
  44. package/build/lsp/workspace.d.ts +6 -3
  45. package/build/lsp/workspace.js +19 -5
  46. package/build/tsconfig.d.ts +2 -1
  47. package/build/tsconfig.js +44 -102
  48. package/package.json +2 -2
  49. package/tsconfig.base.json +12 -19
  50. package/tsconfig.package.json +6 -1
  51. package/build/probe/channels.d.ts +0 -4
  52. package/build/probe/channels.js +0 -16
  53. package/build/probe/kernel/analyze.js +0 -68
  54. package/build/probe/kernel/ast.d.ts +0 -11
  55. package/build/probe/kernel/config.js +0 -209
  56. package/build/probe/kernel/fixpoint.d.ts +0 -6
  57. package/build/probe/kernel/graph.d.ts +0 -4
  58. package/build/probe/kernel/graph.js +0 -507
  59. package/build/probe/overlay/presets/express.jsonc +0 -25
  60. package/build/probe/overlay/presets/node.jsonc +0 -17
  61. /package/build/{probe → guard}/exceptions/jsdoc.d.ts +0 -0
  62. /package/build/{probe → guard}/exceptions/jsdoc.js +0 -0
  63. /package/build/{probe → guard}/kernel/config.d.ts +0 -0
  64. /package/build/{probe → guard}/kernel/format.d.ts +0 -0
  65. /package/build/{probe → guard}/kernel/format.js +0 -0
  66. /package/build/{probe → guard}/kernel/program.d.ts +0 -0
  67. /package/build/{probe → guard}/kernel/program.js +0 -0
  68. /package/build/{probe → guard}/kernel/types.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import * as ts from '../adapter.js';
2
2
  import { overlayThrows } from '../exceptions/channel.js';
3
- import { bodyOf, calleeSelectors, paramSymbols, unwrap } from '../kernel/ast.js';
3
+ import { bodyOf, paramSymbols, unwrap } from '../kernel/ast.js';
4
4
  import { isEmpty } from '../exceptions/value.js';
5
5
  import { isFunctionLike, locationOf } from '../kernel/ids.js';
6
6
  import { bottom, equals, fromParams, widen, } from './value.js';
@@ -32,13 +32,13 @@ function refsSym(env, expr, sym) {
32
32
  }
33
33
  return env.checker.getSymbolAtLocation(e) === sym;
34
34
  }
35
- function containsMatch(node, pred) {
35
+ function containsMatch(node, pred, descendIntoFunctions = false) {
36
36
  let found = false;
37
37
  const rec = (n) => {
38
38
  if (found) {
39
39
  return;
40
40
  }
41
- if (n !== node && isFunctionLike(n)) {
41
+ if (n !== node && !descendIntoFunctions && isFunctionLike(n)) {
42
42
  return;
43
43
  }
44
44
  if (pred(n)) {
@@ -67,7 +67,7 @@ function callCanThrow(env, call) {
67
67
  if (res.targets.length > 0) {
68
68
  return res.targets.some((t) => env.peerThrows(t.id));
69
69
  }
70
- return res.unresolved && env.dispatch === 'pessimist';
70
+ return res.unresolved;
71
71
  }
72
72
  function enclosingStatement(node) {
73
73
  let n = node;
@@ -79,24 +79,13 @@ function enclosingStatement(node) {
79
79
  function blockOf(stmt) {
80
80
  return stmt.parent && ts.isBlock(stmt.parent) ? stmt.parent : undefined;
81
81
  }
82
- function isDisposableType(env, type) {
82
+ function hasDispose(env, type, includeAsync) {
83
83
  if (!type) {
84
84
  return false;
85
85
  }
86
86
  for (const prop of env.checker.getPropertiesOfType(type)) {
87
87
  if (prop.name.startsWith('__@dispose') ||
88
- prop.name.startsWith('__@asyncDispose')) {
89
- return true;
90
- }
91
- }
92
- return false;
93
- }
94
- function hasSyncDispose(env, type) {
95
- if (!type) {
96
- return false;
97
- }
98
- for (const prop of env.checker.getPropertiesOfType(type)) {
99
- if (prop.name.startsWith('__@dispose')) {
88
+ (includeAsync && prop.name.startsWith('__@asyncDispose'))) {
100
89
  return true;
101
90
  }
102
91
  }
@@ -134,7 +123,19 @@ function acquireAt(env, call) {
134
123
  };
135
124
  }
136
125
  }
137
- if (isDisposableType(env, env.checker.getTypeAtLocation(call))) {
126
+ const parent = bindingParentOf(call);
127
+ const target = parent.parent;
128
+ const isInitializer = bindingDeclOf(call) !== undefined ||
129
+ (ts.isPropertyDeclaration(target) && target.initializer === parent) ||
130
+ (ts.isBinaryExpression(target) &&
131
+ target.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
132
+ target.right === parent &&
133
+ ts.isPropertyAccessExpression(target.left) &&
134
+ target.left.expression.kind === ts.SyntaxKind.ThisKeyword);
135
+ if (!isInitializer) {
136
+ return undefined;
137
+ }
138
+ if (hasDispose(env, env.checker.getTypeAtLocation(call), true)) {
138
139
  return {
139
140
  kind: 'handle',
140
141
  releasedBy: undefined,
@@ -144,19 +145,24 @@ function acquireAt(env, call) {
144
145
  }
145
146
  return undefined;
146
147
  }
148
+ function bindingDeclOf(call) {
149
+ const parent = bindingParentOf(call).parent;
150
+ return ts.isVariableDeclaration(parent) ? parent : undefined;
151
+ }
152
+ function bindingParentOf(call) {
153
+ let parent = call;
154
+ while (parent.parent &&
155
+ (ts.isParenthesizedExpression(parent.parent) ||
156
+ ts.isNonNullExpression(parent.parent) ||
157
+ ts.isAsExpression(parent.parent) ||
158
+ ts.isAwaitExpression(parent.parent))) {
159
+ parent = parent.parent;
160
+ }
161
+ return parent;
162
+ }
147
163
  function handleBinding(env, call) {
148
- let p = call;
149
- while (p.parent &&
150
- (ts.isParenthesizedExpression(p.parent) ||
151
- ts.isNonNullExpression(p.parent) ||
152
- ts.isAsExpression(p.parent) ||
153
- ts.isAwaitExpression(p.parent))) {
154
- p = p.parent;
155
- }
156
- const decl = p.parent;
157
- if (!decl ||
158
- !ts.isVariableDeclaration(decl) ||
159
- !ts.isIdentifier(decl.name)) {
164
+ const decl = bindingDeclOf(call);
165
+ if (!decl || !ts.isIdentifier(decl.name)) {
160
166
  return undefined;
161
167
  }
162
168
  const list = decl.parent;
@@ -168,7 +174,7 @@ function handleBinding(env, call) {
168
174
  const sym = env.checker.getSymbolAtLocation(decl.name);
169
175
  return sym ? { using: false, sym } : undefined;
170
176
  }
171
- function isDischargeOf(env, expr, sym, acq) {
177
+ function isDischargeCall(expr, acq, refs) {
172
178
  if (!ts.isCallExpression(expr)) {
173
179
  return false;
174
180
  }
@@ -182,10 +188,10 @@ function isDischargeOf(env, expr, sym, acq) {
182
188
  return false;
183
189
  }
184
190
  const arg = expr.arguments?.[0];
185
- return arg ? refsSym(env, arg, sym) : false;
191
+ return arg ? refs(arg) : false;
186
192
  }
187
193
  if (ts.isPropertyAccessExpression(callee)) {
188
- if (!refsSym(env, callee.expression, sym)) {
194
+ if (!refs(callee.expression)) {
189
195
  return false;
190
196
  }
191
197
  const method = callee.name.text;
@@ -197,7 +203,7 @@ function isDischargeOf(env, expr, sym, acq) {
197
203
  acq.releasedBy.slice(1) === method);
198
204
  }
199
205
  if (ts.isElementAccessExpression(callee)) {
200
- if (!refsSym(env, callee.expression, sym)) {
206
+ if (!refs(callee.expression)) {
201
207
  return false;
202
208
  }
203
209
  const argx = callee.argumentExpression;
@@ -207,6 +213,9 @@ function isDischargeOf(env, expr, sym, acq) {
207
213
  }
208
214
  return false;
209
215
  }
216
+ function isDischargeOf(env, expr, sym, acq) {
217
+ return isDischargeCall(expr, acq, (e) => refsSym(env, e, sym));
218
+ }
210
219
  function callOwnsArg(env, call, sym) {
211
220
  const args = call.arguments ? Array.from(call.arguments) : [];
212
221
  const indices = [];
@@ -218,13 +227,6 @@ function callOwnsArg(env, call, sym) {
218
227
  if (indices.length === 0) {
219
228
  return false;
220
229
  }
221
- const selectors = calleeSelectors(call.expression);
222
- for (const own of env.ownership) {
223
- if (selectors.has(own.callee) &&
224
- own.params.some((p) => indices.includes(p))) {
225
- return true;
226
- }
227
- }
228
230
  const res = env.resolveCall(call);
229
231
  const entry = (res.overlay?.entry ?? undefined);
230
232
  if (entry && Array.isArray(entry.ownsParams)) {
@@ -273,8 +275,9 @@ function statementDischargesOrTransfers(env, s, sym, acq) {
273
275
  }
274
276
  return false;
275
277
  }
276
- function finallyDischarges(env, sym, acq, declStmt, body) {
278
+ function finallyDischarges(env, sym, acq, declStmt, body, sf) {
277
279
  let found = false;
280
+ const declStart = declStmt.getStart(sf);
278
281
  const rec = (n) => {
279
282
  if (found) {
280
283
  return;
@@ -284,7 +287,7 @@ function finallyDischarges(env, sym, acq, declStmt, body) {
284
287
  }
285
288
  if (ts.isTryStatement(n) &&
286
289
  n.finallyBlock &&
287
- declStmt.getStart() < n.finallyBlock.getStart()) {
290
+ declStart < n.finallyBlock.getStart(sf)) {
288
291
  const discharged = containsMatch(n.finallyBlock, (m) => (ts.isCallExpression(m) || ts.isNewExpression(m)) &&
289
292
  isDischargeOf(env, m, sym, acq));
290
293
  if (discharged) {
@@ -356,7 +359,7 @@ function opaqueUseOf(env, sym, body) {
356
359
  return hit;
357
360
  }
358
361
  function handleOutcome(env, acq, sym, declStmt, block, body) {
359
- if (finallyDischarges(env, sym, acq, declStmt, body)) {
362
+ if (finallyDischarges(env, sym, acq, declStmt, body, env.fn.sourceFile)) {
360
363
  return 'safe';
361
364
  }
362
365
  if (firstGuaranteed(env, sym, acq, declStmt, block)) {
@@ -367,7 +370,7 @@ function handleOutcome(env, acq, sym, declStmt, block, body) {
367
370
  }
368
371
  return 'leak';
369
372
  }
370
- function isReleaseCallForPair(node, acq, acquireCall) {
373
+ function isReleaseCallForPair(node, acq, acquireCall, acquireExpressionText) {
371
374
  if (!ts.isCallExpression(node) || acq.releasedBy === undefined) {
372
375
  return false;
373
376
  }
@@ -380,7 +383,8 @@ function isReleaseCallForPair(node, acq, acquireCall) {
380
383
  if (callee.name.text !== acq.releasedBy) {
381
384
  return false;
382
385
  }
383
- if (callee.expression.getText() !== acqCallee.expression.getText()) {
386
+ if (acquireExpressionText === undefined ||
387
+ callee.expression.getText() !== acquireExpressionText) {
384
388
  return false;
385
389
  }
386
390
  for (const i of acq.pairKey ?? []) {
@@ -397,7 +401,11 @@ function hasMatchingRelease(env, acquireCall, acq) {
397
401
  if (!body) {
398
402
  return false;
399
403
  }
400
- return containsMatch(body, (n) => isReleaseCallForPair(n, acq, acquireCall));
404
+ const acqCallee = acquireCall.expression;
405
+ const acquireExpressionText = ts.isPropertyAccessExpression(acqCallee)
406
+ ? acqCallee.expression.getText()
407
+ : undefined;
408
+ return containsMatch(body, (n) => isReleaseCallForPair(n, acq, acquireCall, acquireExpressionText), true);
401
409
  }
402
410
  function classDischargesField(cls, field, acq) {
403
411
  const members = cls.members ?? [];
@@ -407,39 +415,7 @@ function classDischargesField(cls, field, acq) {
407
415
  e.expression.kind === ts.SyntaxKind.ThisKeyword &&
408
416
  e.name.text === field);
409
417
  };
410
- const dischargesHere = (n) => {
411
- if (!ts.isCallExpression(n)) {
412
- return false;
413
- }
414
- const callee = n.expression;
415
- if (ts.isIdentifier(callee)) {
416
- const freeName = acq?.releasedBy !== undefined && !acq.releasedBy.startsWith('#')
417
- ? acq.releasedBy
418
- : undefined;
419
- if (!FREE_RELEASERS.has(callee.text) && callee.text !== freeName) {
420
- return false;
421
- }
422
- const arg = n.arguments?.[0];
423
- return arg ? refsThisField(arg) : false;
424
- }
425
- if (ts.isPropertyAccessExpression(callee) &&
426
- refsThisField(callee.expression)) {
427
- const method = callee.name.text;
428
- return (DISPOSE_METHODS.has(method) ||
429
- (acq?.releasedBy !== undefined &&
430
- acq.releasedBy.startsWith('#') &&
431
- acq.releasedBy.slice(1) === method));
432
- }
433
- if (ts.isElementAccessExpression(callee) &&
434
- refsThisField(callee.expression)) {
435
- const argx = callee.argumentExpression;
436
- return (argx !== undefined &&
437
- ts.isPropertyAccessExpression(argx) &&
438
- (argx.name.text === 'dispose' ||
439
- argx.name.text === 'asyncDispose'));
440
- }
441
- return false;
442
- };
418
+ const dischargesHere = (n) => ts.isCallExpression(n) && isDischargeCall(n, acq, refsThisField);
443
419
  for (const member of members) {
444
420
  const body = member.body;
445
421
  if (body && containsMatch(body, dischargesHere)) {
@@ -494,30 +470,12 @@ function classFieldAcquires(env, cls, ctorBody) {
494
470
  }
495
471
  return out;
496
472
  }
497
- function relatedPath(ctx) {
498
- const related = [];
499
- for (const f of ctx.pathToBoundary(ctx.fn)) {
500
- related.push({
501
- message: `on the path to boundary via ${f.name}`,
502
- location: locationOf(f.node, f.sourceFile),
503
- });
504
- }
505
- return related;
506
- }
507
473
  function usingFix(env, call) {
508
- if (!hasSyncDispose(env, env.checker.getTypeAtLocation(call))) {
474
+ if (!hasDispose(env, env.checker.getTypeAtLocation(call), false)) {
509
475
  return undefined;
510
476
  }
511
- let p = call;
512
- while (p.parent &&
513
- (ts.isParenthesizedExpression(p.parent) ||
514
- ts.isNonNullExpression(p.parent) ||
515
- ts.isAsExpression(p.parent) ||
516
- ts.isAwaitExpression(p.parent))) {
517
- p = p.parent;
518
- }
519
- const decl = p.parent;
520
- if (!decl || !ts.isVariableDeclaration(decl)) {
477
+ const decl = bindingDeclOf(call);
478
+ if (!decl) {
521
479
  return undefined;
522
480
  }
523
481
  const list = decl.parent;
@@ -554,7 +512,7 @@ function releaseText(env, acq, name, call) {
554
512
  ? `${name}.${acq.releasedBy.slice(1)}()`
555
513
  : `${acq.releasedBy}(${name})`;
556
514
  }
557
- if (hasSyncDispose(env, env.checker.getTypeAtLocation(call))) {
515
+ if (hasDispose(env, env.checker.getTypeAtLocation(call), false)) {
558
516
  return `${name}[Symbol.dispose]()`;
559
517
  }
560
518
  return undefined;
@@ -603,47 +561,16 @@ function tryFinallyFix(env, acq, sym, declStmt, block, call) {
603
561
  ],
604
562
  };
605
563
  }
606
- function leakDiagnostic(ctx, node, acq, why, fixes) {
564
+ function leakDiagnostic(node, acq, why, fixes) {
607
565
  return {
608
566
  channel: 'resources',
609
567
  message: `\`${acq.label}\` resource can leak — ${why}`,
610
568
  location: locationOf(node, node.getSourceFile()),
611
- related: relatedPath(ctx),
569
+ related: [],
612
570
  fixes,
613
571
  };
614
572
  }
615
- function parseOwnership(channelConfig) {
616
- if (typeof channelConfig !== 'object' || channelConfig === null) {
617
- return [];
618
- }
619
- const raw = channelConfig['ownership'];
620
- if (raw === undefined) {
621
- return [];
622
- }
623
- if (!Array.isArray(raw)) {
624
- throw new Error('resources: "ownership" must be an array');
625
- }
626
- return raw.map((item, index) => {
627
- if (typeof item !== 'object' || item === null) {
628
- throw new Error(`resources: ownership[${index}] must be an object`);
629
- }
630
- const obj = item;
631
- if (typeof obj['callee'] !== 'string') {
632
- throw new Error(`resources: ownership[${index}].callee must be a string`);
633
- }
634
- const params = obj['params'];
635
- if (!Array.isArray(params) ||
636
- params.some((v) => typeof v !== 'number' || !Number.isInteger(v))) {
637
- throw new Error(`resources: ownership[${index}].params must be an array of integers`);
638
- }
639
- return {
640
- callee: obj['callee'],
641
- params: params,
642
- };
643
- });
644
- }
645
- function makeEnv(checker, dispatch, fn, ownership, summaryOf, resolveCall, resolveCallFor, peerSummaryValue, logDegrade) {
646
- const symbols = paramSymbols(checker, fn.node);
573
+ function makeEnv(checker, fn, summaryOf, resolveCall, resolveCallFor, peerSummaryValue, logDegrade, includeParamSymbols) {
647
574
  const resolveExceptions = (call) => resolveCallFor('exceptions', call);
648
575
  const peerThrows = (fnId) => {
649
576
  const value = peerSummaryValue('exceptions', fnId);
@@ -651,15 +578,13 @@ function makeEnv(checker, dispatch, fn, ownership, summaryOf, resolveCall, resol
651
578
  };
652
579
  return {
653
580
  checker,
654
- dispatch,
655
581
  fn,
656
- ownership,
657
582
  summaryOf,
658
583
  resolveCall,
659
584
  resolveExceptions,
660
585
  peerThrows,
661
586
  logDegrade,
662
- paramSymbols: symbols,
587
+ paramSymbols: includeParamSymbols ? paramSymbols(checker, fn.node) : undefined,
663
588
  };
664
589
  }
665
590
  function ownsParam(env, sym, body) {
@@ -683,17 +608,17 @@ function computeOwnership(env) {
683
608
  if (!body) {
684
609
  return owned;
685
610
  }
686
- for (const [sym, index] of env.paramSymbols) {
611
+ for (const [sym, index] of env.paramSymbols ?? []) {
687
612
  if (ownsParam(env, sym, body)) {
688
613
  owned.add(index);
689
614
  }
690
615
  }
691
616
  return owned;
692
617
  }
693
- function diagnoseAcquire(env, ctx, call, acq, out) {
618
+ function diagnoseAcquire(env, call, acq, out) {
694
619
  if (acq.kind === 'pair') {
695
620
  if (!hasMatchingRelease(env, call, acq)) {
696
- out.push(leakDiagnostic(ctx, call, acq, `no matching \`${acq.releasedBy ?? 'release'}\` on every path`));
621
+ out.push(leakDiagnostic(call, acq, `no matching \`${acq.releasedBy ?? 'release'}\` on every path`));
697
622
  }
698
623
  return;
699
624
  }
@@ -719,23 +644,21 @@ function diagnoseAcquire(env, ctx, call, acq, out) {
719
644
  if (wrapped) {
720
645
  fixes.push(wrapped);
721
646
  }
722
- out.push(leakDiagnostic(ctx, call, acq, 'no release, transfer, or `using` on every path', fixes.length > 0 ? fixes : undefined));
647
+ out.push(leakDiagnostic(call, acq, 'no release, transfer, or `using` on every path', fixes.length > 0 ? fixes : undefined));
723
648
  }
724
649
  else if (outcome === 'degrade') {
725
650
  env.logDegrade(`${acq.label} at ${env.fn.fileName}:${call.getStart()} flows to an opaque sink`);
726
- if (env.dispatch === 'pessimist') {
727
- out.push({
728
- channel: 'resources',
729
- message: `\`${acq.label}\` resource becomes untrackable — it flows to an opaque sink and release cannot be verified`,
730
- location: locationOf(call, call.getSourceFile()),
731
- related: relatedPath(ctx),
732
- });
733
- }
651
+ out.push({
652
+ channel: 'resources',
653
+ message: `\`${acq.label}\` resource becomes untrackable — it flows to an opaque sink and release cannot be verified`,
654
+ location: locationOf(call, call.getSourceFile()),
655
+ related: [],
656
+ });
734
657
  }
735
658
  return;
736
659
  }
737
660
  }
738
- function diagnoseClassFields(env, ctx, out) {
661
+ function diagnoseClassFields(env, out) {
739
662
  if (!ts.isConstructorDeclaration(env.fn.node)) {
740
663
  return;
741
664
  }
@@ -752,12 +675,11 @@ function diagnoseClassFields(env, ctx, out) {
752
675
  channel: 'resources',
753
676
  message: `\`${stored.acq.label}\` stored on \`this.${stored.field}\` can leak — class \`${className(cls)}\` has no release method that discharges it`,
754
677
  location: locationOf(stored.node, stored.node.getSourceFile()),
755
- related: relatedPath(ctx),
678
+ related: [],
756
679
  });
757
680
  }
758
681
  }
759
- const createResourcesChannel = (channelConfig, onDegrade = () => { }) => {
760
- const ownership = parseOwnership(channelConfig);
682
+ const createResourcesChannel = (onDegrade = () => { }) => {
761
683
  return {
762
684
  name: 'resources',
763
685
  dependsOn: ['exceptions'],
@@ -765,16 +687,16 @@ const createResourcesChannel = (channelConfig, onDegrade = () => { }) => {
765
687
  equals,
766
688
  widen,
767
689
  transfer(ctx) {
768
- const env = makeEnv(ctx.checker, ctx.dispatch, ctx.fn, ownership, ctx.summaryOf, ctx.resolveCall, ctx.resolveCallFor, ctx.peerSummaryValue, onDegrade);
690
+ const env = makeEnv(ctx.checker, ctx.fn, ctx.summaryOf, ctx.resolveCall, ctx.resolveCallFor, ctx.peerSummaryValue, onDegrade, true);
769
691
  return {
770
692
  value: fromParams(computeOwnership(env)),
771
693
  fromCallbacks: new Set(),
772
694
  };
773
695
  },
774
696
  diagnose(ctx) {
775
- const env = makeEnv(ctx.checker, ctx.dispatch, ctx.fn, ownership, ctx.summaryOf, ctx.resolveCall, ctx.resolveCallFor, ctx.peerSummaryValue, onDegrade);
697
+ const env = makeEnv(ctx.checker, ctx.fn, ctx.summaryOf, ctx.resolveCall, ctx.resolveCallFor, ctx.peerSummaryValue, onDegrade, false);
776
698
  const out = [];
777
- diagnoseClassFields(env, ctx, out);
699
+ diagnoseClassFields(env, out);
778
700
  const body = bodyOf(ctx.fn.node);
779
701
  if (body) {
780
702
  const visit = (n) => {
@@ -784,7 +706,7 @@ const createResourcesChannel = (channelConfig, onDegrade = () => { }) => {
784
706
  if (ts.isCallExpression(n) || ts.isNewExpression(n)) {
785
707
  const acq = acquireAt(env, n);
786
708
  if (acq) {
787
- diagnoseAcquire(env, ctx, n, acq, out);
709
+ diagnoseAcquire(env, n, acq, out);
788
710
  }
789
711
  }
790
712
  ts.forEachChild(n, visit);
@@ -3,7 +3,6 @@ type ResourcesValue = {
3
3
  };
4
4
  declare function bottom(): ResourcesValue;
5
5
  declare function fromParams(indices: Iterable<number>): ResourcesValue;
6
- declare function join(a: ResourcesValue, b: ResourcesValue): ResourcesValue;
7
6
  declare function equals(a: ResourcesValue, b: ResourcesValue): boolean;
8
7
  declare function widen(_prev: ResourcesValue, next: ResourcesValue, _round: number): ResourcesValue;
9
- export { bottom, equals, fromParams, join, type ResourcesValue, widen };
8
+ export { bottom, equals, fromParams, type ResourcesValue, widen };
@@ -6,19 +6,6 @@ function fromParams(indices) {
6
6
  const set = new Set(indices);
7
7
  return { ownsParams: set.size === 0 ? EMPTY : set };
8
8
  }
9
- function join(a, b) {
10
- if (a.ownsParams.size === 0) {
11
- return b;
12
- }
13
- if (b.ownsParams.size === 0) {
14
- return a;
15
- }
16
- const set = new Set(a.ownsParams);
17
- for (const i of b.ownsParams) {
18
- set.add(i);
19
- }
20
- return { ownsParams: set };
21
- }
22
9
  function equals(a, b) {
23
10
  if (a.ownsParams.size !== b.ownsParams.size) {
24
11
  return false;
@@ -33,4 +20,4 @@ function equals(a, b) {
33
20
  function widen(_prev, next, _round) {
34
21
  return next;
35
22
  }
36
- export { bottom, equals, fromParams, join, widen };
23
+ export { bottom, equals, fromParams, widen };
@@ -1,10 +1,10 @@
1
1
  import { DiagnosticSeverity } from 'vscode-languageserver/node';
2
2
  import type { CodeAction, Diagnostic as LspDiagnostic, Hover } from 'vscode-languageserver/node';
3
3
  import type { TextDocument } from 'vscode-languageserver-textdocument';
4
- import type { Diagnostic as AnalyzeDiagnostic } from '../probe/kernel/types.js';
4
+ import type { Diagnostic as AnalyzeDiagnostic } from '../guard/kernel/types.js';
5
5
  export type DocumentResolver = (fileName: string) => TextDocument | undefined;
6
6
  declare function toLspDiagnostic(diagnostic: AnalyzeDiagnostic, severity: DiagnosticSeverity, resolve: DocumentResolver, uriOf: (fileName: string) => string): LspDiagnostic;
7
- declare function groupByFile(diagnostics: ReadonlyArray<AnalyzeDiagnostic>, severity: DiagnosticSeverity, resolve: DocumentResolver, uriOf: (fileName: string) => string): Map<string, LspDiagnostic[]>;
7
+ declare function groupByFile(diagnostics: ReadonlyArray<AnalyzeDiagnostic>, severityOf: (channel: string) => DiagnosticSeverity, resolve: DocumentResolver, uriOf: (fileName: string) => string): Map<string, LspDiagnostic[]>;
8
8
  declare function hoverAt(diagnostics: ReadonlyArray<AnalyzeDiagnostic>, offset: number, document: TextDocument): Hover | undefined;
9
9
  declare function codeActionsAt(diagnostics: ReadonlyArray<AnalyzeDiagnostic>, startOffset: number, endOffset: number, document: TextDocument, uriOf: (fileName: string) => string): CodeAction[];
10
10
  export { codeActionsAt, DiagnosticSeverity, groupByFile, hoverAt, toLspDiagnostic };
@@ -22,7 +22,7 @@ function toLspDiagnostic(diagnostic, severity, resolve, uriOf) {
22
22
  source: 'analyze',
23
23
  };
24
24
  }
25
- function groupByFile(diagnostics, severity, resolve, uriOf) {
25
+ function groupByFile(diagnostics, severityOf, resolve, uriOf) {
26
26
  let grouped = new Map();
27
27
  for (let i = 0, n = diagnostics.length; i < n; i++) {
28
28
  let diagnostic = diagnostics[i], fileName = diagnostic.location.fileName, list = grouped.get(fileName);
@@ -30,7 +30,7 @@ function groupByFile(diagnostics, severity, resolve, uriOf) {
30
30
  list = [];
31
31
  grouped.set(fileName, list);
32
32
  }
33
- list.push(toLspDiagnostic(diagnostic, severity, resolve, uriOf));
33
+ list.push(toLspDiagnostic(diagnostic, severityOf(diagnostic.channel), resolve, uriOf));
34
34
  }
35
35
  return grouped;
36
36
  }
@@ -9,6 +9,15 @@ function pathKey(fileName) {
9
9
  let resolved = NodePath.resolve(fileName);
10
10
  return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
11
11
  }
12
+ function lspSeverity(severity) {
13
+ if (severity === 'warn') {
14
+ return DiagnosticSeverity.Warning;
15
+ }
16
+ if (severity === 'info') {
17
+ return DiagnosticSeverity.Information;
18
+ }
19
+ return DiagnosticSeverity.Error;
20
+ }
12
21
  function findTsconfig(rootDir) {
13
22
  let candidate = NodePath.join(rootDir, 'tsconfig.json');
14
23
  return NodeFS.existsSync(candidate) ? candidate : undefined;
@@ -19,12 +28,38 @@ function rootFromInitialize(params) {
19
28
  }
20
29
  function createServer(connection) {
21
30
  let analyzed = new Map(), documents = new TextDocuments(TextDocument), pending = new Set(), published = new Set(), timer, workspace;
31
+ function publishConfigError() {
32
+ if (!workspace) {
33
+ return;
34
+ }
35
+ let uri = pathToFileURL(workspace.configPath).toString();
36
+ if (workspace.configError) {
37
+ connection.sendDiagnostics({
38
+ diagnostics: [{
39
+ message: workspace.configError,
40
+ range: { end: { character: 0, line: 0 }, start: { character: 0, line: 0 } },
41
+ severity: DiagnosticSeverity.Error,
42
+ source: 'analyze',
43
+ }],
44
+ uri,
45
+ });
46
+ published.add(uri);
47
+ }
48
+ else if (published.has(uri)) {
49
+ connection.sendDiagnostics({ diagnostics: [], uri });
50
+ published.delete(uri);
51
+ }
52
+ }
22
53
  function runAnalysis() {
23
54
  timer = undefined;
24
- if (!workspace || !workspace.config) {
55
+ if (!workspace) {
25
56
  return;
26
57
  }
27
- let changed = [...pending];
58
+ publishConfigError();
59
+ if (!workspace.config) {
60
+ return;
61
+ }
62
+ let config = workspace.config, changed = [...pending];
28
63
  pending.clear();
29
64
  try {
30
65
  workspace.refresh(changed);
@@ -46,7 +81,10 @@ function createServer(connection) {
46
81
  }
47
82
  list.push(diagnostic);
48
83
  }
49
- let openByPath = new Map(documents.all().map((document) => [pathKey(fileURLToPath(document.uri)), document])), resolve = (fileName) => openByPath.get(pathKey(fileName)), severity = workspace.config.severity === 'warning' ? DiagnosticSeverity.Warning : DiagnosticSeverity.Error, uriOf = (fileName) => openByPath.get(pathKey(fileName))?.uri ?? pathToFileURL(fileName).toString(), grouped = groupByFile(result.diagnostics, severity, resolve, uriOf), next = new Set();
84
+ let openByPath = new Map(documents.all().map((document) => [pathKey(fileURLToPath(document.uri)), document])), resolve = (fileName) => openByPath.get(pathKey(fileName)), severityOf = (channel) => lspSeverity(config.channels[channel]?.severity), uriOf = (fileName) => openByPath.get(pathKey(fileName))?.uri ?? pathToFileURL(fileName).toString(), grouped = groupByFile(result.diagnostics, severityOf, resolve, uriOf), next = new Set();
85
+ if (workspace.configError) {
86
+ next.add(pathToFileURL(workspace.configPath).toString());
87
+ }
50
88
  for (let [fileName, diagnostics] of grouped) {
51
89
  let uri = uriOf(fileName);
52
90
  next.add(uri);
@@ -1,11 +1,14 @@
1
- import type { AnalyzeResult } from '../probe/kernel/analyze.js';
2
- import type { AnalyzeConfig } from '../probe/kernel/types.js';
1
+ import type { AnalyzeResult } from '../guard/kernel/analyze.js';
2
+ import type { AnalyzeConfig } from '../guard/kernel/types.js';
3
3
  declare class AnalyzeWorkspace {
4
4
  private api;
5
- private configPath;
5
+ readonly configPath: string;
6
6
  private snapshot;
7
+ private throwIndex;
7
8
  config: AnalyzeConfig | undefined;
9
+ configError: string | undefined;
8
10
  constructor(tsconfigPath: string);
11
+ private loadConfig;
9
12
  reloadConfig(): void;
10
13
  refresh(changed: ReadonlyArray<string>): void;
11
14
  analyze(): AnalyzeResult | undefined;