mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-21 20:18:06 +02:00
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:
parent
4db0805de6
commit
a438886217
291 changed files with 9485 additions and 3851 deletions
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue