ucn 5.6.0 → 5.6.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.
- package/.claude/skills/ucn/SKILL.md +4 -2
- package/.claude/skills/ucn/references/commands.md +1 -1
- package/CHANGELOG.md +11 -0
- package/README.md +5 -8
- package/core/analysis.js +3 -3
- package/core/cache.js +3 -1
- package/core/call-fit.js +130 -0
- package/core/callers.js +1 -1
- package/core/capture.js +40 -8
- package/core/change-kinds.js +221 -2
- package/core/check.js +48 -15
- package/core/command-contracts.js +2 -2
- package/core/deadcode.js +94 -23
- package/core/deletion.js +29 -9
- package/core/entrypoint-toml.js +110 -0
- package/core/index-ir.js +2 -2
- package/core/ir.js +1 -1
- package/core/outgoing-check.js +116 -0
- package/core/output/check.js +5 -2
- package/core/output/reporting.js +4 -0
- package/core/output/search.js +3 -0
- package/core/output/tracing.js +1 -1
- package/core/output-budget.js +29 -11
- package/core/python-binding-identity.js +707 -0
- package/core/reflection.js +99 -10
- package/core/registry.js +2 -2
- package/core/runtime-entrypoints.js +159 -0
- package/core/search.js +61 -38
- package/core/test-units.js +116 -42
- package/core/tracing.js +193 -635
- package/core/verify.js +37 -12
- package/languages/csharp.js +4 -4
- package/languages/go.js +1 -1
- package/languages/index.js +45 -1
- package/languages/java.js +4 -4
- package/languages/javascript.js +40 -6
- package/languages/python.js +49 -15
- package/languages/rust.js +126 -112
- package/languages/static-js.js +88 -0
- package/mcp/server.js +1 -1
- package/package.json +4 -3
package/core/verify.js
CHANGED
|
@@ -1777,10 +1777,19 @@ function analyzeCallSite(index, call, funcName, occurrence = 0) {
|
|
|
1777
1777
|
if (!argsNode) return { args: [], argCount: 0, isMethodCall, ...ctx };
|
|
1778
1778
|
|
|
1779
1779
|
let args = [];
|
|
1780
|
+
const argumentFacts = [];
|
|
1780
1781
|
for (let i = 0; i < argsNode.namedChildCount; i++) {
|
|
1781
1782
|
const argNode = argsNode.namedChild(i);
|
|
1782
1783
|
if (argNode.type.includes('comment')) continue;
|
|
1783
1784
|
args.push(argNode.text.trim());
|
|
1785
|
+
let value = argNode.type === 'keyword_argument' ? argNode.childForFieldName('value') : argNode;
|
|
1786
|
+
// C# wraps each ordinary value in an argument node. Named or
|
|
1787
|
+
// ref/out arguments remain outside the literal proof domain.
|
|
1788
|
+
if (value?.type === 'argument' && value.namedChildCount === 1 && value.childCount === 1) {
|
|
1789
|
+
value = value.namedChild(0);
|
|
1790
|
+
}
|
|
1791
|
+
argumentFacts.push({ kind: classifyArgNode(value), nodeType: value?.type, text: value?.text,
|
|
1792
|
+
...(argNode.type === 'keyword_argument' && { keyword: argNode.childForFieldName('name')?.text }) });
|
|
1784
1793
|
}
|
|
1785
1794
|
|
|
1786
1795
|
// Python argument structure (fix #281): keyword arguments bind by
|
|
@@ -1850,7 +1859,7 @@ function analyzeCallSite(index, call, funcName, occurrence = 0) {
|
|
|
1850
1859
|
}
|
|
1851
1860
|
}
|
|
1852
1861
|
|
|
1853
|
-
return {
|
|
1862
|
+
return Object.defineProperty({
|
|
1854
1863
|
// Where the call is in the source: the same node, whoever reads
|
|
1855
1864
|
// the file next (an alias, the second call on a line).
|
|
1856
1865
|
span: { start: callNode.startIndex, end: callNode.endIndex },
|
|
@@ -1863,7 +1872,7 @@ function analyzeCallSite(index, call, funcName, occurrence = 0) {
|
|
|
1863
1872
|
isMethodCall,
|
|
1864
1873
|
...(indirectKind && { indirectKind }),
|
|
1865
1874
|
...ctx,
|
|
1866
|
-
};
|
|
1875
|
+
}, 'argumentFacts', { value: argumentFacts });
|
|
1867
1876
|
} catch (e) {
|
|
1868
1877
|
return { args: null, argCount: 0 };
|
|
1869
1878
|
}
|
|
@@ -1967,7 +1976,7 @@ function classifyArgNode(node) {
|
|
|
1967
1976
|
}
|
|
1968
1977
|
// Numbers
|
|
1969
1978
|
if (t === 'number' || t === 'integer' || t === 'float' || t === 'number_literal' ||
|
|
1970
|
-
t === 'integer_literal' || t === 'float_literal' || t === 'decimal_integer_literal' ||
|
|
1979
|
+
t === 'integer_literal' || t === 'int_literal' || t === 'float_literal' || t === 'decimal_integer_literal' ||
|
|
1971
1980
|
t === 'hex_integer_literal' || t === 'real_literal') {
|
|
1972
1981
|
return 'number_literal';
|
|
1973
1982
|
}
|
|
@@ -2139,10 +2148,11 @@ function bindKeywordCall(params, analysis) {
|
|
|
2139
2148
|
* @param {object} options - { file }
|
|
2140
2149
|
* @returns {object} Verification results with mismatches
|
|
2141
2150
|
*/
|
|
2142
|
-
function verify(index, name, options = {}) {
|
|
2151
|
+
function verify(index, name, options = {}, direct = null) {
|
|
2143
2152
|
index._beginOp();
|
|
2144
2153
|
try {
|
|
2145
|
-
const { def, warnings } =
|
|
2154
|
+
const { def, warnings } = direct ? { def: direct.definition, warnings: [] }
|
|
2155
|
+
: index.resolveSymbol(name, { file: options.file, className: options.className, line: options.line });
|
|
2146
2156
|
if (!def) {
|
|
2147
2157
|
return { found: false, function: name };
|
|
2148
2158
|
}
|
|
@@ -2179,6 +2189,7 @@ function verify(index, name, options = {}) {
|
|
|
2179
2189
|
const rawParamLists = ctorParamLists ||
|
|
2180
2190
|
(callableIdentityParams?.length ? callableIdentityParams : null) ||
|
|
2181
2191
|
[(arrowTypes?.paramsStructured) || def.paramsStructured || []];
|
|
2192
|
+
if (direct) direct.parameterLists = rawParamLists.map(stripSelf);
|
|
2182
2193
|
const params = stripSelf(rawParamLists[0]);
|
|
2183
2194
|
const arities = rawParamLists.map(l => {
|
|
2184
2195
|
const list = stripSelf(l);
|
|
@@ -2207,10 +2218,11 @@ function verify(index, name, options = {}) {
|
|
|
2207
2218
|
// (the target-less check already resolved its base through the diff).
|
|
2208
2219
|
const repoFile = index.repoPathOf ? index.repoPathOf(def.file) : def.file;
|
|
2209
2220
|
if (options.base && options.checkBaseRef) assertGitCommit(repoFile, baseRef);
|
|
2210
|
-
const baseRead = startBaseRead(def.file, baseRef, repoFile);
|
|
2221
|
+
const baseRead = direct ? null : startBaseRead(def.file, baseRef, repoFile);
|
|
2211
2222
|
let sweep;
|
|
2212
2223
|
try {
|
|
2213
|
-
sweep =
|
|
2224
|
+
sweep = direct ? { confirmed: direct.sites, unverified: [], account: null, rebound: [], baseChanges: [] }
|
|
2225
|
+
: contractedCallerSweep(index, name, def, {
|
|
2214
2226
|
baseChanges: () => baseDeclarationChanges(index, name, def, baseRef, baseRead),
|
|
2215
2227
|
});
|
|
2216
2228
|
} finally {
|
|
@@ -2296,6 +2308,7 @@ function verify(index, name, options = {}) {
|
|
|
2296
2308
|
const occurrence = verifyLineSeen.get(siteKey) || 0;
|
|
2297
2309
|
verifyLineSeen.set(siteKey, occurrence + 1);
|
|
2298
2310
|
const analysis = analyzeCallSite(index, call, name, occurrence);
|
|
2311
|
+
if (direct) (direct.callShapes ||= []).push(analysis);
|
|
2299
2312
|
|
|
2300
2313
|
// Carry callerFile/callerStartLine so tagInTestCase can resolve the
|
|
2301
2314
|
// enclosing function in a later pass.
|
|
@@ -2356,9 +2369,10 @@ function verify(index, name, options = {}) {
|
|
|
2356
2369
|
}
|
|
2357
2370
|
|
|
2358
2371
|
// Check if arg count is valid
|
|
2359
|
-
const countOk =
|
|
2360
|
-
? argCount >=
|
|
2361
|
-
:
|
|
2372
|
+
const countOk = direct && !inheritedCtorOnly
|
|
2373
|
+
? arities.some(arity => argCount >= arity.min && (arity.hasRest || argCount <= arity.max))
|
|
2374
|
+
: hasRest ? argCount >= minArgs
|
|
2375
|
+
: (argCount >= minArgs && argCount <= expectedParamCount);
|
|
2362
2376
|
if (call.rebind && call.rebind.kind !== 'no-overload' && (countOk || call.rebind.kind !== 'count')) {
|
|
2363
2377
|
// Bound the base declaration; the new one may still take it, or
|
|
2364
2378
|
// another overload may: the compiler decides (fix #394).
|
|
@@ -2441,7 +2455,7 @@ function verify(index, name, options = {}) {
|
|
|
2441
2455
|
...carry,
|
|
2442
2456
|
});
|
|
2443
2457
|
}
|
|
2444
|
-
clearTreeCache(index);
|
|
2458
|
+
if (!direct) clearTreeCache(index);
|
|
2445
2459
|
|
|
2446
2460
|
// Feature A: tag each entry with `inTestCase` based on its enclosing function.
|
|
2447
2461
|
// Done after the per-call loop because tagInTestCase prefers a single pass
|
|
@@ -6961,4 +6975,15 @@ function _methodGroupPass(index, name, renameTo, renamedDefs, changes, usages) {
|
|
|
6961
6975
|
|
|
6962
6976
|
const IDENTITY_TYPE_KINDS_FOR_CREF = new Set(['class', 'struct', 'interface', 'record', 'enum', 'trait', 'type']);
|
|
6963
6977
|
|
|
6964
|
-
|
|
6978
|
+
/** Check already-resolved call sites using exactly the signature rules of verify. */
|
|
6979
|
+
function verifyCallSites(index, definition, sites) {
|
|
6980
|
+
const direct = { definition, sites };
|
|
6981
|
+
const result = verify(index, definition.name, {}, direct);
|
|
6982
|
+
Object.defineProperties(result, {
|
|
6983
|
+
parameterLists: { value: direct.parameterLists },
|
|
6984
|
+
callShapes: { value: direct.callShapes },
|
|
6985
|
+
});
|
|
6986
|
+
return result;
|
|
6987
|
+
}
|
|
6988
|
+
|
|
6989
|
+
module.exports = { verify, verifyCallSites, plan, baseCounterpartOf, sitesReboundByChange, computePlanCallSites, analyzeCallSite, analyzeCallSiteAST, analyzeCallShape, classifyArgNode, findCallNode, clearTreeCache, identifyCallPatterns };
|
package/languages/csharp.js
CHANGED
|
@@ -3473,7 +3473,7 @@ function testUnits(fileEntry, helpers) {
|
|
|
3473
3473
|
const generated = chain.some(fixtureRows);
|
|
3474
3474
|
const row = {
|
|
3475
3475
|
name: symbol.name, line: symbol.nameLine || symbol.startLine,
|
|
3476
|
-
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine,
|
|
3476
|
+
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
|
|
3477
3477
|
...(states.includes('skip') && { skipped: true }),
|
|
3478
3478
|
...(!states.includes('skip') && sometimes && { conditional: true }),
|
|
3479
3479
|
// One declaration the runner reports as several tests: a
|
|
@@ -3539,12 +3539,12 @@ function testUnits(fileEntry, helpers) {
|
|
|
3539
3539
|
...([...chain, cls].some(each => stateOf(each, null) === 'sometimes') && { sometimes: true }),
|
|
3540
3540
|
...([...chain, cls].some(guarded) && { guard: true }),
|
|
3541
3541
|
...(cls.namespace && { namespace: cls.namespace }),
|
|
3542
|
-
line: cls.nameLine || cls.startLine, startLine: cls.startLine, endLine: cls.endLine ?? cls.startLine,
|
|
3542
|
+
line: cls.nameLine || cls.startLine, startLine: cls.startLine, endLine: cls.endLine ?? cls.startLine, sourceSpan: cls.sourceSpan,
|
|
3543
3543
|
members: methodsOf(cls).map(symbol => symbol.name),
|
|
3544
3544
|
// Where each method's body stands, for a test it gives the body to.
|
|
3545
3545
|
memberRows: methodsOf(cls).map(symbol => ({
|
|
3546
3546
|
name: symbol.name, ...signatureOf(symbol), line: symbol.nameLine || symbol.startLine,
|
|
3547
|
-
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine,
|
|
3547
|
+
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
|
|
3548
3548
|
})),
|
|
3549
3549
|
...([...chain, cls].some(fixtureRows) && { several: true, dynamic: 'generated-cases' }),
|
|
3550
3550
|
...(attributeNames(cls).includes('TestClass') && { marked: true }),
|
|
@@ -3554,7 +3554,7 @@ function testUnits(fileEntry, helpers) {
|
|
|
3554
3554
|
units.push({
|
|
3555
3555
|
kind: 'suite', name: cls.name, suite: chain.map(outer => outer.name),
|
|
3556
3556
|
line: cls.nameLine || cls.startLine, startLine: cls.startLine,
|
|
3557
|
-
endLine: cls.endLine ?? cls.startLine,
|
|
3557
|
+
endLine: cls.endLine ?? cls.startLine, sourceSpan: cls.sourceSpan,
|
|
3558
3558
|
});
|
|
3559
3559
|
}
|
|
3560
3560
|
}
|
package/languages/go.js
CHANGED
|
@@ -3952,7 +3952,7 @@ function testUnits(fileEntry, helpers) {
|
|
|
3952
3952
|
// A file under a build constraint is compiled only sometimes.
|
|
3953
3953
|
...((fileEntry.buildConstraint || platform) && { conditional: true, notInEveryBuild: true }),
|
|
3954
3954
|
line: symbol.nameLine || symbol.startLine, startLine: symbol.startLine,
|
|
3955
|
-
endLine: symbol.endLine ?? symbol.startLine,
|
|
3955
|
+
endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
|
|
3956
3956
|
});
|
|
3957
3957
|
}
|
|
3958
3958
|
// Go has no test that is not a declaration: no unit, no test.
|
package/languages/index.js
CHANGED
|
@@ -139,6 +139,7 @@ const STRUCTURAL_TRAITS = {
|
|
|
139
139
|
physicalCallTokenSyntax: true,
|
|
140
140
|
scopeAlternativeBindings: false,
|
|
141
141
|
nameReferenceFamily: 'js',
|
|
142
|
+
discardIdentifierNames: [],
|
|
142
143
|
staticValueImportSyntax: null,
|
|
143
144
|
implicitEnumCaseLabels: false,
|
|
144
145
|
valuePatternConstants: false,
|
|
@@ -196,6 +197,13 @@ const STRUCTURAL_TRAITS = {
|
|
|
196
197
|
memberHidingModifier: null,
|
|
197
198
|
virtualMemberModifiers: null,
|
|
198
199
|
hasArityOverloads: false,
|
|
200
|
+
// Bounded literal proofs for changed project calls; absent means unknown.
|
|
201
|
+
checkLiteralArguments: null,
|
|
202
|
+
checkDirectiveComment: null,
|
|
203
|
+
checkFileDirectiveComment: null,
|
|
204
|
+
checkDirectiveTarget: null,
|
|
205
|
+
checkDirectiveBarrier: null,
|
|
206
|
+
checkTypedComment: null,
|
|
199
207
|
userDefinedConversions: null,
|
|
200
208
|
overloadPhases: null,
|
|
201
209
|
overloadFileSymbols: false,
|
|
@@ -537,6 +545,7 @@ const NOMINAL_TRAITS = {
|
|
|
537
545
|
physicalCallTokenSyntax: true,
|
|
538
546
|
scopeAlternativeBindings: false,
|
|
539
547
|
nameReferenceFamily: null,
|
|
548
|
+
discardIdentifierNames: [],
|
|
540
549
|
staticValueImportSyntax: null,
|
|
541
550
|
implicitEnumCaseLabels: false,
|
|
542
551
|
valuePatternConstants: false,
|
|
@@ -589,6 +598,13 @@ const NOMINAL_TRAITS = {
|
|
|
589
598
|
// the caller contract: a pinned overload is only confirmed when the call
|
|
590
599
|
// site provably binds it.
|
|
591
600
|
hasArityOverloads: false,
|
|
601
|
+
// Bounded literal proofs for changed project calls; absent means unknown.
|
|
602
|
+
checkLiteralArguments: null,
|
|
603
|
+
checkDirectiveComment: null,
|
|
604
|
+
checkFileDirectiveComment: null,
|
|
605
|
+
checkDirectiveTarget: null,
|
|
606
|
+
checkDirectiveBarrier: null,
|
|
607
|
+
checkTypedComment: null,
|
|
592
608
|
// User-defined conversions keep otherwise incompatible argument types
|
|
593
609
|
// possible; selecting an overload still needs a positive conversion proof.
|
|
594
610
|
userDefinedConversions: null,
|
|
@@ -919,6 +935,11 @@ const LANGUAGES = {
|
|
|
919
935
|
treeSitterModule: () => require('tree-sitter-javascript'),
|
|
920
936
|
traits: {
|
|
921
937
|
...STRUCTURAL_TRAITS,
|
|
938
|
+
checkTypedComment: text => /@ts-check\b|@(?:param|type|returns?)\s*\{/.test(text),
|
|
939
|
+
checkDirectiveComment: text => require('./javascript').checkDirectiveComment(text),
|
|
940
|
+
checkFileDirectiveComment: text => require('./javascript').checkFileDirectiveComment(text),
|
|
941
|
+
checkDirectiveTarget: 'next-line',
|
|
942
|
+
checkDirectiveBarrier: text => text.startsWith('/*'),
|
|
922
943
|
testUnits: (fileEntry, helpers) => require('./javascript').testUnits(fileEntry, helpers),
|
|
923
944
|
testRunnerModules: () => require('./javascript').TEST_RUNNER_MODULES,
|
|
924
945
|
testExport: (fileEntry, helpers, name) => require('./javascript').testExport(fileEntry, helpers, name),
|
|
@@ -948,6 +969,11 @@ const LANGUAGES = {
|
|
|
948
969
|
treeSitterModule: () => require('tree-sitter-typescript').typescript,
|
|
949
970
|
traits: {
|
|
950
971
|
...STRUCTURAL_TRAITS,
|
|
972
|
+
checkLiteralArguments: { smallInteger: ['number'], string: ['string'], boolean: ['boolean'] },
|
|
973
|
+
checkDirectiveComment: text => require('./javascript').checkDirectiveComment(text),
|
|
974
|
+
checkFileDirectiveComment: text => require('./javascript').checkFileDirectiveComment(text),
|
|
975
|
+
checkDirectiveTarget: 'next-line',
|
|
976
|
+
checkDirectiveBarrier: text => text.startsWith('/*'),
|
|
951
977
|
testUnits: (fileEntry, helpers) => require('./javascript').testUnits(fileEntry, helpers),
|
|
952
978
|
testRunnerModules: () => require('./javascript').TEST_RUNNER_MODULES,
|
|
953
979
|
testExport: (fileEntry, helpers, name) => require('./javascript').testExport(fileEntry, helpers, name),
|
|
@@ -981,6 +1007,11 @@ const LANGUAGES = {
|
|
|
981
1007
|
treeSitterModule: () => require('tree-sitter-typescript').tsx,
|
|
982
1008
|
traits: {
|
|
983
1009
|
...STRUCTURAL_TRAITS,
|
|
1010
|
+
checkLiteralArguments: { smallInteger: ['number'], string: ['string'], boolean: ['boolean'] },
|
|
1011
|
+
checkDirectiveComment: text => require('./javascript').checkDirectiveComment(text),
|
|
1012
|
+
checkFileDirectiveComment: text => require('./javascript').checkFileDirectiveComment(text),
|
|
1013
|
+
checkDirectiveTarget: 'next-line',
|
|
1014
|
+
checkDirectiveBarrier: text => text.startsWith('/*'),
|
|
984
1015
|
testUnits: (fileEntry, helpers) => require('./javascript').testUnits(fileEntry, helpers),
|
|
985
1016
|
testRunnerModules: () => require('./javascript').TEST_RUNNER_MODULES,
|
|
986
1017
|
testExport: (fileEntry, helpers, name) => require('./javascript').testExport(fileEntry, helpers, name),
|
|
@@ -1014,6 +1045,9 @@ const LANGUAGES = {
|
|
|
1014
1045
|
treeSitterModule: () => require('tree-sitter-python'),
|
|
1015
1046
|
traits: {
|
|
1016
1047
|
...STRUCTURAL_TRAITS,
|
|
1048
|
+
checkLiteralArguments: { smallInteger: ['int', 'float'], string: ['str'], boolean: ['bool'] },
|
|
1049
|
+
checkDirectiveComment: text => require('./python').checkDirectiveComment(text),
|
|
1050
|
+
checkFileDirectiveComment: text => require('./python').checkFileDirectiveComment(text),
|
|
1017
1051
|
testUnits: (fileEntry, helpers) => require('./python').testUnits(fileEntry, helpers),
|
|
1018
1052
|
testRunnerFamily: 'python',
|
|
1019
1053
|
testFileNames: [/^test_[^/]*\.py$/, /_test\.py$/],
|
|
@@ -1129,7 +1163,8 @@ const LANGUAGES = {
|
|
|
1129
1163
|
owners: ['Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum'] },
|
|
1130
1164
|
]),
|
|
1131
1165
|
reflectionApi: {
|
|
1132
|
-
|
|
1166
|
+
pythonBindingIdentity: true,
|
|
1167
|
+
hintPattern: /\b(?:getattr|setattr|hasattr|delattr|eval|exec|globals|vars|patch|MonkeyPatch)\b/,
|
|
1133
1168
|
functions: [{ callee: /^(?:getattr|setattr|hasattr|delattr)$/, nameArg: 1, receiverArg: 0 }],
|
|
1134
1169
|
namespaceLookups: { calls: /^eval$/, subscriptOf: /^(?:globals|vars)\(\)$/ },
|
|
1135
1170
|
// `exec(code)` runs statements; it names nothing a lookup
|
|
@@ -1148,6 +1183,7 @@ const LANGUAGES = {
|
|
|
1148
1183
|
treeSitterModule: () => require('tree-sitter-go'),
|
|
1149
1184
|
traits: {
|
|
1150
1185
|
...NOMINAL_TRAITS,
|
|
1186
|
+
checkLiteralArguments: { smallInteger: ['int', 'int8', 'int16', 'int32', 'int64', 'uint', 'uint8', 'uint16', 'uint32', 'uint64', 'uintptr', 'float32', 'float64'], string: ['string'], boolean: ['bool'] },
|
|
1151
1187
|
testUnits: (fileEntry, helpers) => require('./go').testUnits(fileEntry, helpers),
|
|
1152
1188
|
testGate: (fileEntry, helpers) => require('./go').testGate(fileEntry, helpers),
|
|
1153
1189
|
testRunnerFamily: 'go',
|
|
@@ -1155,6 +1191,7 @@ const LANGUAGES = {
|
|
|
1155
1191
|
selfParam: null,
|
|
1156
1192
|
packageScope: 'directory',
|
|
1157
1193
|
nameReferenceFamily: 'go',
|
|
1194
|
+
discardIdentifierNames: ['_'],
|
|
1158
1195
|
closedQueryBodies: ['function_declaration', 'method_declaration', 'func_literal'],
|
|
1159
1196
|
hasReceiverPackageCalls: true,
|
|
1160
1197
|
exportVisibility: 'capitalization',
|
|
@@ -1186,6 +1223,7 @@ const LANGUAGES = {
|
|
|
1186
1223
|
treeSitterModule: () => require('tree-sitter-rust'),
|
|
1187
1224
|
traits: {
|
|
1188
1225
|
...NOMINAL_TRAITS,
|
|
1226
|
+
checkLiteralArguments: { smallInteger: ['i8', 'i16', 'i32', 'i64', 'i128', 'isize', 'u8', 'u16', 'u32', 'u64', 'u128', 'usize'], string: ['&str'], boolean: ['bool'] },
|
|
1189
1227
|
testUnits: (fileEntry, helpers) => require('./rust').testUnits(fileEntry, helpers),
|
|
1190
1228
|
testRunnerFamily: 'rust',
|
|
1191
1229
|
testFileNames: [/(^|\/)tests\/[^/]+\.rs$/],
|
|
@@ -1384,6 +1422,7 @@ const LANGUAGES = {
|
|
|
1384
1422
|
treeSitterModule: () => require('tree-sitter-c'),
|
|
1385
1423
|
traits: {
|
|
1386
1424
|
...NOMINAL_TRAITS,
|
|
1425
|
+
checkLiteralArguments: { smallInteger: ['int', 'short', 'long', 'long long', 'unsigned', 'unsigned int', 'unsigned short', 'unsigned long', 'unsigned long long', 'float', 'double'] },
|
|
1387
1426
|
selfParam: null,
|
|
1388
1427
|
packageScope: 'file',
|
|
1389
1428
|
nameReferenceFamily: 'c',
|
|
@@ -1596,6 +1635,11 @@ const LANGUAGES = {
|
|
|
1596
1635
|
treeSitterModule: () => require('tree-sitter-html'),
|
|
1597
1636
|
traits: {
|
|
1598
1637
|
...STRUCTURAL_TRAITS,
|
|
1638
|
+
checkTypedComment: text => /@ts-check\b|@(?:param|type|returns?)\s*\{/.test(text),
|
|
1639
|
+
checkDirectiveComment: text => require('./javascript').checkDirectiveComment(text),
|
|
1640
|
+
checkFileDirectiveComment: text => require('./javascript').checkFileDirectiveComment(text),
|
|
1641
|
+
checkDirectiveTarget: 'next-line',
|
|
1642
|
+
checkDirectiveBarrier: text => text.startsWith('/*'),
|
|
1599
1643
|
lenientCallArity: true,
|
|
1600
1644
|
// Embedded scripts supply their own parse trees.
|
|
1601
1645
|
physicalCallTokenSyntax: false,
|
package/languages/java.js
CHANGED
|
@@ -3433,7 +3433,7 @@ function testUnits(fileEntry, helpers) {
|
|
|
3433
3433
|
testngState(symbol).several || (byClass && testngState(owner).several) || chain.some(otherRunner);
|
|
3434
3434
|
const row = {
|
|
3435
3435
|
name: symbol.name, line: symbol.nameLine || symbol.startLine,
|
|
3436
|
-
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine,
|
|
3436
|
+
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
|
|
3437
3437
|
...(off && { skipped: true }),
|
|
3438
3438
|
...(!off && sometimes && { conditional: true }),
|
|
3439
3439
|
// One declaration the runner reports as several tests.
|
|
@@ -3529,12 +3529,12 @@ function testUnits(fileEntry, helpers) {
|
|
|
3529
3529
|
...(maybeRefused.has(cls) && { mayRefuse: true }),
|
|
3530
3530
|
...([...chain, cls].some(otherRunner) && { runner: true }),
|
|
3531
3531
|
...(cls.namespace && { namespace: cls.namespace }),
|
|
3532
|
-
line: cls.nameLine || cls.startLine, startLine: cls.startLine, endLine: cls.endLine ?? cls.startLine,
|
|
3532
|
+
line: cls.nameLine || cls.startLine, startLine: cls.startLine, endLine: cls.endLine ?? cls.startLine, sourceSpan: cls.sourceSpan,
|
|
3533
3533
|
members: methodsOf(cls).map(symbol => symbol.name),
|
|
3534
3534
|
// Where each method's body stands, for a test it gives the body to.
|
|
3535
3535
|
memberRows: methodsOf(cls).map(symbol => ({
|
|
3536
3536
|
name: symbol.name, ...signatureOf(symbol), line: symbol.nameLine || symbol.startLine,
|
|
3537
|
-
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine,
|
|
3537
|
+
startLine: symbol.startLine, endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
|
|
3538
3538
|
...(symbol.type !== 'constructor' && classCandidate(symbol) && { candidate: true }),
|
|
3539
3539
|
})),
|
|
3540
3540
|
// TestNG's @Test on the class makes its public methods tests,
|
|
@@ -3548,7 +3548,7 @@ function testUnits(fileEntry, helpers) {
|
|
|
3548
3548
|
units.push({
|
|
3549
3549
|
kind: 'suite', name: cls.name, suite: chain.map(outer => outer.name),
|
|
3550
3550
|
line: cls.nameLine || cls.startLine, startLine: cls.startLine,
|
|
3551
|
-
endLine: cls.endLine ?? cls.startLine,
|
|
3551
|
+
endLine: cls.endLine ?? cls.startLine, sourceSpan: cls.sourceSpan,
|
|
3552
3552
|
});
|
|
3553
3553
|
}
|
|
3554
3554
|
}
|
package/languages/javascript.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
9
9
|
const { referenceScope, scopeFields } = require('./lexical-scope');
|
|
10
|
+
const { jsStaticKey } = require('./static-js');
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
const {
|
|
@@ -4356,13 +4357,14 @@ function findCallsInCode(code, parser) {
|
|
|
4356
4357
|
argCount: node.childForFieldName('arguments')?.namedChildCount ?? 0,
|
|
4357
4358
|
enclosingFunction,
|
|
4358
4359
|
});
|
|
4359
|
-
} else if (funcNode.type === 'member_expression'
|
|
4360
|
+
} else if (funcNode.type === 'member_expression' ||
|
|
4361
|
+
(funcNode.type === 'subscript_expression' && jsStaticKey(funcNode.childForFieldName('index')) != null)) {
|
|
4360
4362
|
// Method call: obj.foo() or foo.call/apply/bind()
|
|
4361
|
-
const propNode = funcNode.childForFieldName('property');
|
|
4363
|
+
const propNode = funcNode.childForFieldName('property') || funcNode.childForFieldName('index');
|
|
4362
4364
|
let objNode = funcNode.childForFieldName('object');
|
|
4363
4365
|
|
|
4364
4366
|
if (propNode) {
|
|
4365
|
-
const propName = propNode.text;
|
|
4367
|
+
const propName = funcNode.type === 'subscript_expression' ? jsStaticKey(propNode) : propNode.text;
|
|
4366
4368
|
|
|
4367
4369
|
// Handle .call(), .apply(), .bind() - these are calls TO the object.
|
|
4368
4370
|
// boundCall marks the indirection (fix #221, family B): the line
|
|
@@ -5726,6 +5728,19 @@ function findUsagesInCode(code, name, parser, tree, options = {}) {
|
|
|
5726
5728
|
const scopeMemo = options.lexicalScopes ? new Map() : null;
|
|
5727
5729
|
|
|
5728
5730
|
visitNameNodes(tree, code, name, (node) => {
|
|
5731
|
+
// A literal bracket key is the member token of an ordinary call.
|
|
5732
|
+
// Keep it in the same ground-set bucket as the dotted spelling.
|
|
5733
|
+
const literal = node.type === 'string' ? node : node.parent?.type === 'string' ? node.parent : null;
|
|
5734
|
+
const computed = literal?.parent;
|
|
5735
|
+
if (computed?.type === 'subscript_expression' &&
|
|
5736
|
+
sameNode(computed.childForFieldName('index'), literal) && jsStaticKey(literal) === name) {
|
|
5737
|
+
const call = computed.parent;
|
|
5738
|
+
const receiver = computed.childForFieldName('object');
|
|
5739
|
+
usages.push({ line: node.startPosition.row + 1, column: node.startPosition.column,
|
|
5740
|
+
usageType: call?.type === 'call_expression' && sameNode(call.childForFieldName('function'), computed) ? 'call' : 'reference',
|
|
5741
|
+
...(['identifier', 'this', 'super'].includes(receiver?.type) && { receiver: receiver.text }) });
|
|
5742
|
+
return true;
|
|
5743
|
+
}
|
|
5729
5744
|
// Look for identifier, property_identifier (method names in obj.method() calls),
|
|
5730
5745
|
// private_property_identifier (#method definitions and calls),
|
|
5731
5746
|
// type_identifier (TypeScript type annotations), shorthand_property_identifier_pattern
|
|
@@ -5879,7 +5894,7 @@ function findUsagesInCode(code, name, parser, tree, options = {}) {
|
|
|
5879
5894
|
}
|
|
5880
5895
|
// JSX component usage: <Component /> or <Component>...</Component>
|
|
5881
5896
|
else if (parent.type === 'jsx_self_closing_element' || parent.type === 'jsx_opening_element') {
|
|
5882
|
-
usageType = 'call'
|
|
5897
|
+
usageType = /^[A-Z]/.test(node.text) ? 'call' : 'intrinsic';
|
|
5883
5898
|
}
|
|
5884
5899
|
}
|
|
5885
5900
|
|
|
@@ -6898,6 +6913,9 @@ function testUnits(fileEntry, helpers) {
|
|
|
6898
6913
|
kind: api.kind, name: title, titled: true, suite: ctx.suites.map(suite => suite.name),
|
|
6899
6914
|
line: node.startPosition.row + 1, startLine: node.startPosition.row + 1,
|
|
6900
6915
|
endLine: node.endPosition.row + 1,
|
|
6916
|
+
sourceSpan: { start: node.startIndex, end: node.endIndex,
|
|
6917
|
+
startLine: node.startPosition.row + 1, endLine: node.endPosition.row + 1,
|
|
6918
|
+
startColumn: node.startPosition.column, endColumn: node.endPosition.column },
|
|
6901
6919
|
parent: outer ? outer.index : -1,
|
|
6902
6920
|
...(api.only && { only: true }),
|
|
6903
6921
|
...(skipped && { skipped: true }),
|
|
@@ -7118,14 +7136,28 @@ const TS_DIRECTIVE_BLOCK = /^(?:\/|\*)*\s*@(?:ts-expect-error|ts-ignore)/;
|
|
|
7118
7136
|
// A file pragma: `// @name`, the name ending at a space or a colon.
|
|
7119
7137
|
const TS_FILE_PRAGMA = /^\/\/\/?\s*@([^\s:]+)/;
|
|
7120
7138
|
|
|
7139
|
+
function lineCheckDirective(text) {
|
|
7140
|
+
return text.startsWith('//') ? TS_DIRECTIVE_LINE.test(text)
|
|
7141
|
+
: TS_DIRECTIVE_BLOCK.test(text.slice(text.lastIndexOf('\n') + 1).trimStart());
|
|
7142
|
+
}
|
|
7143
|
+
|
|
7144
|
+
/** The same scanner grammar used by check suppression and diff context. */
|
|
7145
|
+
function checkFileDirectiveComment(text) {
|
|
7146
|
+
const pragma = text.startsWith('//') ? TS_FILE_PRAGMA.exec(text) : null;
|
|
7147
|
+
const name = pragma ? pragma[1].toLowerCase() : null;
|
|
7148
|
+
return name === 'ts-nocheck' || name === 'ts-check' ? 'leading' : null;
|
|
7149
|
+
}
|
|
7150
|
+
function checkDirectiveComment(text) {
|
|
7151
|
+
return lineCheckDirective(text) || checkFileDirectiveComment(text) !== null;
|
|
7152
|
+
}
|
|
7153
|
+
|
|
7121
7154
|
/** Lines that hold a directive comment, and whether the file is unchecked. */
|
|
7122
7155
|
function checkDirectives(root) {
|
|
7123
7156
|
const lines = new Set();
|
|
7124
7157
|
let unchecked = false;
|
|
7125
7158
|
for (const comment of root.descendantsOfType('comment')) {
|
|
7126
7159
|
const text = comment.text;
|
|
7127
|
-
const directive =
|
|
7128
|
-
: TS_DIRECTIVE_BLOCK.test(text.slice(text.lastIndexOf('\n') + 1).trimStart());
|
|
7160
|
+
const directive = lineCheckDirective(text);
|
|
7129
7161
|
if (directive) lines.add(comment.endPosition.row + 1);
|
|
7130
7162
|
}
|
|
7131
7163
|
// `@ts-nocheck` and `@ts-check` are pragmas of the `//` comments
|
|
@@ -7343,6 +7375,8 @@ module.exports = {
|
|
|
7343
7375
|
classMemberRole,
|
|
7344
7376
|
typedLiteralMembers,
|
|
7345
7377
|
checkSuppressedAt,
|
|
7378
|
+
checkDirectiveComment,
|
|
7379
|
+
checkFileDirectiveComment,
|
|
7346
7380
|
checkedImportAt,
|
|
7347
7381
|
CLASS_DECLARATION_NODES,
|
|
7348
7382
|
findFunctions,
|
package/languages/python.js
CHANGED
|
@@ -342,6 +342,8 @@ function _processFunction(node, functions, processedRanges, lines, code) {
|
|
|
342
342
|
...(paramTypes && { paramTypes }),
|
|
343
343
|
...(docstring && { docstring }),
|
|
344
344
|
...(decorators.length > 0 && { decorators }),
|
|
345
|
+
...(decorators.length > 0 && { registrationUnknown: require('../core/python-binding-identity').registrationUnknown(node) }),
|
|
346
|
+
runtimeOwnerPath: require('../core/python-binding-identity').pythonRuntimeOwners(node),
|
|
345
347
|
...(isOverloadDecorated(decorators) && { isSignature: true }),
|
|
346
348
|
...(nameLine !== decoratorStartLine && { nameLine })
|
|
347
349
|
});
|
|
@@ -389,6 +391,8 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
389
391
|
members,
|
|
390
392
|
...(docstring && { docstring }),
|
|
391
393
|
...(decorators.length > 0 && { decorators }),
|
|
394
|
+
...(decorators.length > 0 && { registrationUnknown: require('../core/python-binding-identity').registrationUnknown(node) }),
|
|
395
|
+
runtimeOwnerPath: require('../core/python-binding-identity').pythonRuntimeOwners(node),
|
|
392
396
|
...(bases.length > 0 && { extends: bases.join(', ') }),
|
|
393
397
|
// A protocol class is satisfied by shape: no class has to name it.
|
|
394
398
|
...(isProtocolClass(node) && { protocolClass: true }),
|
|
@@ -1317,6 +1321,8 @@ function extractClassMembers(classNode, code) {
|
|
|
1317
1321
|
...(paramTypes && { paramTypes }),
|
|
1318
1322
|
...(docstring && { docstring }),
|
|
1319
1323
|
...(memberDecorators.length > 0 && { decorators: memberDecorators }),
|
|
1324
|
+
...(memberDecorators.length > 0 && { registrationUnknown: require('../core/python-binding-identity').registrationUnknown(funcNode) }),
|
|
1325
|
+
runtimeOwnerPath: require('../core/python-binding-identity').pythonRuntimeOwners(funcNode),
|
|
1320
1326
|
...(isOverloadDecorated(memberDecorators) && { isSignature: true }),
|
|
1321
1327
|
...(isRequiredPythonMember(classNode, memberDecorators) && { requiredMember: true }),
|
|
1322
1328
|
...(nameLine !== startLine && { nameLine })
|
|
@@ -2726,6 +2732,7 @@ function pythonModuleAliases(tree) {
|
|
|
2726
2732
|
|
|
2727
2733
|
// Annotation positions whose value Python evaluates as an expression.
|
|
2728
2734
|
const PY_ANNOTATION_OWNERS = new Set(['typed_parameter', 'typed_default_parameter', 'function_definition', 'assignment']);
|
|
2735
|
+
const annotationQuotesByTree = new WeakMap();
|
|
2729
2736
|
|
|
2730
2737
|
/**
|
|
2731
2738
|
* String annotations holding calls (fix #398F): `def f(v: "Annotated[int,
|
|
@@ -2735,9 +2742,13 @@ const PY_ANNOTATION_OWNERS = new Set(['typed_parameter', 'typed_default_paramete
|
|
|
2735
2742
|
* interpolation); the result lists the byte ranges of their quotes.
|
|
2736
2743
|
*/
|
|
2737
2744
|
function annotationStringQuotes(tree) {
|
|
2745
|
+
const cached = annotationQuotesByTree.get(tree);
|
|
2746
|
+
if (cached) return cached;
|
|
2738
2747
|
const quotes = [];
|
|
2739
|
-
|
|
2740
|
-
|
|
2748
|
+
// The source tree is immutable. A multi-name query should discover
|
|
2749
|
+
// these file-level ranges once, without rebuilding a flat node list
|
|
2750
|
+
// whenever another file displaces the general traversal cache.
|
|
2751
|
+
for (const holder of tree.rootNode.descendantsOfType('type')) {
|
|
2741
2752
|
const string = holder.namedChildCount === 1 ? holder.namedChild(0) : null;
|
|
2742
2753
|
if (string?.type !== 'string' || string.startPosition.row !== string.endPosition.row) continue;
|
|
2743
2754
|
const owner = holder.parent;
|
|
@@ -2754,6 +2765,7 @@ function annotationStringQuotes(tree) {
|
|
|
2754
2765
|
quotes.push({ open: parts[0].startIndex, close: parts[2].startIndex,
|
|
2755
2766
|
contentStart: content.startIndex, contentEnd: content.endIndex });
|
|
2756
2767
|
}
|
|
2768
|
+
annotationQuotesByTree.set(tree, quotes);
|
|
2757
2769
|
return quotes;
|
|
2758
2770
|
}
|
|
2759
2771
|
|
|
@@ -6597,7 +6609,7 @@ function testUnits(fileEntry, helpers) {
|
|
|
6597
6609
|
: (switchUnsettled || (written && written.on === 'unknown')) ? 'collection-switch' : null;
|
|
6598
6610
|
return {
|
|
6599
6611
|
name: symbol.name, line: symbol.nameLine || symbol.startLine, startLine: symbol.startLine,
|
|
6600
|
-
endLine: symbol.endLine ?? symbol.startLine,
|
|
6612
|
+
endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
|
|
6601
6613
|
named: functionNamed(symbol.name), prefixed: symbol.name.startsWith('test'),
|
|
6602
6614
|
// (Where a switch of the file is not read, off is not settled either.)
|
|
6603
6615
|
...(written && written.on === false && !switchUnsettled && { off: true }),
|
|
@@ -6804,7 +6816,7 @@ function testUnits(fileEntry, helpers) {
|
|
|
6804
6816
|
...((written.some(entry => entry.from.module === 'abc' && entry.from.name === 'ABC') || metaPath === 'abc.ABCMeta')
|
|
6805
6817
|
? { abc: true } : metaclass ? { abc: 'unknown' } : {}),
|
|
6806
6818
|
marks: ownMarks.marks, marksOpen: ownMarks.open,
|
|
6807
|
-
line: cls.nameLine || cls.startLine, startLine: cls.startLine, endLine: cls.endLine ?? cls.startLine,
|
|
6819
|
+
line: cls.nameLine || cls.startLine, startLine: cls.startLine, endLine: cls.endLine ?? cls.startLine, sourceSpan: cls.sourceSpan,
|
|
6808
6820
|
members: [...members], tests,
|
|
6809
6821
|
});
|
|
6810
6822
|
}
|
|
@@ -7335,6 +7347,27 @@ function annotatedReceiverType(tree, line, column, receiver) {
|
|
|
7335
7347
|
const TYPE_IGNORE = /(^|#)\s*type:\s*ignore(\[[\s\w-,]*\]|\s|$)/;
|
|
7336
7348
|
const PYRIGHT_IGNORE = /(^|#)\s*pyright:\s*ignore(?:\[([\s\w-,]*)\]|\s|$)/;
|
|
7337
7349
|
const CHECK_MODES = new Set(['basic', 'standard', 'strict']);
|
|
7350
|
+
|
|
7351
|
+
function checkSettingOperands(text) {
|
|
7352
|
+
const value = text.slice(1).trim();
|
|
7353
|
+
if (!value.startsWith('pyright:')) return null;
|
|
7354
|
+
const operands = value.slice('pyright:'.length);
|
|
7355
|
+
return operands.trim().startsWith('ignore') ? null : operands;
|
|
7356
|
+
}
|
|
7357
|
+
|
|
7358
|
+
/** Use the same ignore grammar for suppression and changed-code context. */
|
|
7359
|
+
function checkDirectiveComment(text) {
|
|
7360
|
+
// Ordinary type annotations also affect checking. Keep those even
|
|
7361
|
+
// though they are not suppressions read by checkSuppressedAt.
|
|
7362
|
+
return /^#\s*(?:type|pyright)\s*:/.test(text) ||
|
|
7363
|
+
TYPE_IGNORE.test(text) || PYRIGHT_IGNORE.test(text);
|
|
7364
|
+
}
|
|
7365
|
+
|
|
7366
|
+
function checkFileDirectiveComment(text) {
|
|
7367
|
+
if (TYPE_IGNORE.test(text)) return 'leading-or-local';
|
|
7368
|
+
if (PYRIGHT_IGNORE.test(text)) return null;
|
|
7369
|
+
return checkSettingOperands(text) !== null ? 'column-zero-file' : null;
|
|
7370
|
+
}
|
|
7338
7371
|
// The rule each kind of break is reported under.
|
|
7339
7372
|
const RULE_OF = Object.freeze({
|
|
7340
7373
|
'arity': 'reportCallIssue',
|
|
@@ -7348,12 +7381,14 @@ const RULE_OF = Object.freeze({
|
|
|
7348
7381
|
* all a `type: ignore` comment before any token: nothing is reported;
|
|
7349
7382
|
* lines line -> null (everything on it is ignored: `type: ignore`,
|
|
7350
7383
|
* `pyright: ignore`) or the rules a `pyright: ignore[..]` names;
|
|
7351
|
-
* levels rule -> level set by `# pyright: rule=level` comments
|
|
7352
|
-
*
|
|
7384
|
+
* levels rule -> level set by `# pyright: rule=level` comments. The
|
|
7385
|
+
* checker applies them even at an invalid position, reporting
|
|
7386
|
+
* a separate placement diagnostic; changed-code context tracks
|
|
7387
|
+
* that column-zero requirement independently.
|
|
7353
7388
|
* `basic`, `standard` and `strict` choose a rule set: none of
|
|
7354
7389
|
* them turns a rule off.
|
|
7355
7390
|
*/
|
|
7356
|
-
function checkDirectives(root
|
|
7391
|
+
function checkDirectives(root) {
|
|
7357
7392
|
const ignored = new Map();
|
|
7358
7393
|
const levels = new Map();
|
|
7359
7394
|
let all = false;
|
|
@@ -7371,11 +7406,8 @@ function checkDirectives(root, lines) {
|
|
|
7371
7406
|
ignored.set(line, scoped[2] === undefined ? null
|
|
7372
7407
|
: scoped[2].split(',').map(rule => rule.trim()).filter(Boolean));
|
|
7373
7408
|
}
|
|
7374
|
-
const
|
|
7375
|
-
|
|
7376
|
-
if (!ownLine || !value.startsWith('pyright:')) continue;
|
|
7377
|
-
const operands = value.slice('pyright:'.length);
|
|
7378
|
-
if (operands.trim().startsWith('ignore')) continue;
|
|
7409
|
+
const operands = checkSettingOperands(text);
|
|
7410
|
+
if (operands === null) continue;
|
|
7379
7411
|
const list = operands.split(',').map(operand => operand.trim()).filter(Boolean);
|
|
7380
7412
|
if (list.some(operand => CHECK_MODES.has(operand))) levels.clear();
|
|
7381
7413
|
for (const operand of list) {
|
|
@@ -7482,10 +7514,10 @@ function reportedRanges(root, site) {
|
|
|
7482
7514
|
* problem, module, memo }
|
|
7483
7515
|
*/
|
|
7484
7516
|
function checkSuppressedAt(site) {
|
|
7485
|
-
const {
|
|
7517
|
+
const { root, memo } = site;
|
|
7486
7518
|
if (!root) return false;
|
|
7487
|
-
if (memo && !memo.has('directives')) memo.set('directives', checkDirectives(root
|
|
7488
|
-
const directives = memo ? memo.get('directives') : checkDirectives(root
|
|
7519
|
+
if (memo && !memo.has('directives')) memo.set('directives', checkDirectives(root));
|
|
7520
|
+
const directives = memo ? memo.get('directives') : checkDirectives(root);
|
|
7489
7521
|
if (directives.all) return true;
|
|
7490
7522
|
const rule = RULE_OF[site.kind] || null;
|
|
7491
7523
|
const level = rule ? directives.levels.get(rule) : undefined;
|
|
@@ -7516,6 +7548,8 @@ function checkedImportAt(root, line, module) {
|
|
|
7516
7548
|
}
|
|
7517
7549
|
|
|
7518
7550
|
module.exports = {
|
|
7551
|
+
checkDirectiveComment,
|
|
7552
|
+
checkFileDirectiveComment,
|
|
7519
7553
|
checkSuppressedAt,
|
|
7520
7554
|
checkedImportAt,
|
|
7521
7555
|
annotatedReceiverType,
|