2 follow-up flagships: symfony-0001 + pyright-0001 (Wave 17 backlog cleared)
Both were flagged in Wave 17 survey as borderline real defects deferred for follow-up because the fix needed careful design beyond a single-line set hoist. Both shipped now with full bench + ticket + intel. UNDF-1310 symfony-0001 (HIGH) - PropertyAccessor::writeCollection. Doctrine entity collection diff: in_array($item, $collection, true) per item in $previousValue, then in_array($item, $previousValue, true) per item in $collection. O(P*C). Fix: dual lookup (SplObjectStorage for objects + serialize-keyed array for scalars, in_array fallback for resources). Bench: 5.2x at P=C=100, 88x at P=C=2000. UNDF-1311 pyright-0001 (HIGH) - CallHierarchyProvider outgoing/incoming call dedup. _outgoingCalls.find / _incomingCalls.find with composite key (uri, range) walks the list per call expression. O(C^2). Fix: parallel Map<string, entry> keyed by composite serialized form (uri|start.line|start.char|end.line|end.char). Bench: 2.7x at C=100, 22x at C=2000. Total session flagships: 11 (was 9) — 7 CWE-407 + 3 MOAD-0003 + 1 MOAD-0004. Wave 17 borderline backlog now empty.
This commit is contained in:
parent
2eea7d128c
commit
24052ec668
11 changed files with 758 additions and 0 deletions
|
|
@ -0,0 +1,103 @@
|
|||
# UNDF: UNDF-2026-000001311
|
||||
# CWE-407: Algorithmic Complexity — O(C^2) -> O(C) in
|
||||
# CallHierarchyProvider {outgoing,incoming} call dedup
|
||||
#
|
||||
# Defect: packages/pyright-internal/src/languageService/callHierarchyProvider.ts
|
||||
# has TWO parallel patterns that linear-scan the recorded calls list to
|
||||
# dedup by (uri, range):
|
||||
#
|
||||
# L394-396 (outgoing):
|
||||
# let outgoingCall = this._outgoingCalls.find(
|
||||
# (outgoing) => outgoing.to.uri === callDest.uri &&
|
||||
# rangesAreEqual(outgoing.to.range, callDest.range)
|
||||
# );
|
||||
#
|
||||
# L608-610 (incoming):
|
||||
# let incomingCall = this._incomingCalls.find(
|
||||
# (incoming) => incoming.from.uri === callSource.uri &&
|
||||
# rangesAreEqual(incoming.from.range, callSource.range)
|
||||
# );
|
||||
#
|
||||
# Per discovered call expression, the find() walks the list from index 0.
|
||||
# For C call expressions: O(C^2). For glue functions with 1000+ call sites
|
||||
# (utility/dispatcher/middleware patterns common in large Python codebases),
|
||||
# per-IDE-request cost is 1M+ comparisons.
|
||||
#
|
||||
# Fix: maintain a parallel Map<string, entry> keyed by composite
|
||||
# (uri | start.line | start.character | end.line | end.character). Lookup
|
||||
# via map.get(key) is O(1); list still preserves discovery order and is
|
||||
# what getOutgoingCalls()/getIncomingCalls() returns.
|
||||
#
|
||||
# Complexity gate (defects/pyright/bench/bench-pyright-0001.py):
|
||||
# C=2000: defective ~46ms, fixed <3ms (>=20x speedup)
|
||||
# k-scaling 5x: time ratio must be <17.5x
|
||||
--- a/packages/pyright-internal/src/languageService/callHierarchyProvider.ts
|
||||
+++ b/packages/pyright-internal/src/languageService/callHierarchyProvider.ts
|
||||
@@ -287,7 +287,16 @@ class CallFinder extends ParseTreeWalker {
|
||||
private _evaluator: TypeEvaluator;
|
||||
private _fs: ReadOnlyFileSystem;
|
||||
private _outgoingCalls: CallHierarchyOutgoingCall[] = [];
|
||||
+ // Parallel map for O(1) dedup lookup; key is composite (uri|range).
|
||||
+ // _outgoingCalls remains the discovery-ordered list returned to callers.
|
||||
+ private _outgoingCallsByKey: Map<string, CallHierarchyOutgoingCall> = new Map();
|
||||
|
||||
+ private static _callKey(uri: string, range: Range): string {
|
||||
+ return `${uri}|${range.start.line}|${range.start.character}|`
|
||||
+ + `${range.end.line}|${range.end.character}`;
|
||||
+ }
|
||||
+
|
||||
constructor(parseResults: ParseResults, evaluator: TypeEvaluator, fs: ReadOnlyFileSystem) {
|
||||
super();
|
||||
this._parseResults = parseResults;
|
||||
@@ -391,11 +400,12 @@ class CallFinder extends ParseTreeWalker {
|
||||
|
||||
// Is there already a call recorded for this destination? If so,
|
||||
// we'll simply add a new range. Otherwise, we'll create a new entry.
|
||||
- let outgoingCall: CallHierarchyOutgoingCall | undefined = this._outgoingCalls.find(
|
||||
- (outgoing) => outgoing.to.uri === callDest.uri && rangesAreEqual(outgoing.to.range, callDest.range)
|
||||
- );
|
||||
+ const dedupKey = CallFinder._callKey(callDest.uri, callDest.range);
|
||||
+ let outgoingCall: CallHierarchyOutgoingCall | undefined =
|
||||
+ this._outgoingCallsByKey.get(dedupKey);
|
||||
|
||||
if (!outgoingCall) {
|
||||
outgoingCall = {
|
||||
to: callDest,
|
||||
fromRanges: [],
|
||||
};
|
||||
this._outgoingCalls.push(outgoingCall);
|
||||
+ this._outgoingCallsByKey.set(dedupKey, outgoingCall);
|
||||
}
|
||||
@@ -420,7 +430,9 @@ class CallVisitor extends ParseTreeWalker {
|
||||
private _evaluator: TypeEvaluator;
|
||||
private _functionNode: FunctionNode;
|
||||
private _fileUri: Uri;
|
||||
- private readonly _incomingCalls: CallHierarchyIncomingCall[] = [];
|
||||
+ private _incomingCalls: CallHierarchyIncomingCall[] = [];
|
||||
+ // Parallel map for O(1) dedup lookup; key is composite (uri|range).
|
||||
+ private _incomingCallsByKey: Map<string, CallHierarchyIncomingCall> = new Map();
|
||||
|
||||
constructor(
|
||||
parseResults: ParseResults,
|
||||
@@ -605,11 +617,15 @@ class CallVisitor extends ParseTreeWalker {
|
||||
|
||||
// Is there already a call recorded for this caller? If so,
|
||||
// we'll simply add a new range. Otherwise, we'll create a new entry.
|
||||
- let incomingCall: CallHierarchyIncomingCall | undefined = this._incomingCalls.find(
|
||||
- (incoming) => incoming.from.uri === callSource.uri && rangesAreEqual(incoming.from.range, callSource.range)
|
||||
- );
|
||||
+ const dedupKey = `${callSource.uri}|${callSource.range.start.line}|`
|
||||
+ + `${callSource.range.start.character}|`
|
||||
+ + `${callSource.range.end.line}|`
|
||||
+ + `${callSource.range.end.character}`;
|
||||
+ let incomingCall: CallHierarchyIncomingCall | undefined =
|
||||
+ this._incomingCallsByKey.get(dedupKey);
|
||||
|
||||
if (!incomingCall) {
|
||||
incomingCall = {
|
||||
from: callSource,
|
||||
fromRanges: [],
|
||||
};
|
||||
this._incomingCalls.push(incomingCall);
|
||||
+ this._incomingCallsByKey.set(dedupKey, incomingCall);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue