Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

clone-drift

Flags a near-duplicate function whose AST has diverged from the strict majority of its siblings in the same scope. The contradiction is between “looks like a copy” and “doesn’t actually do what its copies do”.

PropertyValue
Detector IDclone-drift
LanguagesRust, Python
IEEE 1044-2009 classLogic
Default severityWarning
Specdocs/spec/clone-drift-v0.md

What it flags

#![allow(unused)]
fn main() {
fn poll_a(cx: &mut Context<'_>) -> Poll<()> {
    if let Some(x) = inner_a(cx) { return Poll::Ready(x); }
    Poll::Pending
}
fn poll_b(cx: &mut Context<'_>) -> Poll<()> {
    if let Some(x) = inner_b(cx) { return Poll::Ready(x); }
    Poll::Pending
}
fn poll_c(cx: &mut Context<'_>) -> Poll<()> {
    if let Some(x) = inner_c(cx) { return Poll::Ready(x); }
    Poll::Pending
}
fn poll_d(cx: &mut Context<'_>) -> Poll<()> {
    // <- flagged: drifted from the other three
    if inner_d(cx).is_some() { break; }
    Poll::Pending
}
}

poll_a / poll_b / poll_c normalise to identical AST node sequences and form a dominant partition of size 3. poll_d belongs to the same Jaccard ≥ 0.5 cluster but has a singleton normalised form that differs by a small surgical edit (return Poll::Ready(x)break). The finding points at poll_d; related lists the three canonical-form siblings.

How the gates compose

The detector applies four ordered gates so the textbook “one of N copies missed an update” pattern fires while designed N-variant families and library-shape clusters do not:

GatePurposeDefault
MIN_FN_TOKENS = 22drop trivially short functionsconstant
SIMILARITY_THRESHOLD = 0.5pairwise Jaccard for cluster membershipconstant
F5b scope-bounded clusteringclusters never cross crate / package boundariesprovenance-aware
F5c-i strict-majoritydominant partition must be > 50% of the clusterconstant
F5c-ii NEAR_DUPLICATE_THRESHOLD = 0.7drifted singleton must be near-duplicate of the dominant exemplarconstant
F5d-i multi-singleton suppression≥ 2 singleton partitions ⇒ designed family ⇒ silentconstant
F5d-ii length-imbalance × weak-dominant gatehigh length asymmetry under weak canonical evidence ⇒ silentconstants
F5d-iii small-cluster floorMIN_GROUP_SIZE cluster at the resolution limit ⇒ silentconstants

Gates F5b / F5c / F5d landed across P-6 and P-7 in response to the wild β corpus residuals. Wild β clone-drift FP count is 0 in both Rust and Python after F5d.

Language coverage

  • Rust: grandfathered citation (Juergens et al. ICSE 2009 on inconsistent clone changes); the broader Bettenburg MSR 2009 and Krinke ICSM 2007 citations supply the F5c-ii drift gate’s grounding.
  • Python: confirmed citation. Assi, Hassan, Zou (TOSEM 2025, DOI 10.1145/3721125) replicate NiCad / SourcererCC on nine Python deep- learning frameworks. SourcererCC also ships as the Q-15 SOTA baseline comparator for clone-drift (baselines/sourcerercc/).

What it does not flag

  • Clusters smaller than MIN_GROUP_SIZE = 3.
  • Functions whose normalised AST has fewer than MIN_FN_TOKENS = 22 tokens (small utility functions whose drift signal is too noisy).
  • Functions inside impl / trait / mod blocks. Only top-level fn (Rust) and function_definition (Python) participate in v0.
  • Cross-scope siblings. The F5b scope rule keeps clustering within a single crate / package / parent-directory namespace.

Configuration

[detectors.clone-drift]
enabled = false
# Severity overrides apply at SARIF emission time (P5).
severity = "Note"

Per-language disable:

[languages.python]
suppress = ["clone-drift"]

See In-source suppressions for the per-function attribute and comment forms.