-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathassertions.ts
More file actions
130 lines (108 loc) · 3.95 KB
/
assertions.ts
File metadata and controls
130 lines (108 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { normalizePathCase, isFileSystemCaseSensitive } from 'pyright-internal/common/pathUtils';
import { PyrightFileSystem } from 'pyright-internal/pyrightFileSystem';
import { createFromRealFileSystem } from 'pyright-internal/common/realFileSystem';
export enum SeenCondition {
AlwaysFalse = 'always-false',
AlwaysTrue = 'always-true',
Mixed = 'mixed'
}
export class AssertionError extends Error {
constructor(message: string) {
super(message);
this.name = 'AssertionError';
}
}
// Private global state - never export directly
let _assertionFlags = {
pathNormalizationChecks: false,
otherChecks: false
};
let _context = '';
const _sometimesResults = new Map<string, Map<string, SeenCondition>>();
export function setGlobalAssertionFlags(pathNormalizationChecks: boolean, otherChecks: boolean): void {
_assertionFlags.pathNormalizationChecks = pathNormalizationChecks;
_assertionFlags.otherChecks = otherChecks;
}
export function setGlobalContext(context: string): void {
_context = context;
}
// Internal implementation functions
function assertAlwaysImpl(enableFlag: boolean, check: () => boolean, message: () => string): void {
if (!enableFlag) return;
if (!check()) {
throw new AssertionError(message());
}
}
function assertSometimesImpl(enableFlag: boolean, check: () => boolean, key: string): void {
if (!enableFlag) return;
const ctx = _context;
if (ctx === '') {
throw new AssertionError('Context must be set before calling assertSometimes');
}
let ctxMap = _sometimesResults.get(key);
if (!ctxMap) {
ctxMap = new Map();
_sometimesResults.set(key, ctxMap);
}
const result = check() ? SeenCondition.AlwaysTrue : SeenCondition.AlwaysFalse;
const prev = ctxMap.get(ctx);
if (prev === undefined) {
ctxMap.set(ctx, result);
} else if (prev !== result) {
ctxMap.set(ctx, SeenCondition.Mixed);
}
}
const _fs = new PyrightFileSystem(createFromRealFileSystem());
export function assertAlways(check: () => boolean, message: () => string): void {
assertAlwaysImpl(_assertionFlags.otherChecks, check, message);
}
export function assertSometimes(check: () => boolean, key: string): void {
assertSometimesImpl(_assertionFlags.otherChecks, check, key);
}
export function assertNeverNormalized(path: string): void {
const normalized = normalizePathCase(_fs, path);
assertAlwaysImpl(
_assertionFlags.pathNormalizationChecks,
() => normalized !== path,
() => `Path should not be normalized but was: ${path}`
);
}
export function assertAlwaysNormalized(path: string): void {
const normalized = normalizePathCase(_fs, path);
assertAlwaysImpl(
_assertionFlags.pathNormalizationChecks,
() => normalized === path,
() => `Path should be normalized but was not: ${path} -> ${normalized}`
);
}
export function assertSometimesNormalized(path: string, key: string): void {
const normalized = normalizePathCase(_fs, path);
assertSometimesImpl(
_assertionFlags.pathNormalizationChecks,
() => normalized === path,
key
);
}
// Monoidal combination logic
function combine(a: SeenCondition, b: SeenCondition): SeenCondition {
if (a === b) return a;
if (a === SeenCondition.Mixed || b === SeenCondition.Mixed) {
return SeenCondition.Mixed;
}
// AlwaysTrue + AlwaysFalse = Mixed
return SeenCondition.Mixed;
}
export function checkSometimesAssertions(): Map<string, SeenCondition> {
const summary = new Map<string, SeenCondition>();
for (const [key, ctxMap] of _sometimesResults) {
let agg: SeenCondition | undefined;
for (const state of ctxMap.values()) {
agg = agg === undefined ? state : combine(agg, state);
if (agg === SeenCondition.Mixed) break;
}
if (agg !== undefined) {
summary.set(key, agg);
}
}
return summary;
}