@chenglou/freerange 0.0.2 → 0.0.3
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.
- package/CHANGELOG.md +8 -0
- package/README.md +230 -66
- package/dist/fr.js +405 -255
- package/package.json +1 -1
- package/src/audit.ts +2 -1
- package/src/domain/value-identity.ts +56 -0
- package/src/engine/analyze.ts +19 -12
- package/src/engine/state.ts +23 -11
- package/src/engine/transfer.ts +78 -29
- package/src/ir/instructions.ts +7 -1
- package/src/ir/program.ts +7 -0
- package/src/lower/accept.ts +6 -2
- package/src/lower/context.ts +11 -5
- package/src/lower/expression.ts +61 -33
- package/src/lower/function-unit.ts +64 -0
- package/src/lower/module.ts +20 -4
- package/src/lower/platform.ts +3 -0
- package/src/lower/program.ts +62 -34
- package/src/lower/statements.ts +3 -8
- package/src/lower/static-intrinsics.ts +9 -8
- package/src/project.ts +2 -2
- package/src/report/index.ts +2 -0
- package/src/requirements/infer.ts +76 -25
package/dist/fr.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/project.ts
|
|
4
4
|
import { existsSync, realpathSync } from "node:fs";
|
|
5
5
|
import { resolve as resolve3 } from "node:path";
|
|
6
|
-
import * as
|
|
6
|
+
import * as ts13 from "typescript";
|
|
7
7
|
|
|
8
8
|
// src/domain/number.ts
|
|
9
9
|
var float64Scratch = new Float64Array(1);
|
|
@@ -663,6 +663,7 @@ function declaredKindOf(category) {
|
|
|
663
663
|
case "kind":
|
|
664
664
|
return category.declaredKind;
|
|
665
665
|
case "importedConstant":
|
|
666
|
+
case "function":
|
|
666
667
|
case "import":
|
|
667
668
|
case "opaque":
|
|
668
669
|
return null;
|
|
@@ -775,20 +776,61 @@ function siteLocation(program, site) {
|
|
|
775
776
|
return { line: low + 1, column: span.start - lineStarts[low] + 1 };
|
|
776
777
|
}
|
|
777
778
|
|
|
778
|
-
// src/
|
|
779
|
-
function
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
779
|
+
// src/domain/value-identity.ts
|
|
780
|
+
function createValueIdentityOwner(parent = null) {
|
|
781
|
+
return { kind: "valueIdentityOwner", parent };
|
|
782
|
+
}
|
|
783
|
+
function sameValueIdentity(left, right) {
|
|
784
|
+
if (left === right)
|
|
785
|
+
return true;
|
|
786
|
+
if (left.kind !== right.kind)
|
|
787
|
+
return false;
|
|
788
|
+
switch (left.kind) {
|
|
789
|
+
case "local":
|
|
790
|
+
return right.kind === "local" && left.owner === right.owner && left.value === right.value;
|
|
791
|
+
case "property":
|
|
792
|
+
return right.kind === "property" && left.property === right.property && sameValueIdentity(left.object, right.object);
|
|
793
|
+
case "arrayIndex":
|
|
794
|
+
return right.kind === "arrayIndex" && sameValueIdentity(left.array, right.array) && sameValueIdentity(left.index, right.index);
|
|
783
795
|
}
|
|
796
|
+
}
|
|
797
|
+
function valueIdentityUsesOwner(identity, owner) {
|
|
798
|
+
switch (identity.kind) {
|
|
799
|
+
case "local": {
|
|
800
|
+
let current = identity.owner;
|
|
801
|
+
while (current != null) {
|
|
802
|
+
if (current === owner)
|
|
803
|
+
return true;
|
|
804
|
+
current = current.parent;
|
|
805
|
+
}
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
case "property":
|
|
809
|
+
return valueIdentityUsesOwner(identity.object, owner);
|
|
810
|
+
case "arrayIndex":
|
|
811
|
+
return valueIdentityUsesOwner(identity.array, owner) || valueIdentityUsesOwner(identity.index, owner);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/requirements/infer.ts
|
|
816
|
+
function createExpressionContext(fn, parameterExpressions, parameterIdentities, identityOwner = createValueIdentityOwner()) {
|
|
784
817
|
const context = {
|
|
785
818
|
parameterExpressions,
|
|
786
|
-
|
|
787
|
-
|
|
819
|
+
parameterIdentities: [],
|
|
820
|
+
identityOwner,
|
|
821
|
+
identityByValue: [],
|
|
788
822
|
parameterIndexByValue: [],
|
|
789
823
|
instructionByValue: [],
|
|
790
824
|
instructionCount: 0
|
|
791
825
|
};
|
|
826
|
+
context.parameterIdentities = parameterIdentities ?? fn.parameters.map((parameter) => ({
|
|
827
|
+
kind: "local",
|
|
828
|
+
owner: identityOwner,
|
|
829
|
+
value: parameter.value
|
|
830
|
+
}));
|
|
831
|
+
if (context.parameterIdentities.length !== fn.parameters.length) {
|
|
832
|
+
throw new Error(`Expected ${fn.parameters.length} parameter identities for ${fn.name}`);
|
|
833
|
+
}
|
|
792
834
|
for (let index = 0;index < fn.parameters.length; index++) {
|
|
793
835
|
context.parameterIndexByValue[fn.parameters[index].value] = index;
|
|
794
836
|
}
|
|
@@ -891,28 +933,58 @@ function staticRequirement(instruction, site, context, purpose) {
|
|
|
891
933
|
}
|
|
892
934
|
return null;
|
|
893
935
|
}
|
|
894
|
-
function
|
|
936
|
+
function canonicalValueIdentity(value, context) {
|
|
937
|
+
const cached = context.identityByValue[value];
|
|
938
|
+
if (cached != null)
|
|
939
|
+
return cached;
|
|
895
940
|
const stored = resolveStoredValue(value, context);
|
|
896
|
-
if (stored !== value)
|
|
897
|
-
|
|
941
|
+
if (stored !== value) {
|
|
942
|
+
const identity2 = canonicalValueIdentity(stored, context);
|
|
943
|
+
context.identityByValue[value] = identity2;
|
|
944
|
+
return identity2;
|
|
945
|
+
}
|
|
898
946
|
const parameterIndex = context.parameterIndexByValue[value];
|
|
899
|
-
if (parameterIndex != null)
|
|
900
|
-
|
|
947
|
+
if (parameterIndex != null) {
|
|
948
|
+
const identity2 = context.parameterIdentities[parameterIndex];
|
|
949
|
+
if (identity2 == null)
|
|
950
|
+
throw new Error(`Missing identity for parameter ${parameterIndex}`);
|
|
951
|
+
context.identityByValue[value] = identity2;
|
|
952
|
+
return identity2;
|
|
953
|
+
}
|
|
901
954
|
const producer = context.instructionByValue[value];
|
|
955
|
+
let identity;
|
|
902
956
|
if (producer?.kind === "property") {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
957
|
+
identity = {
|
|
958
|
+
kind: "property",
|
|
959
|
+
object: canonicalValueIdentity(producer.object, context),
|
|
960
|
+
property: producer.property
|
|
961
|
+
};
|
|
962
|
+
} else if (producer?.kind === "arrayLength") {
|
|
963
|
+
identity = {
|
|
964
|
+
kind: "property",
|
|
965
|
+
object: canonicalValueIdentity(producer.array, context),
|
|
966
|
+
property: "length"
|
|
967
|
+
};
|
|
968
|
+
} else if (producer?.kind === "stringLength") {
|
|
969
|
+
identity = {
|
|
970
|
+
kind: "property",
|
|
971
|
+
object: canonicalValueIdentity(producer.value, context),
|
|
972
|
+
property: "length"
|
|
973
|
+
};
|
|
974
|
+
} else if (producer?.kind === "arrayIndex") {
|
|
975
|
+
identity = {
|
|
976
|
+
kind: "arrayIndex",
|
|
977
|
+
array: canonicalValueIdentity(producer.array, context),
|
|
978
|
+
index: canonicalValueIdentity(producer.index, context)
|
|
979
|
+
};
|
|
980
|
+
} else {
|
|
981
|
+
identity = { kind: "local", owner: context.identityOwner, value };
|
|
911
982
|
}
|
|
912
|
-
|
|
983
|
+
context.identityByValue[value] = identity;
|
|
984
|
+
return identity;
|
|
913
985
|
}
|
|
914
986
|
function sameRuntimeValue(left, right, context) {
|
|
915
|
-
return left === right ||
|
|
987
|
+
return left === right || sameValueIdentity(canonicalValueIdentity(left, context), canonicalValueIdentity(right, context));
|
|
916
988
|
}
|
|
917
989
|
function addPrecondition(preconditions, candidate) {
|
|
918
990
|
if (candidate.kind === "declaredNumberCheck") {
|
|
@@ -1078,10 +1150,10 @@ function completedEvaluation(evaluation) {
|
|
|
1078
1150
|
|
|
1079
1151
|
// src/engine/state.ts
|
|
1080
1152
|
function hasNonzeroFact(facts, value) {
|
|
1081
|
-
return facts.some((fact) => fact.kind === "nonzero" && fact.value
|
|
1153
|
+
return facts.some((fact) => fact.kind === "nonzero" && sameValueIdentity(fact.value, value));
|
|
1082
1154
|
}
|
|
1083
1155
|
function hasIndexFact(facts, kind, index, array) {
|
|
1084
|
-
return facts.some((fact) => fact.kind === kind && fact.index
|
|
1156
|
+
return facts.some((fact) => fact.kind === kind && sameValueIdentity(fact.index, index) && sameValueIdentity(fact.array, array));
|
|
1085
1157
|
}
|
|
1086
1158
|
function addValueFact(facts, candidate) {
|
|
1087
1159
|
if (!facts.some((fact) => sameValueFact(fact, candidate)))
|
|
@@ -1103,18 +1175,19 @@ function intersectValueFact(left, right) {
|
|
|
1103
1175
|
return left;
|
|
1104
1176
|
if (left.kind === "nonzero" || right.kind === "nonzero")
|
|
1105
1177
|
return null;
|
|
1106
|
-
if (left.index
|
|
1178
|
+
if (!sameValueIdentity(left.index, right.index) || !sameValueIdentity(left.array, right.array))
|
|
1107
1179
|
return null;
|
|
1108
1180
|
return { kind: "belowLength", index: left.index, array: left.array };
|
|
1109
1181
|
}
|
|
1110
1182
|
function sameValueFact(left, right) {
|
|
1111
1183
|
if (left.kind !== right.kind)
|
|
1112
1184
|
return false;
|
|
1113
|
-
if (left.kind === "nonzero" && right.kind === "nonzero")
|
|
1114
|
-
return left.value
|
|
1185
|
+
if (left.kind === "nonzero" && right.kind === "nonzero") {
|
|
1186
|
+
return sameValueIdentity(left.value, right.value);
|
|
1187
|
+
}
|
|
1115
1188
|
if (left.kind === "nonzero" || right.kind === "nonzero")
|
|
1116
1189
|
return false;
|
|
1117
|
-
return left.index
|
|
1190
|
+
return sameValueIdentity(left.index, right.index) && sameValueIdentity(left.array, right.array);
|
|
1118
1191
|
}
|
|
1119
1192
|
function emptySharedState(moduleCount) {
|
|
1120
1193
|
return Array.from({ length: moduleCount }, () => null);
|
|
@@ -1268,10 +1341,10 @@ function evaluateInstructionKinded(instruction, state, context) {
|
|
|
1268
1341
|
const index = requiredNumberWithFacts(state, instruction.index, context.expressionContext);
|
|
1269
1342
|
const element = sequence.kind === "tuple" ? tupleElement(sequence, index) : sequence.element;
|
|
1270
1343
|
const length = sequence.kind === "tuple" ? constantNumber(sequence.elements.length) : sequence.length;
|
|
1271
|
-
const
|
|
1272
|
-
const
|
|
1273
|
-
const assumedValid = hasIndexFact(state.valueFacts, "validIndex",
|
|
1274
|
-
const inBounds = assumedValid || index.integer && !index.mayBeNaN && index.lower >= 0 && index.upper < length.lower || index.integer && !index.mayBeNaN && index.lower >= 0 && hasIndexFact(state.valueFacts, "belowLength",
|
|
1344
|
+
const indexIdentity = canonicalValueIdentity(instruction.index, context.expressionContext);
|
|
1345
|
+
const arrayIdentity = canonicalValueIdentity(instruction.array, context.expressionContext);
|
|
1346
|
+
const assumedValid = hasIndexFact(state.valueFacts, "validIndex", indexIdentity, arrayIdentity);
|
|
1347
|
+
const inBounds = assumedValid || index.integer && !index.mayBeNaN && index.lower >= 0 && index.upper < length.lower || index.integer && !index.mayBeNaN && index.lower >= 0 && hasIndexFact(state.valueFacts, "belowLength", indexIdentity, arrayIdentity);
|
|
1275
1348
|
const firstPossibleIndex = Math.ceil(Math.max(index.lower, 0));
|
|
1276
1349
|
const lastPossibleIndex = Math.floor(Math.min(index.upper, length.upper - 1));
|
|
1277
1350
|
const provablyOut = element == null || firstPossibleIndex > lastPossibleIndex;
|
|
@@ -1298,7 +1371,11 @@ function evaluateInstructionKinded(instruction, state, context) {
|
|
|
1298
1371
|
addBoundsAssumption(context.boundsAssumptions, { site: instruction.site, kind: "elementInBounds" });
|
|
1299
1372
|
}
|
|
1300
1373
|
writeThroughProducers(state, instruction.index, validIndexNumber(index), context.expressionContext.instructionByValue);
|
|
1301
|
-
addValueFact(state.valueFacts, {
|
|
1374
|
+
addValueFact(state.valueFacts, {
|
|
1375
|
+
kind: "validIndex",
|
|
1376
|
+
index: indexIdentity,
|
|
1377
|
+
array: arrayIdentity
|
|
1378
|
+
});
|
|
1302
1379
|
}
|
|
1303
1380
|
return passthroughValue(element);
|
|
1304
1381
|
}
|
|
@@ -1507,6 +1584,15 @@ function evaluateInstructionKinded(instruction, state, context) {
|
|
|
1507
1584
|
return computedNumber(maximumNumbers(operands), operands, instruction.site);
|
|
1508
1585
|
}
|
|
1509
1586
|
case "call": {
|
|
1587
|
+
if (instruction.binding != null && state.shared[instruction.binding] == null) {
|
|
1588
|
+
return {
|
|
1589
|
+
kind: "stop",
|
|
1590
|
+
stop: {
|
|
1591
|
+
site: instruction.site,
|
|
1592
|
+
reason: { kind: "moduleRead", binding: instruction.binding }
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1510
1596
|
const callee = context.program.functions[instruction.function];
|
|
1511
1597
|
if (callee == null)
|
|
1512
1598
|
throw new Error(`Unknown function ${instruction.function}`);
|
|
@@ -1518,9 +1604,9 @@ function evaluateInstructionKinded(instruction, state, context) {
|
|
|
1518
1604
|
}
|
|
1519
1605
|
const arguments_ = instruction.arguments.map((id) => requiredValue(state, id));
|
|
1520
1606
|
const argumentExpressions = instruction.arguments.map((id) => numericExpression(id, context.expressionContext));
|
|
1521
|
-
const
|
|
1522
|
-
const
|
|
1523
|
-
const evaluation = context.evaluateFunction(instruction.function, arguments_, argumentExpressions, state.shared, context.callStack, state.valueFacts,
|
|
1607
|
+
const argumentIdentities = instruction.arguments.map((id) => canonicalValueIdentity(id, context.expressionContext));
|
|
1608
|
+
const calleeOwner = createValueIdentityOwner(context.expressionContext.identityOwner);
|
|
1609
|
+
const evaluation = context.evaluateFunction(instruction.function, arguments_, argumentExpressions, state.shared, context.callStack, state.valueFacts, argumentIdentities, calleeOwner);
|
|
1524
1610
|
const completed = completedEvaluation(evaluation);
|
|
1525
1611
|
if (completed == null) {
|
|
1526
1612
|
if (evaluation.stops.length === 0 && evaluation.normal == null) {
|
|
@@ -1541,7 +1627,7 @@ function evaluateInstructionKinded(instruction, state, context) {
|
|
|
1541
1627
|
return { kind: "stop", stop: { site: instruction.site, reason: { kind: "calleeStopped", callee: instruction.function } } };
|
|
1542
1628
|
}
|
|
1543
1629
|
state.shared = completed.sharedState;
|
|
1544
|
-
state.valueFacts = completed.valueFacts.filter((fact) => !
|
|
1630
|
+
state.valueFacts = completed.valueFacts.filter((fact) => !valueFactUsesOwner(fact, calleeOwner));
|
|
1545
1631
|
for (let index = 0;index < callee.parameters.length; index++) {
|
|
1546
1632
|
refineFiniteCallArgument(state, instruction.arguments[index], callee.parameters[index].type, context.expressionContext);
|
|
1547
1633
|
}
|
|
@@ -1583,11 +1669,10 @@ function addBoundsAssumption(assumptions, candidate) {
|
|
|
1583
1669
|
assumptions.push(candidate);
|
|
1584
1670
|
}
|
|
1585
1671
|
}
|
|
1586
|
-
function
|
|
1587
|
-
const marker = `v:${namespace}`;
|
|
1672
|
+
function valueFactUsesOwner(fact, owner) {
|
|
1588
1673
|
if (fact.kind === "nonzero")
|
|
1589
|
-
return fact.value
|
|
1590
|
-
return fact.index
|
|
1674
|
+
return valueIdentityUsesOwner(fact.value, owner);
|
|
1675
|
+
return valueIdentityUsesOwner(fact.index, owner) || valueIdentityUsesOwner(fact.array, owner);
|
|
1591
1676
|
}
|
|
1592
1677
|
function sentinelsAdmit(sentinels, sentinel) {
|
|
1593
1678
|
return sentinels === "both" || sentinels === sentinel;
|
|
@@ -1947,8 +2032,8 @@ function refineComparison(state, comparison, truth, expressionContext) {
|
|
|
1947
2032
|
if (rightProducer?.kind === "arrayLength") {
|
|
1948
2033
|
addValueFact(result.valueFacts, {
|
|
1949
2034
|
kind: "belowLength",
|
|
1950
|
-
index:
|
|
1951
|
-
array:
|
|
2035
|
+
index: canonicalValueIdentity(comparison.left, expressionContext),
|
|
2036
|
+
array: canonicalValueIdentity(rightProducer.array, expressionContext)
|
|
1952
2037
|
});
|
|
1953
2038
|
}
|
|
1954
2039
|
}
|
|
@@ -1957,8 +2042,8 @@ function refineComparison(state, comparison, truth, expressionContext) {
|
|
|
1957
2042
|
if (leftProducer?.kind === "arrayLength") {
|
|
1958
2043
|
addValueFact(result.valueFacts, {
|
|
1959
2044
|
kind: "belowLength",
|
|
1960
|
-
index:
|
|
1961
|
-
array:
|
|
2045
|
+
index: canonicalValueIdentity(comparison.right, expressionContext),
|
|
2046
|
+
array: canonicalValueIdentity(leftProducer.array, expressionContext)
|
|
1962
2047
|
});
|
|
1963
2048
|
}
|
|
1964
2049
|
}
|
|
@@ -2013,12 +2098,13 @@ function numberWithFacts(state, id, expressionContext) {
|
|
|
2013
2098
|
if (held?.kind !== "number")
|
|
2014
2099
|
return null;
|
|
2015
2100
|
let result = held;
|
|
2016
|
-
const
|
|
2017
|
-
if (state.valueFacts.some((fact) => fact.kind === "validIndex" && fact.index
|
|
2101
|
+
const identity = canonicalValueIdentity(id, expressionContext);
|
|
2102
|
+
if (state.valueFacts.some((fact) => fact.kind === "validIndex" && sameValueIdentity(fact.index, identity))) {
|
|
2018
2103
|
result = validIndexNumber(result);
|
|
2019
2104
|
}
|
|
2020
|
-
if (hasNonzeroFact(state.valueFacts,
|
|
2105
|
+
if (hasNonzeroFact(state.valueFacts, identity)) {
|
|
2021
2106
|
result = excludePointFrom(result, constantNumber(0));
|
|
2107
|
+
}
|
|
2022
2108
|
return result;
|
|
2023
2109
|
}
|
|
2024
2110
|
function validIndexNumber(value2) {
|
|
@@ -2040,7 +2126,10 @@ function recordNonzeroComparisonFacts(state, check, expressionContext) {
|
|
|
2040
2126
|
}
|
|
2041
2127
|
}
|
|
2042
2128
|
function recordNonzeroValueFact(state, value2, expressionContext) {
|
|
2043
|
-
addValueFact(state.valueFacts, {
|
|
2129
|
+
addValueFact(state.valueFacts, {
|
|
2130
|
+
kind: "nonzero",
|
|
2131
|
+
value: canonicalValueIdentity(value2, expressionContext)
|
|
2132
|
+
});
|
|
2044
2133
|
}
|
|
2045
2134
|
function requiredBoolean(state, id) {
|
|
2046
2135
|
const value2 = requiredValue(state, id);
|
|
@@ -2431,7 +2520,7 @@ function analyzeProgram(program) {
|
|
|
2431
2520
|
initializerState[binding] = constantNumber(category.value);
|
|
2432
2521
|
}
|
|
2433
2522
|
}
|
|
2434
|
-
const initializer = runEvaluation(program.initializer, null, [], [], initializerState, program, []
|
|
2523
|
+
const initializer = runEvaluation(program.initializer, null, [], [], initializerState, program, []);
|
|
2435
2524
|
const moduleValues = publishedModuleValues(program, initializer.run, initializer.evaluation);
|
|
2436
2525
|
const functionEntrySharedState = seedModuleSlots(program, moduleValues);
|
|
2437
2526
|
const moduleReads = transitiveModuleBindings(functionUsage(program));
|
|
@@ -2452,8 +2541,7 @@ function analyzeProgram(program) {
|
|
|
2452
2541
|
argumentExpressions.push({ kind: "parameter", index });
|
|
2453
2542
|
}
|
|
2454
2543
|
const { evaluation } = runEvaluation(fn, functionID, arguments_, argumentExpressions, sharedState, program, [], {
|
|
2455
|
-
boundsAssumptions: moduleReads[functionID].size > 0 ? initializerBounds : []
|
|
2456
|
-
identityNamespace: `function:${functionID}/`
|
|
2544
|
+
boundsAssumptions: moduleReads[functionID].size > 0 ? initializerBounds : []
|
|
2457
2545
|
});
|
|
2458
2546
|
functions.push(publishedAnalysis(fn, evaluation));
|
|
2459
2547
|
}
|
|
@@ -2567,9 +2655,9 @@ function publishedModuleValues(program, run, evaluation) {
|
|
|
2567
2655
|
}
|
|
2568
2656
|
const fullyAnalyzed = evaluation.stops.length === 0 && program.initializerSkips.length === 0 && program.functions.every((lowered) => lowered.kind === "lowered");
|
|
2569
2657
|
return program.moduleBindings.map((binding, index) => {
|
|
2570
|
-
if (binding.category.kind !== "value" || demoted.has(index))
|
|
2658
|
+
if (binding.category.kind !== "value" && binding.category.kind !== "function" || demoted.has(index))
|
|
2571
2659
|
return null;
|
|
2572
|
-
if (holdsMutableStructure(binding.category.declaredKind) && !fullyAnalyzed)
|
|
2660
|
+
if (binding.category.kind === "value" && holdsMutableStructure(binding.category.declaredKind) && !fullyAnalyzed)
|
|
2573
2661
|
return null;
|
|
2574
2662
|
const slot = end?.[index];
|
|
2575
2663
|
return slot ?? null;
|
|
@@ -2588,7 +2676,7 @@ function runEvaluation(fn, functionID, arguments_, argumentExpressions, sharedSt
|
|
|
2588
2676
|
for (let index = 0;index < fn.parameters.length; index++) {
|
|
2589
2677
|
initial.values[fn.parameters[index].value] = arguments_[index];
|
|
2590
2678
|
}
|
|
2591
|
-
const expressionContext = createExpressionContext(fn, argumentExpressions, seed.
|
|
2679
|
+
const expressionContext = createExpressionContext(fn, argumentExpressions, seed.parameterIdentities, seed.identityOwner);
|
|
2592
2680
|
const preconditions = [];
|
|
2593
2681
|
const boundsAssumptions = [...seed.boundsAssumptions ?? []];
|
|
2594
2682
|
const successors = blockSuccessors(fn);
|
|
@@ -2607,13 +2695,13 @@ function runEvaluation(fn, functionID, arguments_, argumentExpressions, sharedSt
|
|
|
2607
2695
|
expressionContext,
|
|
2608
2696
|
preconditions,
|
|
2609
2697
|
boundsAssumptions,
|
|
2610
|
-
evaluateFunction: (callee, values, expressions, calleeState, stack, valueFacts,
|
|
2698
|
+
evaluateFunction: (callee, values, expressions, calleeState, stack, valueFacts, parameterIdentities, identityOwner) => {
|
|
2611
2699
|
const calleeFn = program.functions[callee];
|
|
2612
2700
|
if (calleeFn == null)
|
|
2613
2701
|
throw new Error(`Unknown function ${callee}`);
|
|
2614
2702
|
if (calleeFn.kind !== "lowered")
|
|
2615
2703
|
throw new Error(`Analysis reached unlowered function ${calleeFn.name}`);
|
|
2616
|
-
return runEvaluation(calleeFn, callee, values, expressions, calleeState, program, stack, { valueFacts,
|
|
2704
|
+
return runEvaluation(calleeFn, callee, values, expressions, calleeState, program, stack, { valueFacts, parameterIdentities, identityOwner }).evaluation;
|
|
2617
2705
|
}
|
|
2618
2706
|
};
|
|
2619
2707
|
let queueIndex = 0;
|
|
@@ -2887,13 +2975,13 @@ function reachableFrom(successors, start) {
|
|
|
2887
2975
|
}
|
|
2888
2976
|
|
|
2889
2977
|
// src/lower/program.ts
|
|
2890
|
-
import * as
|
|
2978
|
+
import * as ts9 from "typescript";
|
|
2891
2979
|
|
|
2892
2980
|
// src/lower/accept.ts
|
|
2893
2981
|
import * as ts from "typescript";
|
|
2894
2982
|
|
|
2895
2983
|
// src/lower/context.ts
|
|
2896
|
-
function createFunctionContext(sourceFile, checker, functionsBySymbol, moduleBindingsBySymbol, sites, staticAnnotations = []) {
|
|
2984
|
+
function createFunctionContext(sourceFile, checker, functionsBySymbol, moduleBindingsBySymbol, sites, staticAnnotations = [], returnsVoid = true) {
|
|
2897
2985
|
const entry = { loopHeader: null, parameters: [], instructions: [], terminator: null };
|
|
2898
2986
|
return {
|
|
2899
2987
|
sourceFile,
|
|
@@ -2908,6 +2996,7 @@ function createFunctionContext(sourceFile, checker, functionsBySymbol, moduleBin
|
|
|
2908
2996
|
bindings: new Map,
|
|
2909
2997
|
parameters: [],
|
|
2910
2998
|
assertions: [],
|
|
2999
|
+
returnsVoid,
|
|
2911
3000
|
loops: []
|
|
2912
3001
|
};
|
|
2913
3002
|
}
|
|
@@ -3025,8 +3114,10 @@ function unsupported(node, reason) {
|
|
|
3025
3114
|
}
|
|
3026
3115
|
|
|
3027
3116
|
// src/lower/accept.ts
|
|
3028
|
-
function assertAccepted(root) {
|
|
3117
|
+
function assertAccepted(root, deferFunctionBodies = false) {
|
|
3029
3118
|
const visit = (node) => {
|
|
3119
|
+
if (deferFunctionBodies && node !== root && ts.isFunctionLike(node))
|
|
3120
|
+
return;
|
|
3030
3121
|
if (ts.isTypeNode(node))
|
|
3031
3122
|
return;
|
|
3032
3123
|
if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node)) {
|
|
@@ -3116,7 +3207,8 @@ var catalog = [
|
|
|
3116
3207
|
{ path: ["document", "body", "scrollTop"], call: false, fact: { ...anyFinite, integer: false } },
|
|
3117
3208
|
{ path: ["document", "body", "scrollLeft"], call: false, fact: { ...anyFinite, integer: false } },
|
|
3118
3209
|
{ path: ["performance", "now"], call: true, fact: { lower: 0, upper: Number.MAX_VALUE, integer: false } },
|
|
3119
|
-
{ path: ["Date", "now"], call: true, fact: { lower: 0, upper: Number.MAX_VALUE, integer: true } }
|
|
3210
|
+
{ path: ["Date", "now"], call: true, fact: { lower: 0, upper: Number.MAX_VALUE, integer: true } },
|
|
3211
|
+
{ path: ["Math", "random"], call: true, fact: { lower: 0, upper: 0.9999999999999999, integer: false } }
|
|
3120
3212
|
];
|
|
3121
3213
|
function platformFact(expression, call, checker) {
|
|
3122
3214
|
const parts = [];
|
|
@@ -3499,7 +3591,8 @@ function lowerExpression(expression, context) {
|
|
|
3499
3591
|
arguments_.push(lowerParameterDefault(default_2, parameter.initializer, context));
|
|
3500
3592
|
continue;
|
|
3501
3593
|
}
|
|
3502
|
-
|
|
3594
|
+
const callableParameter = callee.signature?.declaration?.parameters[index];
|
|
3595
|
+
if (callableParameter != null && ts4.isParameter(callableParameter) && callableParameter.questionToken != null) {
|
|
3503
3596
|
arguments_.push(addInstruction(context, parameter, { kind: "nullishConstant", sentinel: "undefined" }));
|
|
3504
3597
|
continue;
|
|
3505
3598
|
}
|
|
@@ -3519,7 +3612,12 @@ function lowerExpression(expression, context) {
|
|
|
3519
3612
|
});
|
|
3520
3613
|
arguments_.push(lowerValueBranch(argument, supplied, () => value2, () => lowerParameterDefault(default_, parameter.initializer, context), context));
|
|
3521
3614
|
}
|
|
3522
|
-
return addInstruction(context, current, {
|
|
3615
|
+
return addInstruction(context, current, {
|
|
3616
|
+
kind: "call",
|
|
3617
|
+
function: callee.id,
|
|
3618
|
+
arguments: arguments_,
|
|
3619
|
+
binding: callee.binding
|
|
3620
|
+
});
|
|
3523
3621
|
}
|
|
3524
3622
|
if (ts4.isPropertyAccessExpression(current.expression)) {
|
|
3525
3623
|
const platformCall = current.arguments.length === 0 ? platformFact(current.expression, true, context.checker) : null;
|
|
@@ -3871,6 +3969,15 @@ function lowerValueBranch(node, condition, lowerTrueArm, lowerFalseArm, context)
|
|
|
3871
3969
|
whenFalse: { block: whenFalse, arguments: [] },
|
|
3872
3970
|
site: addSite(context, node)
|
|
3873
3971
|
});
|
|
3972
|
+
return joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context);
|
|
3973
|
+
}
|
|
3974
|
+
function lowerBranchingValue(node, condition, lowerTrueArm, lowerFalseArm, context) {
|
|
3975
|
+
const whenTrue = createBlock(context);
|
|
3976
|
+
const whenFalse = createBlock(context);
|
|
3977
|
+
lowerBranchingCondition(condition, whenTrue, whenFalse, context);
|
|
3978
|
+
return joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context);
|
|
3979
|
+
}
|
|
3980
|
+
function joinValueBranches(node, whenTrue, whenFalse, lowerTrueArm, lowerFalseArm, context) {
|
|
3874
3981
|
context.currentBlock = context.blocks[whenTrue];
|
|
3875
3982
|
const trueValue = lowerTrueArm();
|
|
3876
3983
|
const trueBlock = context.currentBlock;
|
|
@@ -3897,28 +4004,7 @@ function lowerConditionalExpression(expression, context) {
|
|
|
3897
4004
|
if (valueKind(resultType, context.checker) == null) {
|
|
3898
4005
|
throw unsupported(expression, { kind: "valueType", typeText: context.checker.typeToString(resultType) });
|
|
3899
4006
|
}
|
|
3900
|
-
|
|
3901
|
-
const whenFalse = createBlock(context);
|
|
3902
|
-
lowerBranchingCondition(expression.condition, whenTrue, whenFalse, context);
|
|
3903
|
-
context.currentBlock = context.blocks[whenTrue];
|
|
3904
|
-
const trueValue = lowerExpression(expression.whenTrue, context);
|
|
3905
|
-
const trueBlock = context.currentBlock;
|
|
3906
|
-
context.currentBlock = context.blocks[whenFalse];
|
|
3907
|
-
const falseValue = lowerExpression(expression.whenFalse, context);
|
|
3908
|
-
const falseBlock = context.currentBlock;
|
|
3909
|
-
const continuation = createBlock(context, 1);
|
|
3910
|
-
terminate(trueBlock, {
|
|
3911
|
-
kind: "jump",
|
|
3912
|
-
target: { block: continuation, arguments: [trueValue] },
|
|
3913
|
-
site: addSite(context, expression)
|
|
3914
|
-
});
|
|
3915
|
-
terminate(falseBlock, {
|
|
3916
|
-
kind: "jump",
|
|
3917
|
-
target: { block: continuation, arguments: [falseValue] },
|
|
3918
|
-
site: addSite(context, expression)
|
|
3919
|
-
});
|
|
3920
|
-
context.currentBlock = context.blocks[continuation];
|
|
3921
|
-
return context.currentBlock.parameters[0];
|
|
4007
|
+
return lowerBranchingValue(expression, expression.condition, () => lowerExpression(expression.whenTrue, context), () => lowerExpression(expression.whenFalse, context), context);
|
|
3922
4008
|
}
|
|
3923
4009
|
function lowerBranchingCondition(expression, whenTrue, whenFalse, context) {
|
|
3924
4010
|
const current = unwrap(expression, context.checker);
|
|
@@ -3953,8 +4039,7 @@ function lowerLogicalExpression(expression, context) {
|
|
|
3953
4039
|
requireBooleanCondition(expression.left, context.checker);
|
|
3954
4040
|
requireBooleanCondition(expression.right, context.checker);
|
|
3955
4041
|
const isAnd = expression.operatorToken.kind === ts4.SyntaxKind.AmpersandAmpersandToken;
|
|
3956
|
-
|
|
3957
|
-
return lowerValueBranch(expression, condition, () => isAnd ? lowerExpression(expression.right, context) : addInstruction(context, expression, { kind: "booleanConstant", value: true }), () => isAnd ? addInstruction(context, expression, { kind: "booleanConstant", value: false }) : lowerExpression(expression.right, context), context);
|
|
4042
|
+
return lowerBranchingValue(expression, expression.left, () => isAnd ? lowerExpression(expression.right, context) : addInstruction(context, expression, { kind: "booleanConstant", value: true }), () => isAnd ? addInstruction(context, expression, { kind: "booleanConstant", value: false }) : lowerExpression(expression.right, context), context);
|
|
3958
4043
|
}
|
|
3959
4044
|
function arithmeticOperator(kind) {
|
|
3960
4045
|
switch (kind) {
|
|
@@ -4492,11 +4577,51 @@ function unwrap(expression, checker) {
|
|
|
4492
4577
|
}
|
|
4493
4578
|
}
|
|
4494
4579
|
|
|
4580
|
+
// src/lower/function-unit.ts
|
|
4581
|
+
import * as ts5 from "typescript";
|
|
4582
|
+
function callableSignature(unit, checker) {
|
|
4583
|
+
if (unit.initializer == null) {
|
|
4584
|
+
return checker.getSignatureFromDeclaration(unit.declaration) ?? null;
|
|
4585
|
+
}
|
|
4586
|
+
const signatures = checker.getSignaturesOfType(checker.getTypeAtLocation(unit.name), ts5.SignatureKind.Call);
|
|
4587
|
+
if (signatures.length !== 1)
|
|
4588
|
+
return null;
|
|
4589
|
+
const signature = signatures[0];
|
|
4590
|
+
const signatureDeclaration = signature.declaration;
|
|
4591
|
+
if (signatureDeclaration == null || signature.thisParameter != null || signature.parameters.length !== unit.declaration.parameters.length || signatureDeclaration.parameters.some((parameter) => !ts5.isParameter(parameter) || parameter.dotDotDotToken != null)) {
|
|
4592
|
+
return null;
|
|
4593
|
+
}
|
|
4594
|
+
return signature;
|
|
4595
|
+
}
|
|
4596
|
+
function topLevelFunctionUnits(sourceFile) {
|
|
4597
|
+
const units = [];
|
|
4598
|
+
for (const statement of sourceFile.statements) {
|
|
4599
|
+
if (ts5.isFunctionDeclaration(statement) && statement.name != null) {
|
|
4600
|
+
units.push({ name: statement.name, declaration: statement, initializer: null });
|
|
4601
|
+
continue;
|
|
4602
|
+
}
|
|
4603
|
+
if (!ts5.isVariableStatement(statement) || (statement.declarationList.flags & ts5.NodeFlags.Const) === 0)
|
|
4604
|
+
continue;
|
|
4605
|
+
for (const initializer of statement.declarationList.declarations) {
|
|
4606
|
+
if (!ts5.isIdentifier(initializer.name) || initializer.initializer == null)
|
|
4607
|
+
continue;
|
|
4608
|
+
const declaration = directFunctionExpression(initializer.initializer);
|
|
4609
|
+
if (declaration != null) {
|
|
4610
|
+
units.push({ name: initializer.name, declaration, initializer });
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
return units;
|
|
4615
|
+
}
|
|
4616
|
+
function directFunctionExpression(expression) {
|
|
4617
|
+
return ts5.isArrowFunction(expression) || ts5.isFunctionExpression(expression) ? expression : null;
|
|
4618
|
+
}
|
|
4619
|
+
|
|
4495
4620
|
// src/lower/module.ts
|
|
4496
|
-
import * as
|
|
4621
|
+
import * as ts7 from "typescript";
|
|
4497
4622
|
|
|
4498
4623
|
// src/lower/statements.ts
|
|
4499
|
-
import * as
|
|
4624
|
+
import * as ts6 from "typescript";
|
|
4500
4625
|
function lowerStatements(statements, context) {
|
|
4501
4626
|
for (const statement of statements) {
|
|
4502
4627
|
if (context.currentBlock.terminator != null)
|
|
@@ -4505,61 +4630,57 @@ function lowerStatements(statements, context) {
|
|
|
4505
4630
|
}
|
|
4506
4631
|
}
|
|
4507
4632
|
function lowerStatement(statement, context) {
|
|
4508
|
-
if (
|
|
4633
|
+
if (ts6.isVariableStatement(statement)) {
|
|
4509
4634
|
lowerVariableDeclarationList(statement.declarationList, context);
|
|
4510
4635
|
return;
|
|
4511
4636
|
}
|
|
4512
|
-
if (
|
|
4637
|
+
if (ts6.isReturnStatement(statement)) {
|
|
4513
4638
|
let value2 = statement.expression == null ? null : lowerExpression(statement.expression, context);
|
|
4514
|
-
if (
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
const returnsVoid = returnType == null || (returnType.flags & (ts5.TypeFlags.Void | ts5.TypeFlags.Undefined)) !== 0;
|
|
4519
|
-
if (!returnsVoid) {
|
|
4520
|
-
value2 = addInstruction(context, statement, { kind: "nullishConstant", sentinel: "undefined" });
|
|
4521
|
-
}
|
|
4639
|
+
if (context.returnsVoid)
|
|
4640
|
+
value2 = null;
|
|
4641
|
+
if (value2 == null && !context.returnsVoid) {
|
|
4642
|
+
value2 = addInstruction(context, statement, { kind: "nullishConstant", sentinel: "undefined" });
|
|
4522
4643
|
}
|
|
4523
4644
|
terminate(context.currentBlock, { kind: "return", value: value2, site: addSite(context, statement) });
|
|
4524
4645
|
return;
|
|
4525
4646
|
}
|
|
4526
|
-
if (
|
|
4647
|
+
if (ts6.isExpressionStatement(statement)) {
|
|
4527
4648
|
lowerStatementExpression(statement.expression, context);
|
|
4528
4649
|
return;
|
|
4529
4650
|
}
|
|
4530
|
-
if (
|
|
4651
|
+
if (ts6.isIfStatement(statement)) {
|
|
4531
4652
|
lowerIfStatement(statement, context);
|
|
4532
4653
|
return;
|
|
4533
4654
|
}
|
|
4534
|
-
if (
|
|
4655
|
+
if (ts6.isForOfStatement(statement)) {
|
|
4535
4656
|
lowerForOfStatement(statement, context);
|
|
4536
4657
|
return;
|
|
4537
4658
|
}
|
|
4538
|
-
if (
|
|
4659
|
+
if (ts6.isForStatement(statement)) {
|
|
4539
4660
|
lowerForStatement(statement, context);
|
|
4540
4661
|
return;
|
|
4541
4662
|
}
|
|
4542
|
-
if (
|
|
4663
|
+
if (ts6.isWhileStatement(statement)) {
|
|
4543
4664
|
lowerWhileStatement(statement, context);
|
|
4544
4665
|
return;
|
|
4545
4666
|
}
|
|
4546
|
-
if (
|
|
4667
|
+
if (ts6.isContinueStatement(statement)) {
|
|
4547
4668
|
lowerContinueStatement(statement, context);
|
|
4548
4669
|
return;
|
|
4549
4670
|
}
|
|
4550
|
-
if (
|
|
4671
|
+
if (ts6.isBlock(statement)) {
|
|
4551
4672
|
lowerStatements(statement.statements, context);
|
|
4552
4673
|
return;
|
|
4553
4674
|
}
|
|
4554
|
-
if (
|
|
4675
|
+
if (ts6.isSwitchStatement(statement)) {
|
|
4555
4676
|
lowerSwitchStatement(statement, context);
|
|
4556
4677
|
return;
|
|
4557
4678
|
}
|
|
4558
|
-
if (
|
|
4679
|
+
if (ts6.isThrowStatement(statement)) {
|
|
4559
4680
|
terminate(context.currentBlock, { kind: "thrown", site: addSite(context, statement) });
|
|
4560
4681
|
return;
|
|
4561
4682
|
}
|
|
4562
|
-
throw unsupported(statement, { kind: "statementForm", syntax:
|
|
4683
|
+
throw unsupported(statement, { kind: "statementForm", syntax: ts6.SyntaxKind[statement.kind] });
|
|
4563
4684
|
}
|
|
4564
4685
|
function lowerIfStatement(statement, context) {
|
|
4565
4686
|
const bindingsBeforeBranch = new Map(context.bindings);
|
|
@@ -4586,7 +4707,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4586
4707
|
const subjectType = context.checker.getTypeAtLocation(statement.expression);
|
|
4587
4708
|
const subjectKind = valueKind(subjectType, context.checker);
|
|
4588
4709
|
const tagUnionExpression = taggedUnionTagRead(statement.expression, context);
|
|
4589
|
-
const missingFlags =
|
|
4710
|
+
const missingFlags = ts6.TypeFlags.Null | ts6.TypeFlags.Undefined;
|
|
4590
4711
|
const nullableOpaqueSubject = subjectKind === "nullable" && subjectType.isUnion() && subjectType.types.every((member) => (member.flags & missingFlags) !== 0 || valueKind(member, context.checker) === "opaque");
|
|
4591
4712
|
if (tagUnionExpression == null && subjectKind !== "number" && subjectKind !== "opaque" && !nullableOpaqueSubject) {
|
|
4592
4713
|
throw unsupported(statement.expression, { kind: "switchSubject", typeText: context.checker.typeToString(subjectType) });
|
|
@@ -4598,7 +4719,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4598
4719
|
const clauses = statement.caseBlock.clauses;
|
|
4599
4720
|
for (let index = 0;index < clauses.length; index++) {
|
|
4600
4721
|
const clause = clauses[index];
|
|
4601
|
-
if (
|
|
4722
|
+
if (ts6.isDefaultClause(clause)) {
|
|
4602
4723
|
if (index !== clauses.length - 1 || pendingLabels.length > 0) {
|
|
4603
4724
|
throw unsupported(clause, { kind: "switchDefaultNotLast" });
|
|
4604
4725
|
}
|
|
@@ -4619,7 +4740,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4619
4740
|
const lowerBody = (group) => {
|
|
4620
4741
|
const body = group.statements;
|
|
4621
4742
|
const last = body[body.length - 1];
|
|
4622
|
-
const endsWithBreak = last != null &&
|
|
4743
|
+
const endsWithBreak = last != null && ts6.isBreakStatement(last);
|
|
4623
4744
|
lowerStatements(endsWithBreak ? body.slice(0, -1) : body, context);
|
|
4624
4745
|
if (context.currentBlock.terminator == null) {
|
|
4625
4746
|
if (!endsWithBreak)
|
|
@@ -4638,7 +4759,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4638
4759
|
let condition;
|
|
4639
4760
|
if (tagUnionExpression != null) {
|
|
4640
4761
|
const unwrappedLabel = label;
|
|
4641
|
-
if (!
|
|
4762
|
+
if (!ts6.isStringLiteral(unwrappedLabel) && !ts6.isNoSubstitutionTemplateLiteral(unwrappedLabel)) {
|
|
4642
4763
|
throw unsupported(label, { kind: "switchLabel", typeText: context.checker.typeToString(context.checker.getTypeAtLocation(label)) });
|
|
4643
4764
|
}
|
|
4644
4765
|
condition = addInstruction(context, label, { kind: "tagCheck", union: subject, tagValue: unwrappedLabel.text, negated: false });
|
|
@@ -4680,7 +4801,7 @@ function lowerSwitchStatement(statement, context) {
|
|
|
4680
4801
|
mergeAtContinuation(exits, bindingsBefore, statement, context);
|
|
4681
4802
|
}
|
|
4682
4803
|
function lowerForOfStatement(statement, context) {
|
|
4683
|
-
if (!
|
|
4804
|
+
if (!ts6.isVariableDeclarationList(statement.initializer) || statement.initializer.declarations.length !== 1 || !ts6.isIdentifier(statement.initializer.declarations[0].name)) {
|
|
4684
4805
|
throw unsupported(statement.initializer, { kind: "variableDeclarationShape" });
|
|
4685
4806
|
}
|
|
4686
4807
|
const elementName = statement.initializer.declarations[0].name;
|
|
@@ -4757,7 +4878,7 @@ function lowerForOfStatement(statement, context) {
|
|
|
4757
4878
|
}
|
|
4758
4879
|
function lowerForStatement(statement, context) {
|
|
4759
4880
|
if (statement.initializer != null) {
|
|
4760
|
-
if (
|
|
4881
|
+
if (ts6.isVariableDeclarationList(statement.initializer)) {
|
|
4761
4882
|
lowerVariableDeclarationList(statement.initializer, context);
|
|
4762
4883
|
} else {
|
|
4763
4884
|
lowerStatementExpression(statement.initializer, context);
|
|
@@ -4866,13 +4987,13 @@ function lowerBranch(statement, block, bindings, context) {
|
|
|
4866
4987
|
}
|
|
4867
4988
|
function lowerVariableDeclarationList(declarations, context) {
|
|
4868
4989
|
for (const declaration of declarations.declarations) {
|
|
4869
|
-
if (
|
|
4990
|
+
if (ts6.isObjectBindingPattern(declaration.name) && declaration.initializer != null) {
|
|
4870
4991
|
const source = lowerExpression(declaration.initializer, context);
|
|
4871
4992
|
for (const element of declaration.name.elements) {
|
|
4872
|
-
if (!
|
|
4993
|
+
if (!ts6.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
|
|
4873
4994
|
throw unsupported(element, { kind: "variableDeclarationShape" });
|
|
4874
4995
|
}
|
|
4875
|
-
const property = element.propertyName == null ? element.name.text :
|
|
4996
|
+
const property = element.propertyName == null ? element.name.text : ts6.isIdentifier(element.propertyName) ? element.propertyName.text : null;
|
|
4876
4997
|
if (property == null)
|
|
4877
4998
|
throw unsupported(element, { kind: "variableDeclarationShape" });
|
|
4878
4999
|
const elementType = context.checker.getTypeAtLocation(element.name);
|
|
@@ -4889,7 +5010,7 @@ function lowerVariableDeclarationList(declarations, context) {
|
|
|
4889
5010
|
}
|
|
4890
5011
|
continue;
|
|
4891
5012
|
}
|
|
4892
|
-
if (!
|
|
5013
|
+
if (!ts6.isIdentifier(declaration.name) || declaration.initializer == null) {
|
|
4893
5014
|
throw unsupported(declaration, { kind: "variableDeclarationShape" });
|
|
4894
5015
|
}
|
|
4895
5016
|
const value2 = lowerExpression(declaration.initializer, context);
|
|
@@ -4908,12 +5029,12 @@ function lowerVariableDeclarationList(declarations, context) {
|
|
|
4908
5029
|
function assignedSymbols(nodes, checker) {
|
|
4909
5030
|
const symbols = new Set;
|
|
4910
5031
|
const visit = (node) => {
|
|
4911
|
-
if (
|
|
5032
|
+
if (ts6.isFunctionLike(node))
|
|
4912
5033
|
return;
|
|
4913
5034
|
const assignment = identifierAssignment(node);
|
|
4914
5035
|
if (assignment != null)
|
|
4915
5036
|
symbols.add(requiredSymbol(assignment.target, checker));
|
|
4916
|
-
|
|
5037
|
+
ts6.forEachChild(node, visit);
|
|
4917
5038
|
};
|
|
4918
5039
|
for (const node of nodes)
|
|
4919
5040
|
visit(node);
|
|
@@ -4932,45 +5053,46 @@ function scanModuleBindings(sourceFile, checker) {
|
|
|
4932
5053
|
bindings.push({ name: name.text, category });
|
|
4933
5054
|
};
|
|
4934
5055
|
for (const statement of sourceFile.statements) {
|
|
4935
|
-
if (
|
|
4936
|
-
if ((statement.declarationList.flags & (
|
|
5056
|
+
if (ts7.isVariableStatement(statement)) {
|
|
5057
|
+
if ((statement.declarationList.flags & (ts7.NodeFlags.Let | ts7.NodeFlags.Const)) === 0)
|
|
4937
5058
|
continue;
|
|
5059
|
+
const isConst = (statement.declarationList.flags & ts7.NodeFlags.Const) !== 0;
|
|
4938
5060
|
for (const declarator of statement.declarationList.declarations) {
|
|
4939
|
-
if (
|
|
4940
|
-
register(declarator.name, declaredCategory(declarator.name, checker));
|
|
5061
|
+
if (ts7.isIdentifier(declarator.name)) {
|
|
5062
|
+
register(declarator.name, isConst && declarator.initializer != null && directFunctionExpression(declarator.initializer) != null ? { kind: "function" } : declaredCategory(declarator.name, checker));
|
|
4941
5063
|
continue;
|
|
4942
5064
|
}
|
|
4943
|
-
if (
|
|
5065
|
+
if (ts7.isObjectBindingPattern(declarator.name)) {
|
|
4944
5066
|
for (const element of declarator.name.elements) {
|
|
4945
|
-
if (
|
|
5067
|
+
if (ts7.isIdentifier(element.name))
|
|
4946
5068
|
register(element.name, declaredCategory(element.name, checker));
|
|
4947
5069
|
}
|
|
4948
5070
|
}
|
|
4949
5071
|
}
|
|
4950
5072
|
continue;
|
|
4951
5073
|
}
|
|
4952
|
-
if (
|
|
5074
|
+
if (ts7.isImportDeclaration(statement)) {
|
|
4953
5075
|
const clause = statement.importClause;
|
|
4954
5076
|
if (clause == null || clause.isTypeOnly)
|
|
4955
5077
|
continue;
|
|
4956
5078
|
if (clause.name != null)
|
|
4957
5079
|
register(clause.name, importedCategory(clause.name, checker));
|
|
4958
5080
|
const named = clause.namedBindings;
|
|
4959
|
-
if (named != null &&
|
|
5081
|
+
if (named != null && ts7.isNamedImports(named)) {
|
|
4960
5082
|
for (const element of named.elements) {
|
|
4961
5083
|
if (!element.isTypeOnly)
|
|
4962
5084
|
register(element.name, importedCategory(element.name, checker));
|
|
4963
5085
|
}
|
|
4964
5086
|
}
|
|
4965
|
-
if (named != null &&
|
|
5087
|
+
if (named != null && ts7.isNamespaceImport(named))
|
|
4966
5088
|
register(named.name, { kind: "import" });
|
|
4967
5089
|
}
|
|
4968
5090
|
}
|
|
4969
5091
|
const visit = (node, insideFunction) => {
|
|
4970
5092
|
if (insideFunction)
|
|
4971
5093
|
demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings);
|
|
4972
|
-
const enteringFunction = insideFunction ||
|
|
4973
|
-
|
|
5094
|
+
const enteringFunction = insideFunction || ts7.isFunctionLike(node);
|
|
5095
|
+
ts7.forEachChild(node, (child) => {
|
|
4974
5096
|
visit(child, enteringFunction);
|
|
4975
5097
|
});
|
|
4976
5098
|
};
|
|
@@ -4979,13 +5101,13 @@ function scanModuleBindings(sourceFile, checker) {
|
|
|
4979
5101
|
}
|
|
4980
5102
|
function importedCategory(name, checker) {
|
|
4981
5103
|
const symbol = checker.getSymbolAtLocation(name);
|
|
4982
|
-
if (symbol == null || (symbol.flags &
|
|
5104
|
+
if (symbol == null || (symbol.flags & ts7.SymbolFlags.Alias) === 0)
|
|
4983
5105
|
return { kind: "import" };
|
|
4984
5106
|
const target = checker.getAliasedSymbol(symbol);
|
|
4985
5107
|
const declaration = target.valueDeclaration;
|
|
4986
|
-
if (declaration == null || !
|
|
5108
|
+
if (declaration == null || !ts7.isVariableDeclaration(declaration))
|
|
4987
5109
|
return { kind: "import" };
|
|
4988
|
-
if ((
|
|
5110
|
+
if ((ts7.getCombinedNodeFlags(declaration) & ts7.NodeFlags.Const) === 0)
|
|
4989
5111
|
return { kind: "import" };
|
|
4990
5112
|
if (declaration.getSourceFile().isDeclarationFile)
|
|
4991
5113
|
return { kind: "import" };
|
|
@@ -5001,7 +5123,7 @@ function demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, wri
|
|
|
5001
5123
|
written[binding] = true;
|
|
5002
5124
|
};
|
|
5003
5125
|
const target = (expression) => {
|
|
5004
|
-
if (
|
|
5126
|
+
if (ts7.isIdentifier(expression)) {
|
|
5005
5127
|
const symbol = checker.getSymbolAtLocation(expression);
|
|
5006
5128
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5007
5129
|
if (binding != null)
|
|
@@ -5009,34 +5131,34 @@ function demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, wri
|
|
|
5009
5131
|
return;
|
|
5010
5132
|
}
|
|
5011
5133
|
const visitPattern = (child) => {
|
|
5012
|
-
if (
|
|
5134
|
+
if (ts7.isShorthandPropertyAssignment(child)) {
|
|
5013
5135
|
const symbol = checker.getShorthandAssignmentValueSymbol(child);
|
|
5014
5136
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5015
5137
|
if (binding != null)
|
|
5016
5138
|
record(binding);
|
|
5017
|
-
|
|
5139
|
+
ts7.forEachChild(child, visitPattern);
|
|
5018
5140
|
return;
|
|
5019
5141
|
}
|
|
5020
|
-
if (
|
|
5142
|
+
if (ts7.isIdentifier(child)) {
|
|
5021
5143
|
const symbol = checker.getSymbolAtLocation(child);
|
|
5022
5144
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5023
5145
|
if (binding != null)
|
|
5024
5146
|
record(binding);
|
|
5025
5147
|
return;
|
|
5026
5148
|
}
|
|
5027
|
-
|
|
5149
|
+
ts7.forEachChild(child, visitPattern);
|
|
5028
5150
|
};
|
|
5029
|
-
|
|
5151
|
+
ts7.forEachChild(expression, visitPattern);
|
|
5030
5152
|
};
|
|
5031
|
-
if (
|
|
5153
|
+
if (ts7.isBinaryExpression(node)) {
|
|
5032
5154
|
const kind = node.operatorToken.kind;
|
|
5033
|
-
if (kind >=
|
|
5155
|
+
if (kind >= ts7.SyntaxKind.FirstAssignment && kind <= ts7.SyntaxKind.LastAssignment)
|
|
5034
5156
|
target(node.left);
|
|
5035
5157
|
}
|
|
5036
|
-
if ((
|
|
5158
|
+
if ((ts7.isPrefixUnaryExpression(node) || ts7.isPostfixUnaryExpression(node)) && (node.operator === ts7.SyntaxKind.PlusPlusToken || node.operator === ts7.SyntaxKind.MinusMinusToken) && ts7.isExpression(node.operand)) {
|
|
5037
5159
|
target(node.operand);
|
|
5038
5160
|
}
|
|
5039
|
-
if ((
|
|
5161
|
+
if ((ts7.isForOfStatement(node) || ts7.isForInStatement(node)) && ts7.isExpression(node.initializer)) {
|
|
5040
5162
|
target(node.initializer);
|
|
5041
5163
|
}
|
|
5042
5164
|
}
|
|
@@ -5049,8 +5171,8 @@ function lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, si
|
|
|
5049
5171
|
continue;
|
|
5050
5172
|
const recovery = snapshotLowering(context);
|
|
5051
5173
|
try {
|
|
5052
|
-
assertAccepted(statement);
|
|
5053
|
-
if (
|
|
5174
|
+
assertAccepted(statement, true);
|
|
5175
|
+
if (ts7.isVariableStatement(statement)) {
|
|
5054
5176
|
lowerTopLevelDeclarations(statement, context, scan);
|
|
5055
5177
|
continue;
|
|
5056
5178
|
}
|
|
@@ -5088,31 +5210,31 @@ function lowerModuleInitializer(sourceFile, checker, functionsBySymbol, scan, si
|
|
|
5088
5210
|
};
|
|
5089
5211
|
}
|
|
5090
5212
|
function containsArrayLiteral(root) {
|
|
5091
|
-
if (
|
|
5213
|
+
if (ts7.isArrayLiteralExpression(root))
|
|
5092
5214
|
return true;
|
|
5093
5215
|
let found = false;
|
|
5094
|
-
|
|
5216
|
+
ts7.forEachChild(root, (child) => {
|
|
5095
5217
|
if (!found && containsArrayLiteral(child))
|
|
5096
5218
|
found = true;
|
|
5097
5219
|
});
|
|
5098
5220
|
return found;
|
|
5099
5221
|
}
|
|
5100
5222
|
function forEachImmediatelyEvaluatedChild(node, visit) {
|
|
5101
|
-
if (
|
|
5102
|
-
if (node.name != null &&
|
|
5223
|
+
if (ts7.isFunctionLike(node)) {
|
|
5224
|
+
if (node.name != null && ts7.isComputedPropertyName(node.name)) {
|
|
5103
5225
|
visit(node.name.expression);
|
|
5104
5226
|
}
|
|
5105
5227
|
return;
|
|
5106
5228
|
}
|
|
5107
|
-
|
|
5229
|
+
ts7.forEachChild(node, visit);
|
|
5108
5230
|
}
|
|
5109
5231
|
function lowerSupportedArgumentsOfSkippedTopLevelCall(statement, stop, context) {
|
|
5110
|
-
if (!
|
|
5232
|
+
if (!ts7.isExpressionStatement(statement))
|
|
5111
5233
|
return;
|
|
5112
5234
|
let expression = statement.expression;
|
|
5113
|
-
while (
|
|
5235
|
+
while (ts7.isParenthesizedExpression(expression))
|
|
5114
5236
|
expression = expression.expression;
|
|
5115
|
-
if (!
|
|
5237
|
+
if (!ts7.isCallExpression(expression) || expression !== stop.node || expression.questionDotToken != null || !plainCallTarget(expression.expression))
|
|
5116
5238
|
return;
|
|
5117
5239
|
for (const argument of expression.arguments) {
|
|
5118
5240
|
const recovery = snapshotLowering(context);
|
|
@@ -5127,23 +5249,23 @@ function lowerSupportedArgumentsOfSkippedTopLevelCall(statement, stop, context)
|
|
|
5127
5249
|
}
|
|
5128
5250
|
}
|
|
5129
5251
|
function plainCallTarget(expression) {
|
|
5130
|
-
if (
|
|
5252
|
+
if (ts7.isIdentifier(expression))
|
|
5131
5253
|
return true;
|
|
5132
|
-
if (
|
|
5254
|
+
if (ts7.isParenthesizedExpression(expression))
|
|
5133
5255
|
return plainCallTarget(expression.expression);
|
|
5134
|
-
return
|
|
5256
|
+
return ts7.isPropertyAccessExpression(expression) && expression.questionDotToken == null && plainCallTarget(expression.expression);
|
|
5135
5257
|
}
|
|
5136
5258
|
function lowerTopLevelDeclarations(statement, context, scan) {
|
|
5137
5259
|
for (const declarator of statement.declarationList.declarations) {
|
|
5138
5260
|
if (declarator.initializer == null)
|
|
5139
5261
|
throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
|
|
5140
|
-
if (
|
|
5262
|
+
if (ts7.isObjectBindingPattern(declarator.name)) {
|
|
5141
5263
|
const source = lowerExpression(declarator.initializer, context);
|
|
5142
5264
|
for (const element of declarator.name.elements) {
|
|
5143
|
-
if (!
|
|
5265
|
+
if (!ts7.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
|
|
5144
5266
|
throw new LoweringStop(element, { kind: "variableDeclarationShape" });
|
|
5145
5267
|
}
|
|
5146
|
-
const property = element.propertyName == null ? element.name.text :
|
|
5268
|
+
const property = element.propertyName == null ? element.name.text : ts7.isIdentifier(element.propertyName) ? element.propertyName.text : null;
|
|
5147
5269
|
if (property == null)
|
|
5148
5270
|
throw new LoweringStop(element, { kind: "variableDeclarationShape" });
|
|
5149
5271
|
const elementType = context.checker.getTypeAtLocation(element.name);
|
|
@@ -5159,27 +5281,32 @@ function lowerTopLevelDeclarations(statement, context, scan) {
|
|
|
5159
5281
|
}
|
|
5160
5282
|
continue;
|
|
5161
5283
|
}
|
|
5162
|
-
if (!
|
|
5284
|
+
if (!ts7.isIdentifier(declarator.name)) {
|
|
5163
5285
|
throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
|
|
5164
5286
|
}
|
|
5165
5287
|
const symbol = context.checker.getSymbolAtLocation(declarator.name);
|
|
5166
5288
|
const binding = symbol == null ? undefined : scan.bindingsBySymbol.get(symbol);
|
|
5167
5289
|
if (binding == null)
|
|
5168
5290
|
throw new LoweringStop(declarator, { kind: "variableDeclarationShape" });
|
|
5291
|
+
if ((statement.declarationList.flags & ts7.NodeFlags.Const) !== 0 && directFunctionExpression(declarator.initializer) != null) {
|
|
5292
|
+
const marker = addInstruction(context, declarator.initializer, { kind: "opaqueConstant" });
|
|
5293
|
+
addInstruction(context, declarator, { kind: "moduleWrite", binding, value: marker });
|
|
5294
|
+
continue;
|
|
5295
|
+
}
|
|
5169
5296
|
const value2 = lowerExpression(declarator.initializer, context);
|
|
5170
5297
|
addInstruction(context, declarator, { kind: "moduleWrite", binding, value: value2 });
|
|
5171
5298
|
}
|
|
5172
5299
|
}
|
|
5173
5300
|
function skippedAtTopLevel(statement) {
|
|
5174
|
-
return
|
|
5301
|
+
return ts7.isFunctionDeclaration(statement) && statement.name != null || ts7.isImportDeclaration(statement) || ts7.isTypeAliasDeclaration(statement) || ts7.isInterfaceDeclaration(statement) || ts7.isExportDeclaration(statement);
|
|
5175
5302
|
}
|
|
5176
5303
|
function scanSkippedModuleEffects(root, checker, bindingsBySymbol, bindings) {
|
|
5177
5304
|
const directWrites = [];
|
|
5178
5305
|
let invokesUnknownCode = false;
|
|
5179
5306
|
const visit = (node) => {
|
|
5180
5307
|
demoteModuleWritesInNode(node, checker, bindingsBySymbol, bindings, directWrites);
|
|
5181
|
-
invokesUnknownCode ||=
|
|
5182
|
-
if (
|
|
5308
|
+
invokesUnknownCode ||= ts7.isCallExpression(node) || ts7.isNewExpression(node) || ts7.isTaggedTemplateExpression(node) || ts7.isAwaitExpression(node) || ts7.isYieldExpression(node) || ts7.isForOfStatement(node) || ts7.isSpreadElement(node) || ts7.isArrayBindingPattern(node) || ts7.isVariableDeclarationList(node) && (node.flags & ts7.NodeFlags.Using) !== 0 || ts7.isBinaryExpression(node) && (node.operatorToken.kind === ts7.SyntaxKind.InstanceOfKeyword || node.operatorToken.kind === ts7.SyntaxKind.EqualsToken && containsArrayLiteral(node.left)) || ts7.isJsxElement(node) || ts7.isJsxSelfClosingElement(node) || ts7.isJsxFragment(node) || ts7.isClassDeclaration(node) || ts7.isClassExpression(node);
|
|
5309
|
+
if (ts7.isVariableDeclaration(node) && ts7.isIdentifier(node.name)) {
|
|
5183
5310
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
5184
5311
|
const binding = symbol == null ? undefined : bindingsBySymbol.get(symbol);
|
|
5185
5312
|
if (binding != null) {
|
|
@@ -5201,7 +5328,7 @@ function declaredRecordProperties(type, checker, seen) {
|
|
|
5201
5328
|
return null;
|
|
5202
5329
|
const properties = [];
|
|
5203
5330
|
for (const property of checker.getPropertiesOfType(type)) {
|
|
5204
|
-
const optional = (property.flags &
|
|
5331
|
+
const optional = (property.flags & ts7.SymbolFlags.Optional) !== 0;
|
|
5205
5332
|
const walked = declaredKind(checker.getTypeOfSymbol(property), checker, [...seen, type]);
|
|
5206
5333
|
const opaqueLeaf = { kind: "opaque" };
|
|
5207
5334
|
const propertyDeclared = declaredOnlyInDeclarationFiles(property) ? opaqueLeaf : walked ?? opaqueLeaf;
|
|
@@ -5307,14 +5434,14 @@ function declaredKindUncached(type, checker, seen) {
|
|
|
5307
5434
|
}
|
|
5308
5435
|
if (inner == null)
|
|
5309
5436
|
return null;
|
|
5310
|
-
const admitsNull = type.types.some((member) => (member.flags &
|
|
5311
|
-
const admitsUndefined = type.types.some((member) => (member.flags &
|
|
5437
|
+
const admitsNull = type.types.some((member) => (member.flags & ts7.TypeFlags.Null) !== 0);
|
|
5438
|
+
const admitsUndefined = type.types.some((member) => (member.flags & ts7.TypeFlags.Undefined) !== 0);
|
|
5312
5439
|
return { kind: "nullish", inner, sentinels: admitsNull && admitsUndefined ? "both" : admitsNull ? "null" : "undefined" };
|
|
5313
5440
|
}
|
|
5314
5441
|
case "opaque":
|
|
5315
5442
|
return { kind: "opaque" };
|
|
5316
5443
|
case "array": {
|
|
5317
|
-
const element = checker.getIndexTypeOfType(type,
|
|
5444
|
+
const element = checker.getIndexTypeOfType(type, ts7.IndexKind.Number);
|
|
5318
5445
|
if (element == null)
|
|
5319
5446
|
return null;
|
|
5320
5447
|
const elementKind = declaredKind(element, checker, [...seen, type]);
|
|
@@ -5366,7 +5493,7 @@ function declaredKindUncached(type, checker, seen) {
|
|
|
5366
5493
|
}
|
|
5367
5494
|
function numericLiteralInterval(type) {
|
|
5368
5495
|
const members = type.isUnion() ? type.types : [type];
|
|
5369
|
-
if (!members.every((member) => (member.flags &
|
|
5496
|
+
if (!members.every((member) => (member.flags & ts7.TypeFlags.NumberLiteral) !== 0 && (member.flags & ts7.TypeFlags.EnumLiteral) === 0))
|
|
5370
5497
|
return null;
|
|
5371
5498
|
let lower = Infinity;
|
|
5372
5499
|
let upper = -Infinity;
|
|
@@ -5419,7 +5546,7 @@ function joinScalarDeclaredKinds(members) {
|
|
|
5419
5546
|
function tupleHasOptionalOrRestPositions(type, checker) {
|
|
5420
5547
|
if (!checker.isTupleType(type))
|
|
5421
5548
|
return false;
|
|
5422
|
-
return type.target.elementFlags.some((flags) => (flags &
|
|
5549
|
+
return type.target.elementFlags.some((flags) => (flags & ts7.ElementFlags.Required) === 0);
|
|
5423
5550
|
}
|
|
5424
5551
|
function demote(bindings, binding) {
|
|
5425
5552
|
const category = bindings[binding].category;
|
|
@@ -5433,6 +5560,7 @@ function demote(bindings, binding) {
|
|
|
5433
5560
|
break;
|
|
5434
5561
|
}
|
|
5435
5562
|
case "kind":
|
|
5563
|
+
case "function":
|
|
5436
5564
|
case "import":
|
|
5437
5565
|
case "opaque":
|
|
5438
5566
|
break;
|
|
@@ -5440,31 +5568,31 @@ function demote(bindings, binding) {
|
|
|
5440
5568
|
}
|
|
5441
5569
|
|
|
5442
5570
|
// src/lower/static-intrinsics.ts
|
|
5443
|
-
import * as
|
|
5444
|
-
function scanStaticAnnotations(sourceFile,
|
|
5445
|
-
const ownerIndex = new Map(
|
|
5446
|
-
const callsByFunction =
|
|
5571
|
+
import * as ts8 from "typescript";
|
|
5572
|
+
function scanStaticAnnotations(sourceFile, functions, checker) {
|
|
5573
|
+
const ownerIndex = new Map(functions.map((unit, index) => [unit.declaration, index]));
|
|
5574
|
+
const callsByFunction = functions.map(() => []);
|
|
5447
5575
|
const outsideTopLevelFunctions = [];
|
|
5448
5576
|
const visit = (node) => {
|
|
5449
|
-
if (
|
|
5450
|
-
const owner =
|
|
5451
|
-
const index = owner
|
|
5577
|
+
if (ts8.isCallExpression(node) && isStaticIntent(node, checker)) {
|
|
5578
|
+
const owner = ts8.findAncestor(node, ts8.isFunctionLike);
|
|
5579
|
+
const index = owner == null ? undefined : ownerIndex.get(owner);
|
|
5452
5580
|
if (index == null)
|
|
5453
5581
|
outsideTopLevelFunctions.push(node);
|
|
5454
5582
|
else
|
|
5455
5583
|
callsByFunction[index].push(node);
|
|
5456
5584
|
}
|
|
5457
|
-
|
|
5585
|
+
ts8.forEachChild(node, visit);
|
|
5458
5586
|
};
|
|
5459
5587
|
visit(sourceFile);
|
|
5460
5588
|
return {
|
|
5461
|
-
functions:
|
|
5589
|
+
functions: functions.map((unit, index) => {
|
|
5462
5590
|
const calls = callsByFunction[index];
|
|
5463
5591
|
const callSet = new Set(calls);
|
|
5464
5592
|
const leading = new Set;
|
|
5465
|
-
if (declaration.body != null) {
|
|
5466
|
-
for (const statement of declaration.body.statements) {
|
|
5467
|
-
if (!
|
|
5593
|
+
if (unit.declaration.body != null && ts8.isBlock(unit.declaration.body)) {
|
|
5594
|
+
for (const statement of unit.declaration.body.statements) {
|
|
5595
|
+
if (!ts8.isExpressionStatement(statement) || !ts8.isCallExpression(statement.expression) || !callSet.has(statement.expression))
|
|
5468
5596
|
break;
|
|
5469
5597
|
leading.add(statement.expression);
|
|
5470
5598
|
}
|
|
@@ -5478,34 +5606,30 @@ function annotationForCall(call, role) {
|
|
|
5478
5606
|
if (call.arguments.length !== 1) {
|
|
5479
5607
|
return { kind: "invalid", call, role, node: call, problem: "argumentCount" };
|
|
5480
5608
|
}
|
|
5481
|
-
if (!
|
|
5609
|
+
if (!ts8.isExpressionStatement(call.parent) || call.parent.expression !== call) {
|
|
5482
5610
|
return { kind: "invalid", call, role, node: call, problem: "position" };
|
|
5483
5611
|
}
|
|
5484
5612
|
const callee = call.expression;
|
|
5485
|
-
if (call.questionDotToken != null ||
|
|
5613
|
+
if (call.questionDotToken != null || ts8.isPropertyAccessExpression(callee) && callee.questionDotToken != null) {
|
|
5486
5614
|
return { kind: "invalid", call, role, node: call, problem: "optionalCall" };
|
|
5487
5615
|
}
|
|
5488
5616
|
return { kind: "valid", call, role, condition: call.arguments[0] };
|
|
5489
5617
|
}
|
|
5490
5618
|
function isStaticIntent(call, checker) {
|
|
5491
|
-
if (!
|
|
5619
|
+
if (!ts8.isPropertyAccessExpression(call.expression))
|
|
5492
5620
|
return false;
|
|
5493
5621
|
const access = call.expression;
|
|
5494
|
-
if (!
|
|
5622
|
+
if (!ts8.isIdentifier(access.expression) || access.expression.text !== "console" || access.name.text !== "assert")
|
|
5495
5623
|
return false;
|
|
5496
5624
|
const resolved = checker.getSymbolAtLocation(access.expression);
|
|
5497
|
-
const globalConsole = checker.resolveName("console", undefined,
|
|
5625
|
+
const globalConsole = checker.resolveName("console", undefined, ts8.SymbolFlags.Value, false);
|
|
5498
5626
|
return resolved != null && resolved === globalConsole;
|
|
5499
5627
|
}
|
|
5500
5628
|
|
|
5501
5629
|
// src/lower/program.ts
|
|
5502
5630
|
function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
5503
5631
|
const { sourceFile, checker } = checked;
|
|
5504
|
-
const declarations =
|
|
5505
|
-
for (const statement of sourceFile.statements) {
|
|
5506
|
-
if (ts8.isFunctionDeclaration(statement) && statement.name != null)
|
|
5507
|
-
declarations.push(statement);
|
|
5508
|
-
}
|
|
5632
|
+
const declarations = topLevelFunctionUnits(sourceFile);
|
|
5509
5633
|
const staticScan = scanStaticAnnotations(sourceFile, declarations, checker);
|
|
5510
5634
|
const recordStaticAnnotationIssues = (sites2) => staticScan.outsideTopLevelFunctions.map((call) => {
|
|
5511
5635
|
sites2.push(nodeSpan(sourceFile, call));
|
|
@@ -5519,9 +5643,9 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
|
5519
5643
|
baseDirectory,
|
|
5520
5644
|
lineStarts: [...sourceFile.getLineStarts()],
|
|
5521
5645
|
sites: sites2,
|
|
5522
|
-
functions: declarations.map((
|
|
5646
|
+
functions: declarations.map((unit, index) => ({
|
|
5523
5647
|
kind: "unsupported",
|
|
5524
|
-
name:
|
|
5648
|
+
name: unit.name.text,
|
|
5525
5649
|
hasStaticAnnotations: staticScan.functions[index].length > 0,
|
|
5526
5650
|
site: 0,
|
|
5527
5651
|
reason
|
|
@@ -5548,19 +5672,31 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
|
5548
5672
|
return rejectFile(nodeSpan(sourceFile, evalNode), { kind: "evalInFile" });
|
|
5549
5673
|
}
|
|
5550
5674
|
const functionsBySymbol = new Map;
|
|
5675
|
+
const scan = scanModuleBindings(sourceFile, checker);
|
|
5676
|
+
const topLevelFunctions = [];
|
|
5551
5677
|
for (let index = 0;index < declarations.length; index++) {
|
|
5552
|
-
const
|
|
5553
|
-
const symbol = checker.getSymbolAtLocation(
|
|
5678
|
+
const unit = declarations[index];
|
|
5679
|
+
const symbol = checker.getSymbolAtLocation(unit.name);
|
|
5554
5680
|
if (symbol == null)
|
|
5555
|
-
throw new Error(`Function
|
|
5556
|
-
|
|
5681
|
+
throw new Error(`Function ${unit.name.text} has no TypeScript symbol`);
|
|
5682
|
+
const binding = scan.bindingsBySymbol.get(symbol);
|
|
5683
|
+
if (unit.initializer != null && binding == null) {
|
|
5684
|
+
throw new Error(`Const-bound function ${unit.name.text} has no module binding`);
|
|
5685
|
+
}
|
|
5686
|
+
const fn = {
|
|
5687
|
+
...unit,
|
|
5688
|
+
id: index,
|
|
5689
|
+
binding: unit.initializer == null ? null : binding,
|
|
5690
|
+
signature: callableSignature(unit, checker)
|
|
5691
|
+
};
|
|
5692
|
+
topLevelFunctions.push(fn);
|
|
5693
|
+
functionsBySymbol.set(symbol, fn);
|
|
5557
5694
|
}
|
|
5558
|
-
const scan = scanModuleBindings(sourceFile, checker);
|
|
5559
5695
|
const sites = [];
|
|
5560
5696
|
const staticAnnotationIssues = recordStaticAnnotationIssues(sites);
|
|
5561
5697
|
const functions = [];
|
|
5562
|
-
for (let index = 0;index <
|
|
5563
|
-
const declaration =
|
|
5698
|
+
for (let index = 0;index < topLevelFunctions.length; index++) {
|
|
5699
|
+
const declaration = topLevelFunctions[index];
|
|
5564
5700
|
const staticAnnotations = staticScan.functions[index];
|
|
5565
5701
|
try {
|
|
5566
5702
|
functions.push(lowerFunction(declaration, staticAnnotations, sourceFile, checker, functionsBySymbol, scan, sites));
|
|
@@ -5590,7 +5726,8 @@ function lowerSource(checked, baseDirectory = process.cwd()) {
|
|
|
5590
5726
|
initializerSkips: skips
|
|
5591
5727
|
};
|
|
5592
5728
|
}
|
|
5593
|
-
function lowerFunction(
|
|
5729
|
+
function lowerFunction(unit, staticAnnotations, sourceFile, checker, functionsBySymbol, scan, sites) {
|
|
5730
|
+
const { declaration } = unit;
|
|
5594
5731
|
for (const annotation of staticAnnotations) {
|
|
5595
5732
|
if (annotation.kind === "invalid") {
|
|
5596
5733
|
throw unsupported(annotation.node, { kind: "staticAssertionForm", problem: annotation.problem });
|
|
@@ -5598,24 +5735,31 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5598
5735
|
}
|
|
5599
5736
|
if (declaration.body == null)
|
|
5600
5737
|
throw unsupported(declaration, { kind: "functionWithoutBody" });
|
|
5601
|
-
if (declaration.asteriskToken != null || declaration.modifiers?.some((modifier) => modifier.kind ===
|
|
5738
|
+
if (!ts9.isArrowFunction(declaration) && declaration.asteriskToken != null || declaration.modifiers?.some((modifier) => modifier.kind === ts9.SyntaxKind.AsyncKeyword) === true) {
|
|
5602
5739
|
throw unsupported(declaration, { kind: "asyncOrGeneratorFunction" });
|
|
5603
5740
|
}
|
|
5604
5741
|
assertAccepted(declaration);
|
|
5605
|
-
const signature =
|
|
5606
|
-
if (signature
|
|
5742
|
+
const signature = unit.signature;
|
|
5743
|
+
if (signature == null) {
|
|
5744
|
+
throw unsupported(unit.name, {
|
|
5745
|
+
kind: unit.initializer == null ? "functionWithoutSignature" : "constFunctionSignature"
|
|
5746
|
+
});
|
|
5747
|
+
}
|
|
5748
|
+
if (checker.getTypePredicateOfSignature(signature) != null) {
|
|
5607
5749
|
throw unsupported(declaration, { kind: "typePredicate" });
|
|
5608
5750
|
}
|
|
5609
|
-
const returnType =
|
|
5610
|
-
const returnsVoid = (returnType.flags & (
|
|
5751
|
+
const returnType = checker.getReturnTypeOfSignature(signature);
|
|
5752
|
+
const returnsVoid = (returnType.flags & (ts9.TypeFlags.Void | ts9.TypeFlags.Undefined | ts9.TypeFlags.Never)) !== 0;
|
|
5611
5753
|
if (!returnsVoid && valueKind(returnType, checker) == null) {
|
|
5612
5754
|
throw unsupported(declaration.type ?? declaration, { kind: "valueType", typeText: checker.typeToString(returnType) });
|
|
5613
5755
|
}
|
|
5614
|
-
const context = createFunctionContext(sourceFile, checker, functionsBySymbol, scan.bindingsBySymbol, sites, staticAnnotations);
|
|
5756
|
+
const context = createFunctionContext(sourceFile, checker, functionsBySymbol, scan.bindingsBySymbol, sites, staticAnnotations, returnsVoid);
|
|
5615
5757
|
const entry = context.currentBlock;
|
|
5616
|
-
for (
|
|
5617
|
-
|
|
5618
|
-
|
|
5758
|
+
for (let parameterIndex = 0;parameterIndex < declaration.parameters.length; parameterIndex++) {
|
|
5759
|
+
const parameter = declaration.parameters[parameterIndex];
|
|
5760
|
+
const parameterType = unit.initializer == null ? checker.getTypeAtLocation(parameter) : checker.getTypeOfSymbolAtLocation(signature.parameters[parameterIndex], signature.declaration);
|
|
5761
|
+
if (ts9.isObjectBindingPattern(parameter.name)) {
|
|
5762
|
+
const type2 = lowerParameterType(parameter, parameterType, checker);
|
|
5619
5763
|
const patternName = parameter.name.getText(sourceFile).replace(/\s+/g, " ");
|
|
5620
5764
|
if (parameter.initializer != null) {
|
|
5621
5765
|
throw unsupported(parameter, { kind: "parameterDefaultValue", name: patternName });
|
|
@@ -5624,10 +5768,10 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5624
5768
|
const bindings = [];
|
|
5625
5769
|
context.parameters.push({ value: value3, name: patternName, type: type2, site: addSite(context, parameter), bindings });
|
|
5626
5770
|
for (const element of parameter.name.elements) {
|
|
5627
|
-
if (!
|
|
5771
|
+
if (!ts9.isIdentifier(element.name) || element.dotDotDotToken != null || element.initializer != null) {
|
|
5628
5772
|
throw unsupported(element, { kind: "destructuredParameter" });
|
|
5629
5773
|
}
|
|
5630
|
-
const property = element.propertyName == null ? element.name.text :
|
|
5774
|
+
const property = element.propertyName == null ? element.name.text : ts9.isIdentifier(element.propertyName) ? element.propertyName.text : null;
|
|
5631
5775
|
if (property == null)
|
|
5632
5776
|
throw unsupported(element, { kind: "destructuredParameter" });
|
|
5633
5777
|
bindings.push({ property, local: element.name.text });
|
|
@@ -5643,12 +5787,12 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5643
5787
|
}
|
|
5644
5788
|
continue;
|
|
5645
5789
|
}
|
|
5646
|
-
if (!
|
|
5790
|
+
if (!ts9.isIdentifier(parameter.name))
|
|
5647
5791
|
throw unsupported(parameter.name, { kind: "destructuredParameter" });
|
|
5648
5792
|
if (parameter.dotDotDotToken != null) {
|
|
5649
5793
|
throw unsupported(parameter, { kind: "parameterType", typeText: `...${checker.typeToString(checker.getTypeAtLocation(parameter))}`, optionalOrRestTuple: false });
|
|
5650
5794
|
}
|
|
5651
|
-
let type = lowerParameterType(parameter, checker);
|
|
5795
|
+
let type = lowerParameterType(parameter, parameterType, checker);
|
|
5652
5796
|
if (parameter.initializer != null) {
|
|
5653
5797
|
const default_ = parameterDefaultLiteral(parameter.initializer, checker);
|
|
5654
5798
|
if (default_ == null || !parameterDefaultFits(default_, type)) {
|
|
@@ -5661,7 +5805,16 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5661
5805
|
context.parameters.push({ value: value2, name: parameter.name.text, type, site: addSite(context, parameter), bindings: null });
|
|
5662
5806
|
}
|
|
5663
5807
|
lowerFiniteInputRequirements(context);
|
|
5664
|
-
|
|
5808
|
+
if (ts9.isBlock(declaration.body)) {
|
|
5809
|
+
lowerStatements(declaration.body.statements, context);
|
|
5810
|
+
} else {
|
|
5811
|
+
const value2 = lowerExpression(declaration.body, context);
|
|
5812
|
+
terminate(context.currentBlock, {
|
|
5813
|
+
kind: "return",
|
|
5814
|
+
value: returnsVoid ? null : value2,
|
|
5815
|
+
site: addSite(context, declaration.body)
|
|
5816
|
+
});
|
|
5817
|
+
}
|
|
5665
5818
|
if (context.currentBlock.terminator == null) {
|
|
5666
5819
|
if (!returnsVoid) {
|
|
5667
5820
|
terminate(context.currentBlock, { kind: "stop", site: addSite(context, declaration), reason: { kind: "missingReturn" } });
|
|
@@ -5671,12 +5824,12 @@ function lowerFunction(declaration, staticAnnotations, sourceFile, checker, func
|
|
|
5671
5824
|
}
|
|
5672
5825
|
return {
|
|
5673
5826
|
kind: "lowered",
|
|
5674
|
-
name:
|
|
5827
|
+
name: unit.name.text,
|
|
5675
5828
|
assertions: context.assertions,
|
|
5676
5829
|
parameters: context.parameters,
|
|
5677
5830
|
returnPropertyNames: declaredRecordReturnNames(returnType, checker),
|
|
5678
5831
|
entry: 0,
|
|
5679
|
-
blocks: sealBlocks(context.blocks,
|
|
5832
|
+
blocks: sealBlocks(context.blocks, unit.name.text)
|
|
5680
5833
|
};
|
|
5681
5834
|
}
|
|
5682
5835
|
function lowerFiniteInputRequirements(context) {
|
|
@@ -5710,7 +5863,7 @@ function declaredRecordReturnNames(returnType, checker) {
|
|
|
5710
5863
|
if (kind === "object")
|
|
5711
5864
|
return checker.getPropertiesOfType(returnType).map((property) => property.name);
|
|
5712
5865
|
if (kind === "nullable" && returnType.isUnion()) {
|
|
5713
|
-
const missing =
|
|
5866
|
+
const missing = ts9.TypeFlags.Null | ts9.TypeFlags.Undefined;
|
|
5714
5867
|
const members = returnType.types.filter((member) => (member.flags & missing) === 0);
|
|
5715
5868
|
if (members.length > 0 && members.every((member) => valueKind(member, checker) === "object")) {
|
|
5716
5869
|
const names = new Set;
|
|
@@ -5723,8 +5876,7 @@ function declaredRecordReturnNames(returnType, checker) {
|
|
|
5723
5876
|
}
|
|
5724
5877
|
return null;
|
|
5725
5878
|
}
|
|
5726
|
-
function lowerParameterType(parameter, checker) {
|
|
5727
|
-
const type = checker.getTypeAtLocation(parameter);
|
|
5879
|
+
function lowerParameterType(parameter, type, checker) {
|
|
5728
5880
|
const declared = declaredKind(type, checker, []);
|
|
5729
5881
|
if (declared == null) {
|
|
5730
5882
|
throw unsupported(parameter, {
|
|
@@ -5735,12 +5887,6 @@ function lowerParameterType(parameter, checker) {
|
|
|
5735
5887
|
}
|
|
5736
5888
|
return declared;
|
|
5737
5889
|
}
|
|
5738
|
-
function functionReturnType(declaration, checker) {
|
|
5739
|
-
const signature = checker.getSignatureFromDeclaration(declaration);
|
|
5740
|
-
if (signature == null)
|
|
5741
|
-
throw unsupported(declaration, { kind: "functionWithoutSignature" });
|
|
5742
|
-
return checker.getReturnTypeOfSignature(signature);
|
|
5743
|
-
}
|
|
5744
5890
|
|
|
5745
5891
|
// src/analyze.ts
|
|
5746
5892
|
function analyzeCheckedSource(checked, baseDirectory) {
|
|
@@ -6505,6 +6651,7 @@ function formatStop(stop, program, analysis) {
|
|
|
6505
6651
|
return `reads ${binding.name}, whose value the analysis does not track (read at ${formatSite(program, stop.site)})`;
|
|
6506
6652
|
case "value":
|
|
6507
6653
|
case "kind":
|
|
6654
|
+
case "function":
|
|
6508
6655
|
return `reads ${binding.name} before it is initialized (read at ${formatSite(program, stop.site)})`;
|
|
6509
6656
|
}
|
|
6510
6657
|
}
|
|
@@ -6563,6 +6710,8 @@ function formatUnsupportedReason(reason) {
|
|
|
6563
6710
|
return "node without a TypeScript symbol";
|
|
6564
6711
|
case "functionWithoutSignature":
|
|
6565
6712
|
return "function without a TypeScript signature";
|
|
6713
|
+
case "constFunctionSignature":
|
|
6714
|
+
return "a top-level const function whose declared call shape differs from its implementation";
|
|
6566
6715
|
case "functionWithoutBody":
|
|
6567
6716
|
return "function declarations need bodies";
|
|
6568
6717
|
case "destructuredParameter":
|
|
@@ -6788,7 +6937,7 @@ function formatNumber(value2) {
|
|
|
6788
6937
|
}
|
|
6789
6938
|
|
|
6790
6939
|
// src/typescript/diagnostics.ts
|
|
6791
|
-
import * as
|
|
6940
|
+
import * as ts10 from "typescript";
|
|
6792
6941
|
|
|
6793
6942
|
class TypeScriptDiagnosticsError extends Error {
|
|
6794
6943
|
diagnostics;
|
|
@@ -6805,10 +6954,10 @@ class TypeScriptDiagnosticsError extends Error {
|
|
|
6805
6954
|
function formatTypeScriptDiagnostics(diagnostics, options, currentDirectory) {
|
|
6806
6955
|
const host = {
|
|
6807
6956
|
getCurrentDirectory: () => currentDirectory,
|
|
6808
|
-
getCanonicalFileName:
|
|
6809
|
-
getNewLine: () =>
|
|
6957
|
+
getCanonicalFileName: ts10.sys.useCaseSensitiveFileNames ? (file) => file : (file) => file.toLowerCase(),
|
|
6958
|
+
getNewLine: () => ts10.sys.newLine
|
|
6810
6959
|
};
|
|
6811
|
-
return usePrettyOutput(options["pretty"]) ?
|
|
6960
|
+
return usePrettyOutput(options["pretty"]) ? ts10.formatDiagnosticsWithColorAndContext(diagnostics, host) : ts10.formatDiagnostics(diagnostics, host);
|
|
6812
6961
|
}
|
|
6813
6962
|
function usePrettyOutput(configured) {
|
|
6814
6963
|
if (typeof configured === "boolean")
|
|
@@ -6819,7 +6968,7 @@ function usePrettyOutput(configured) {
|
|
|
6819
6968
|
const forceColor = process.env["FORCE_COLOR"];
|
|
6820
6969
|
if (forceColor != null && forceColor !== "")
|
|
6821
6970
|
return true;
|
|
6822
|
-
return
|
|
6971
|
+
return ts10.sys.writeOutputIsTTY?.() === true;
|
|
6823
6972
|
}
|
|
6824
6973
|
function color(code, text) {
|
|
6825
6974
|
return `\x1B[${code}m${text}\x1B[0m`;
|
|
@@ -7046,7 +7195,7 @@ function createFileAudit({ program, analysis }) {
|
|
|
7046
7195
|
};
|
|
7047
7196
|
}
|
|
7048
7197
|
function formatAuditCoverage(coverage) {
|
|
7049
|
-
const parts = coverage.functions === 0 ? ["no named
|
|
7198
|
+
const parts = coverage.functions === 0 ? ["no named top-level functions"] : [`${coverage.analyzed}/${coverage.functions} functions fully analyzed`];
|
|
7050
7199
|
if (coverage.partial > 0)
|
|
7051
7200
|
parts.push(`${coverage.partial} partially supported`);
|
|
7052
7201
|
if (coverage.unsupported > 0)
|
|
@@ -7141,6 +7290,7 @@ function guidesForUnsupportedReason(reason) {
|
|
|
7141
7290
|
case "unknownIdentifier":
|
|
7142
7291
|
case "missingSymbol":
|
|
7143
7292
|
case "functionWithoutSignature":
|
|
7293
|
+
case "constFunctionSignature":
|
|
7144
7294
|
case "functionWithoutBody":
|
|
7145
7295
|
case "destructuredParameter":
|
|
7146
7296
|
case "parameterType":
|
|
@@ -7211,12 +7361,12 @@ function isCallerInput(expression) {
|
|
|
7211
7361
|
|
|
7212
7362
|
// src/typescript/check.ts
|
|
7213
7363
|
import { resolve } from "node:path";
|
|
7214
|
-
import * as
|
|
7364
|
+
import * as ts11 from "typescript";
|
|
7215
7365
|
var fallbackOptions = {
|
|
7216
|
-
target:
|
|
7217
|
-
module:
|
|
7218
|
-
moduleResolution:
|
|
7219
|
-
moduleDetection:
|
|
7366
|
+
target: ts11.ScriptTarget.ESNext,
|
|
7367
|
+
module: ts11.ModuleKind.ESNext,
|
|
7368
|
+
moduleResolution: ts11.ModuleResolutionKind.Bundler,
|
|
7369
|
+
moduleDetection: ts11.ModuleDetectionKind.Force,
|
|
7220
7370
|
strict: true,
|
|
7221
7371
|
noUncheckedIndexedAccess: true,
|
|
7222
7372
|
exactOptionalPropertyTypes: true,
|
|
@@ -7226,11 +7376,11 @@ var fallbackOptions = {
|
|
|
7226
7376
|
};
|
|
7227
7377
|
function checkFile(file) {
|
|
7228
7378
|
const absoluteFile = resolve(file);
|
|
7229
|
-
const program =
|
|
7379
|
+
const program = ts11.createProgram([absoluteFile], fallbackOptions);
|
|
7230
7380
|
return checkedSource(program, absoluteFile, fallbackOptions);
|
|
7231
7381
|
}
|
|
7232
7382
|
function checkedSource(program, file, options) {
|
|
7233
|
-
const diagnostics =
|
|
7383
|
+
const diagnostics = ts11.getPreEmitDiagnostics(program);
|
|
7234
7384
|
if (diagnostics.length > 0) {
|
|
7235
7385
|
throw new TypeScriptDiagnosticsError(diagnostics, options, process.cwd());
|
|
7236
7386
|
}
|
|
@@ -7242,9 +7392,9 @@ function checkedSource(program, file, options) {
|
|
|
7242
7392
|
|
|
7243
7393
|
// src/typescript/project.ts
|
|
7244
7394
|
import { dirname, isAbsolute, relative as relative2, resolve as resolve2, sep } from "node:path";
|
|
7245
|
-
import * as
|
|
7395
|
+
import * as ts12 from "typescript";
|
|
7246
7396
|
function findTypeScriptConfig(searchFrom) {
|
|
7247
|
-
return
|
|
7397
|
+
return ts12.findConfigFile(resolve2(searchFrom), (file) => ts12.sys.fileExists(file), "tsconfig.json") ?? null;
|
|
7248
7398
|
}
|
|
7249
7399
|
function loadTypeScriptProjectGraph(configPath) {
|
|
7250
7400
|
const loaded = [];
|
|
@@ -7261,8 +7411,8 @@ function loadTypeScriptProjectGraph(configPath) {
|
|
|
7261
7411
|
const parsed = parseConfig(absoluteConfigPath);
|
|
7262
7412
|
requireStrictNullChecks(parsed.options, absoluteConfigPath);
|
|
7263
7413
|
for (const reference of parsed.projectReferences ?? [])
|
|
7264
|
-
load(
|
|
7265
|
-
const program =
|
|
7414
|
+
load(ts12.resolveProjectReferencePath(reference));
|
|
7415
|
+
const program = ts12.createProgram({
|
|
7266
7416
|
rootNames: parsed.fileNames,
|
|
7267
7417
|
options: parsed.options,
|
|
7268
7418
|
configFileParsingDiagnostics: parsed.errors,
|
|
@@ -7297,8 +7447,8 @@ function projectSources(projects) {
|
|
|
7297
7447
|
return [...sources.values()].sort((left, right) => left.sourceFile.fileName.localeCompare(right.sourceFile.fileName));
|
|
7298
7448
|
}
|
|
7299
7449
|
function parseConfig(configPath) {
|
|
7300
|
-
const parsed =
|
|
7301
|
-
...
|
|
7450
|
+
const parsed = ts12.getParsedCommandLineOfConfigFile(configPath, undefined, {
|
|
7451
|
+
...ts12.sys,
|
|
7302
7452
|
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
|
|
7303
7453
|
throw new TypeScriptDiagnosticsError([diagnostic], {}, dirname(configPath));
|
|
7304
7454
|
}
|
|
@@ -7359,7 +7509,7 @@ function analyzeProject(searchFrom) {
|
|
|
7359
7509
|
const projects = loadTypeScriptProjectGraph(configPath);
|
|
7360
7510
|
const rootProject = projects.at(-1);
|
|
7361
7511
|
const sources = projectSources(projects);
|
|
7362
|
-
const diagnostics = uniqueDiagnostics(projects.flatMap((project) =>
|
|
7512
|
+
const diagnostics = uniqueDiagnostics(projects.flatMap((project) => ts13.getPreEmitDiagnostics(project.program)));
|
|
7363
7513
|
requireNoTypeScriptErrors(diagnostics, rootProject.parsed.options);
|
|
7364
7514
|
const files = [];
|
|
7365
7515
|
let analyzed = 0;
|
|
@@ -7483,7 +7633,7 @@ function collectLintFindings({ program, analysis }) {
|
|
|
7483
7633
|
collectStops(analysis.initializer);
|
|
7484
7634
|
collectAssertions(analysis.initializer);
|
|
7485
7635
|
for (const issue of program.staticAnnotationIssues) {
|
|
7486
|
-
addError(issue.site, "console-assert", "console.assert is only supported inside a named top-level function
|
|
7636
|
+
addError(issue.site, "console-assert", "console.assert is only supported inside a named top-level function");
|
|
7487
7637
|
}
|
|
7488
7638
|
for (const fn of analysis.functions) {
|
|
7489
7639
|
collectStops(fn);
|
|
@@ -7580,7 +7730,7 @@ function formatLintPrefix(finding, rule, pretty) {
|
|
|
7580
7730
|
return formatDiagnosticPrefix(finding, lintLevel(finding), rule, pretty);
|
|
7581
7731
|
}
|
|
7582
7732
|
function formatCoverage(coverage) {
|
|
7583
|
-
return `coverage: ${coverage.analyzed}/${coverage.functions} named top-level
|
|
7733
|
+
return `coverage: ${coverage.analyzed}/${coverage.functions} named top-level functions fully analyzed; ${coverage.partial} partially supported; ${coverage.unsupported} unsupported.`;
|
|
7584
7734
|
}
|
|
7585
7735
|
function analyzeTargetFile(file) {
|
|
7586
7736
|
const absoluteFile = resolve3(file);
|
|
@@ -7596,7 +7746,7 @@ function analyzeTargetFile(file) {
|
|
|
7596
7746
|
if (source == null) {
|
|
7597
7747
|
throw new Error(`File is not part of the project resolved from ${configPath}: ${absoluteFile}`);
|
|
7598
7748
|
}
|
|
7599
|
-
const diagnostics =
|
|
7749
|
+
const diagnostics = ts13.getPreEmitDiagnostics(source.project.program, source.sourceFile);
|
|
7600
7750
|
requireNoTypeScriptErrors(diagnostics, rootProject.parsed.options);
|
|
7601
7751
|
return {
|
|
7602
7752
|
detailed: analyzeProjectSource(source, process.cwd()),
|
|
@@ -7605,7 +7755,7 @@ function analyzeTargetFile(file) {
|
|
|
7605
7755
|
}
|
|
7606
7756
|
function canonicalFilePath(file) {
|
|
7607
7757
|
const real = realpathSync.native(file);
|
|
7608
|
-
return
|
|
7758
|
+
return ts13.sys.useCaseSensitiveFileNames ? real : real.toLowerCase();
|
|
7609
7759
|
}
|
|
7610
7760
|
function analyzeFileAlone(absoluteFile) {
|
|
7611
7761
|
return {
|
|
@@ -7622,7 +7772,7 @@ function analyzeProjectSource(source, reportBaseDirectory) {
|
|
|
7622
7772
|
function uniqueDiagnostics(diagnostics) {
|
|
7623
7773
|
const seen = new Set;
|
|
7624
7774
|
return diagnostics.filter((diagnostic) => {
|
|
7625
|
-
const message =
|
|
7775
|
+
const message = ts13.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
7626
7776
|
`);
|
|
7627
7777
|
const key = `${diagnostic.file?.fileName ?? ""}:${diagnostic.start ?? ""}:${diagnostic.length ?? ""}:${diagnostic.code}:${message}`;
|
|
7628
7778
|
if (seen.has(key))
|
|
@@ -7643,7 +7793,7 @@ function requireNoTypeScriptErrors(diagnostics, options) {
|
|
|
7643
7793
|
printTypeScriptDiagnostics(diagnostics, options, process.cwd());
|
|
7644
7794
|
}
|
|
7645
7795
|
function hasErrorDiagnostics(diagnostics) {
|
|
7646
|
-
return diagnostics.some((diagnostic) => diagnostic.category ===
|
|
7796
|
+
return diagnostics.some((diagnostic) => diagnostic.category === ts13.DiagnosticCategory.Error);
|
|
7647
7797
|
}
|
|
7648
7798
|
|
|
7649
7799
|
// fr.ts
|