Skip to content

Confidence

Parsing gives syntax, not meaning. Several rules need more than syntax, and sometimes the analyzer cannot get it.

What is out of reach

slopometer builds its own project-local name resolution. In Rust that is the module tree from mod declarations and file layout, use statements including aliases, globs and re-exports, and a table of which types implement which traits. In TypeScript it is what each module exports and which declarations implement which interfaces. In C it is which names a header declares, which is the author stating the public surface rather than the analyzer inferring it. That is ordinary work, not research, and it covers more than "no semantics" suggests.

What stays out of reach is types, borrow analysis, macro-generated items, and anything inside a dependency.

TypeScript resolves the export graph. Starting from the entry point the package manifest names and following forwards between analyzed files, the published surface is the set of names an importer can actually write. A declaration exported only from an internal module is not on it, whatever that module says.

That answer can be stood behind, so findings resting on it reach the score. It replaced a flat list of every module-level export, which covered the whole internal surface: across the TypeScript corpus that cut doc/missing-on-public-item from 3,735 findings to 1,352, and raised the share that could be confirmed from under one percent to forty-two.

Two things still leave it uncertain. A package with no readable manifest has no entry point to walk from, so the flat export list is the best answer available and it is reported as approximate. And a forward of everything from a specifier the analyzer cannot follow, such as a package, could put any number of names on the surface, so the surface stops being known.

C is where this costs most. Nothing is preprocessed, so a conditional region is read but never evaluated, and a macro is not read at all. Findings inside a conditional region are unconfirmed because whether that code is built was never established, and any file-scope macro puts every count of callers or declarations in doubt. Measured across twenty widely deployed C projects, 97.6 percent of C findings came back unconfirmed and nineteen of the twenty carried no score. That is recorded in calibration rather than worked around.

A category now carries no score at all when fewer than a tenth of its findings are confirmed. A score is computed from confirmed findings alone, so a category the analyzer could barely read would otherwise score near perfect while describing nothing.

Two things put a TypeScript finding out of reach in the same way a macro does in Rust. An export * from a package the analyzer never reads adds names to a module that cannot be enumerated, so findings anchored there are unconfirmed. And where a linter configuration exists only as JavaScript, whether the project already enforces a rule cannot be established without running its code, which this analyzer will not do.

Silence would have been the wrong answer

The first design had detectors stay quiet whenever they could not prove a precondition. That protects the score and throws away real information, and it makes the semantic rules feel broken rather than careful.

Instead, every finding carries a confidence:

  • Confirmed means the detector established every fact its rule depends on.
  • Unconfirmed means it matched on evidence it could not fully establish.

Only confirmed findings affect scores. Unconfirmed ones stay in the report, hidden behind --show-unconfirmed, with the default output stating how many were omitted.

When a finding is downgraded

A finding is reported as unconfirmed when it is anchored in a file the parser had to recover from, when it sits inside a macro region the analyzer could not expand, or when the modules enclosing it were never seen being declared.

That last one is the interesting case. A crate whose lib.rs body lives inside a macro leaves the analyzer unable to read the visibility of the modules it declares, so whether an item is reachable becomes a guess. The guess is reported, and kept out of the arithmetic.

Reporting on a macro is not the same as reporting inside one

A finding anchored exactly at a macro invocation is reporting on the macro itself, not on anything the macro hides, so it stays confirmed. Without that distinction a rule about panic! could never be confirmed, since panic! is itself a macro.

Precision over recall

The standing rule underneath all of this: a rule that fires on correct code costs more trust than a rule that misses a real problem costs value.

Missing a finding is the cheap failure. Rules that would need types or borrows were removed from scope rather than shipped unable to fire, and rules that measured their own imprecision were switched off rather than left to erode confidence in everything else.

Dual licensed under MIT or Apache-2.0.