Python fp and docs updtes (#58)

* refactor: Update comments for clarity and add expectations.json files for performance metrics

* feat: Implement FP guard for JS/TS local-collection receivers to suppress missing ownership checks

* feat: Enhance Rust parameter handling to classify local collections and prevent false ownership checks

* refactor: Simplify code formatting for better readability in multiple files

* refactor: Improve UTF-8 sequence length handling and enhance clarity in loop iteration

* feat: Update Java and Python patterns to include new security rules

* refactor: Improve comment clarity and consistency across multiple Rust files

* refactor: Simplify code formatting for improved readability in integration tests and module files

* refactor: Improve comment formatting and enhance clarity in assertions across multiple files
This commit is contained in:
Eli Peter 2026-04-29 19:53:34 -04:00 committed by GitHub
parent 4db0805de6
commit a438886217
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
291 changed files with 9485 additions and 3851 deletions

View file

@ -106,7 +106,7 @@ impl ConstValue {
if let Ok(i) = t.parse::<i64>() {
return Some(ConstValue::Int(i));
}
// Negative with space: "- 5" not supported, conservative
// Negative with space: "- 5", not supported, conservative
None
}
}
@ -118,9 +118,9 @@ impl ConstValue {
pub struct TypeSet(u16);
impl TypeSet {
/// All 12 type bits set no type constraint (Top).
/// All 12 type bits set, no type constraint (Top).
pub const TOP: Self = Self(0x0FFF);
/// No type bits unsatisfiable (Bottom).
/// No type bits, unsatisfiable (Bottom).
pub const BOTTOM: Self = Self(0);
pub fn singleton(kind: &TypeKind) -> Self {
@ -149,7 +149,7 @@ impl TypeSet {
self == Self::TOP
}
/// Complement all types NOT in this set.
/// Complement, all types NOT in this set.
pub fn complement(self) -> Self {
Self(!self.0 & Self::TOP.0)
}
@ -184,7 +184,7 @@ fn type_kind_index(kind: &TypeKind) -> u32 {
TypeKind::Url => 10,
TypeKind::HttpClient => 11,
TypeKind::LocalCollection => 12,
// Phase 6 DTO types carry per-field structural info that the
// the analysis DTO types carry per-field structural info that the
// bitset domain can't represent. Collapse to Unknown so callers
// still see "any type possible" rather than crashing on an
// unhandled variant. Same-file/cross-file Dto-aware paths read
@ -274,7 +274,7 @@ impl Nullability {
/// Boolean state lattice.
///
/// Same shape as [`Nullability`]. No `negate()` negation is structural
/// Same shape as [`Nullability`]. No `negate()`, negation is structural
/// on [`ConditionExpr`](super::lower::ConditionExpr).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BoolState {
@ -313,7 +313,7 @@ impl BoolState {
/// Abstract fact about a single SSA value.
///
/// Combines interval, constant, type, null, and boolean constraints.
/// There is intentionally no generic `negate()` on ValueFact negation
/// There is intentionally no generic `negate()` on ValueFact, negation
/// is structural on [`ConditionExpr`](super::lower::ConditionExpr) and
/// then applied as atomic refinements by the solver.
#[derive(Clone, Debug, PartialEq, Eq)]
@ -857,14 +857,14 @@ impl PathEnv {
// `assume_neq`, and a few internal sites. Large generated inputs
// (thousands of short statements on one line) can drive millions
// of calls and overflow a plain u16 `refine_count`. Saturate to
// stay within bounds the refinement pipeline is already
// stay within bounds, the refinement pipeline is already
// idempotent past the cap, so saturation is semantically a no-op.
self.refine_count = self.refine_count.saturating_add(1);
// Check size bound
let pos = self.facts.binary_search_by_key(&v, |(k, _)| *k);
if pos.is_err() && self.facts.len() >= MAX_PATH_ENV_ENTRIES {
return; // bounded don't grow
return; // bounded, don't grow
}
// Get meet count for widening
@ -963,7 +963,7 @@ impl PathEnv {
let ra = self.uf.find_immutable(a);
let rb = self.uf.find_immutable(b);
if ra == rb {
// Already known equal contradiction
// Already known equal, contradiction
self.unsat = true;
return;
}
@ -1040,7 +1040,7 @@ impl PathEnv {
return;
}
// Step 4: dedup check if this exact constraint already exists, skip
// Step 4: dedup check, if this exact constraint already exists, skip
let already_present = self
.relational
.iter()
@ -1052,7 +1052,7 @@ impl PathEnv {
if self.relational.len() < MAX_RELATIONAL {
self.relational.push((ra, op, rb));
}
// If at capacity, skip conservative: losing a constraint only
// If at capacity, skip, conservative: losing a constraint only
// loses pruning power, never introduces unsoundness.
}
@ -1089,7 +1089,7 @@ impl PathEnv {
if has_strict || op == RelOp::Lt {
return true;
}
// All Le: a <= b <= ... <= a means all equal satisfiable
// All Le: a <= b <= ... <= a means all equal, satisfiable
return false;
}
// Continue walking (take first outgoing edge)
@ -1181,11 +1181,11 @@ impl PathEnv {
while i < self.facts.len() && j < other.facts.len() {
match self.facts[i].0.cmp(&other.facts[j].0) {
std::cmp::Ordering::Less => {
// Only in self drop (absent on other side = Top)
// Only in self, drop (absent on other side = Top)
i += 1;
}
std::cmp::Ordering::Greater => {
// Only in other drop
// Only in other, drop
j += 1;
}
std::cmp::Ordering::Equal => {

View file

@ -8,10 +8,10 @@
//! 1. **Structural:** `condition_negated` (AST-level boolean)
//! 2. **Structural:** `condition_vars` (AST-extracted identifiers)
//! 3. **Structural:** compound decomposition (already handled by
//! `build_condition_chain` each leaf is a separate Block/Branch)
//! 4. **Structural:** `value_defs` resolve var names to [`SsaValue`]s
//! 5. **Structural:** `const_values` augment with known constants
//! 6. **Text fallback:** `condition_text` parse comparison operator and
//! `build_condition_chain`, each leaf is a separate Block/Branch)
//! 4. **Structural:** `value_defs`, resolve var names to [`SsaValue`]s
//! 5. **Structural:** `const_values`, augment with known constants
//! 6. **Text fallback:** `condition_text`, parse comparison operator and
//! literal operand. Necessary because individual comparisons are NOT
//! decomposed into separate SSA operations (condition nodes → `Nop`).
@ -82,7 +82,7 @@ impl CompOp {
/// Structured condition expression with SSA-resolved operands.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConditionExpr {
/// `lhs op rhs` e.g., `x > 5`, `x == y`.
/// `lhs op rhs`, e.g., `x > 5`, `x == y`.
Comparison {
lhs: Operand,
op: CompOp,
@ -98,7 +98,7 @@ pub enum ConditionExpr {
},
/// Boolean truthiness test: `if (x)`.
BoolTest { var: SsaValue },
/// Could not parse or resolve conservatively no refinement.
/// Could not parse or resolve, conservatively no refinement.
Unknown,
}
@ -240,7 +240,7 @@ pub fn lower_condition_with_stacks(
.map(|(name, val)| (name.as_str(), *val))
.collect();
// No const_values at lowering time empty lookup
// No const_values at lowering time, empty lookup
let const_lookup: HashMap<SsaValue, super::domain::ConstValue> = HashMap::new();
let lower = text.to_ascii_lowercase();

View file

@ -1,6 +1,6 @@
//! Constraint solver: apply conditions to [`PathEnv`] and check satisfiability.
//!
//! The solver operates on structured [`ConditionExpr`] values never on raw
//! The solver operates on structured [`ConditionExpr`] values, never on raw
//! text. Negation is always structural (via [`ConditionExpr::negate`] /
//! [`CompOp::negate`]), not via a generic "negate ValueFact" operation.
@ -13,7 +13,7 @@ use super::lower::{CompOp, ConditionExpr, Operand};
/// for the branch where the condition has the given polarity.
///
/// `polarity = true`: condition holds (true branch).
/// `polarity = false`: condition does NOT hold (false branch) negate
/// `polarity = false`: condition does NOT hold (false branch), negate
/// the condition structurally, then apply.
pub fn refine_env(env: &PathEnv, cond: &ConditionExpr, polarity: bool) -> PathEnv {
if env.is_unsat() {
@ -97,7 +97,7 @@ fn apply_condition(env: &mut PathEnv, cond: &ConditionExpr) {
}
ConditionExpr::Unknown => {
// No information no refinement
// No information, no refinement
}
}
}
@ -232,7 +232,7 @@ pub fn class_name_to_type_kind(name: &str) -> Option<TypeKind> {
"Boolean" => Some(TypeKind::Bool),
"List" | "ArrayList" | "Collection" | "Set" | "HashSet" => Some(TypeKind::Array),
"URL" | "URI" => Some(TypeKind::Url),
// Framework HTTP clients also listed in JAVA_HIERARCHY (type_facts.rs)
// Framework HTTP clients, also listed in JAVA_HIERARCHY (type_facts.rs)
// for subtype resolution. Both locations needed: this function is called
// directly by the constraint solver, while the hierarchy provides
// is_subtype_of() for instanceof checks.

View file

@ -156,7 +156,7 @@ fn valuefact_widen_stable_bound() {
b.lo = Some(0);
b.lo_strict = true;
let w = a.widen(&b);
assert_eq!(w.lo, Some(0)); // stable preserved
assert_eq!(w.lo, Some(0)); // stable, preserved
assert!(w.lo_strict);
}
@ -357,7 +357,7 @@ fn pathenv_max_refine_per_block() {
let v = SsaValue(0);
// Reset counter
env.reset_refine_count();
// Refine many times should stop after MAX_REFINE_PER_BLOCK
// Refine many times, should stop after MAX_REFINE_PER_BLOCK
for _ in 0..(MAX_REFINE_PER_BLOCK + 50) {
let mut f = ValueFact::top();
f.null = Nullability::NonNull;