Skip to main content

WorkspaceDiagnosticKind

Enum WorkspaceDiagnosticKind 

Source
pub enum WorkspaceDiagnosticKind {
Show 36 variants UndeclaredWorkspace, MalformedPackageJson { error: String, }, GlobMatchedNoPackageJson { pattern: String, }, MalformedTsconfig { error: String, }, TsconfigReferenceDirMissing, MalformedPnpmWorkspaceYaml { error: String, }, SkippedLargeFile { size_bytes: u64, }, SkippedMinifiedFile { size_bytes: u64, }, SkippedSourceDotdir, SourceReadFailure { error: String, }, SourceParseDegraded { error_count: u32, panicked: bool, }, BunLockbOverrideResolutionSkipped, BunLockOverrideResolutionSkipped, PnpmLockOverrideResolutionSkipped, NpmLockOverrideResolutionSkipped, BunResolutionsShadowedByOverrides, PnpmWorkspaceOverridesIgnored { cause: PnpmWorkspaceOverridesIgnoredCause, }, NodeModulesMissing, BoundariesNotConfigured, RulePacksNotConfigured, ExcludedByDefaultIgnore { pattern: String, file_count: u32, directory_count: u32, }, NoSourceFilesAnalyzed { excluded_file_count: u32, }, FileScoresUnavailable { error: String, }, HotspotsSkipped { cause: String, }, ShallowClone { ownership_requested: bool, }, UnpinnedClock, OwnershipUnavailable { cause: String, error: String, }, TrendSnapshotUnreadable { error: String, }, TrendGroupBaselineUnavailable { cause: String, }, PluginConfigUnreadable { plugin: String, key: String, reason: String, }, PluginEffectNotModeled { plugin: String, key: String, reason: String, }, CoverageAutoDetected, FlagAgeShallowClone, FlagAgeUnavailable { cause: String, }, IgnoreDependenciesGlobUnmatched { pattern: String, }, IgnoreFindingsPatternUnmatched { pattern: String, },
}
Expand description

Why a workspace-discovery candidate was rejected, or why a sibling directory looked workspace-like but was not declared.

Wire-format names are kebab-case so JSON consumers (CI integrations, MCP agents, LSP clients) get a stable, language-neutral identifier.

Variants§

§

UndeclaredWorkspace

A directory contains package.json but is not declared as a workspace in package.json workspaces, pnpm-workspace.yaml, or tsconfig.json references. Surfaced by find_undeclared_workspaces.

§

MalformedPackageJson

A declared workspace’s package.json failed to parse. The directory is dropped from discovery, but analysis still proceeds (degraded).

Fields

§error: String

serde_json parse error text.

§

GlobMatchedNoPackageJson

A workspace glob pattern matched a directory that contains no package.json. Honors the extended skip list and ignorePatterns before emitting.

Fields

§pattern: String

The glob pattern that matched the directory.

§

MalformedTsconfig

The root tsconfig.json, or a config in its extends chain, exists but failed to parse. path names the file that failed. Fallow ignores the project references, path aliases and compiler options of that file.

Fields

§error: String

JSONC parse error text.

§

TsconfigReferenceDirMissing

tsconfig.json lists a references[].path that does not point to an existing directory.

§

MalformedPnpmWorkspaceYaml

pnpm-workspace.yaml exists but failed to parse as YAML. The catalog checks are skipped and dependency-override analysis proceeds without the file’s entries (degraded) until the syntax is fixed.

Fields

§error: String

YAML parse error text.

§

SkippedLargeFile

A source file was skipped at discovery because it exceeds the configured per-file size limit (--max-file-size / FALLOW_MAX_FILE_SIZE, default 5 MB). The file is never read, parsed, or analyzed, guarding against the out-of-memory blowup a single multi-MB generated/vendored/bundled file causes (issue #1086). Surfaced by source discovery, not workspace discovery, but shares this channel so the skip is visible in workspace_diagnostics[] on fallow dead-code / dupes / health JSON.

Fields

§size_bytes: u64

On-disk size of the skipped file in bytes.

§

SkippedMinifiedFile

A large JavaScript bundle was skipped at discovery because it appears to be minified generated output. The file is never parsed or analyzed, guarding against sub-limit bundles that can still create very large ASTs and extraction payloads (issue #1086). Use --max-file-size 0 when the bundled file really should be analyzed.

Fields

§size_bytes: u64

On-disk size of the skipped file in bytes.

§

SkippedSourceDotdir

A dot-prefixed directory was not traversed by source discovery even though it contains at least one source file the project has not excluded. Hidden directories are skipped by default apart from a small convention allowlist (.storybook, .vitepress, .well-known, .changeset, .github) and the directories an active framework plugin or a package.json script reference contributes, so files inside are never parsed and their imports and exports are invisible to every analysis. A file, export or dependency that only the directory uses can be reported as unused. A !<dir>/** entry in ignorePatterns adds the directory to traversal (issue #2452). Or add the file to entry, the export to ignoreExports or the dependency to ignoreDependencies to stop that false positive, or add the directory to ignorePatterns to silence this (issue #461). Running fallow with --root against the directory analyzes it on its own and does not fix the main run (issue #2797).

“Not excluded” is measured the way the run measures it: a directory whose contents are gitignored, or excluded by ignorePatterns, or (on a --production run) excluded as test or story files, never earns this diagnostic, because the advertised remedies would find nothing there either. Generated tool output and non-git VCS metadata are excluded by name.

The advisory is best-effort and bounded: one run inspects a fixed number of skipped directories with a fixed I/O budget, in sorted path order, so a pathological tree yields a deterministic prefix rather than an unbounded array or an unbounded scan. The stderr note says “at least” when a ceiling bound the run.

Surfaced by source discovery, not workspace discovery, but shares this channel so the skip is visible in workspace_diagnostics[] on fallow dead-code / dupes / health JSON.

Unlike the two skipped-file kinds beside it, this one is CAPPED. To bound the directory reads the check costs, a run classifies at most 64 candidate directories and spends at most 1024 directory entries across all of them, so on a project that exceeds either ceiling the array is a prefix of the skipped directories rather than all of them, and the stderr note says “at least N”. No measured repository comes close to either ceiling. A consumer needing an exact total should run fallow with --root against the tree rather than infer one from this array.

§

SourceReadFailure

A source discovered with a stable FileId could not be read before parsing. Analysis continues with the remaining sparse module IDs and reports the underlying filesystem or UTF-8 error.

Fields

§error: String

Filesystem or UTF-8 decoding error from read_to_string.

§

SourceParseDegraded

A source file was read but parsed with diagnostics, so the module extracted from it may be missing imports, exports, or references after the first error. Analysis proceeds with the partial module, which is why this is reported: an import the parser never saw credits nothing, and its target can surface as a confident unused-file or unused-export finding with a delete-file or remove-export action on it.

Recorded by the parse stage, alongside source-read-failure, and never used to withhold a finding. oxc reports recoverable errors for valid syntax newer than the parser as well as for genuinely broken files, so gating findings on this would mute real results project-wide instead of just the affected file.

Fields

§error_count: u32

Number of parser diagnostics reported for the file.

§panicked: bool

true when the parser abandoned the file instead of recovering, so the extracted module is a fragment at best.

§

BunLockbOverrideResolutionSkipped

Dependency-override resolution was skipped because bun’s legacy binary bun.lockb sits next to this package.json, fallow cannot read the binary format, and no parseable text lockfile was found to use instead: no bun.lock that parses, and no readable pnpm-lock.yaml, package-lock.json, or npm-shrinkwrap.json. A yarn.lock is never consulted (yarn ignores overrides), so it does not prevent the skip either. The manifest declares overrides, so the unused-dependency-overrides check would otherwise have run; without resolution ground truth it would flag every transitive-only pin, so no unused-override findings are reported at all (issue #2358). Surfaced by the override analysis, not workspace discovery, but shares this channel so the skip is visible in workspace_diagnostics[] JSON and as a stderr warning.

§

BunLockOverrideResolutionSkipped

Dependency-override resolution was skipped because bun’s text bun.lock exists but could not be parsed and no readable pnpm or npm lockfile was available as independent resolution ground truth.

§

PnpmLockOverrideResolutionSkipped

Dependency-override resolution was skipped because pnpm-lock.yaml exists but could not be parsed, for example because it still holds unresolved merge-conflict markers, and no other parseable lockfile was available as independent resolution ground truth.

§

NpmLockOverrideResolutionSkipped

Dependency-override resolution was skipped because package-lock.json or npm-shrinkwrap.json exists but could not be parsed, for example because it still holds unresolved merge-conflict markers, and no other parseable lockfile was available as independent resolution ground truth.

§

BunResolutionsShadowedByOverrides

A bun manifest declares both overrides and a non-empty resolutions object. Bun applies overrides and ignores resolutions, so fallow reports the shadowed configuration without offering removal advice.

§

PnpmWorkspaceOverridesIgnored

The declared pnpm version ignores the overrides section of pnpm-workspace.yaml. pnpm does not print a warning. Fallow reports no findings for the ignored entries. cause tells why pnpm ignores the section.

Fields

§cause: PnpmWorkspaceOverridesIgnoredCause

Why pnpm ignores the section. The cause decides the remedy.

§

NodeModulesMissing

The project has no node_modules directory and is not a Deno project that legitimately runs without one. Analysis proceeds, but three things degrade silently: package exports and conditional exports cannot be read, so imports into a dependency’s subpaths resolve less precisely; framework plugins that activate on an installed package stay inactive, so their entry points and path aliases are missing; and a dependency’s installed shape cannot be inspected, so type-only dependency classification falls back to declaration-based heuristics.

Recorded once per run by the source walk, anchored at the missing node_modules directory so the reported path is a real location rather than the empty string a root-anchored diagnostic would render. This used to be a bare tracing::warn! duplicated in two pipelines, so it never reached JSON output and never reached fallow doctor, which reported pass on a tree that had never been installed.

§

BoundariesNotConfigured

boundaries is empty while boundary-violation is not off, so the boundary detector never ran. Its summary counters are therefore structurally zero and say nothing about the project.

This is the UNCONFIGURED zero, not the user-chosen one: a project that sets boundary-violation: off asked for silence and can see that choice in fallow config. A project that left boundaries empty cannot distinguish “no violations” from “nothing was measured”.

§

RulePacksNotConfigured

rulePacks is empty while policy-violation is not off, so the policy detector never ran and its summary counters are structurally zero. The unconfigured counterpart of Self::BoundariesNotConfigured.

§

ExcludedByDefaultIgnore

One of fallow’s built-in discovery ignore patterns (**/dist/**, **/build/**, **/coverage/**, and the four minified-bundle globs) removed at least one candidate source file from this walk. The files are never read, so their imports and exports are invisible to every analysis, and until issue #2638 the drop was completely silent: pointing fallow at a directory a built-in pattern matches returned a clean report with exit 0 and nothing said why.

**/node_modules/** is carved out and never appears in pattern: installed dependencies are not the first-party source this diagnostic is about, and a project that does not gitignore them would get a five-figure count with no useful remedy. **/.git/** cannot fire, because hidden directories are not traversed.

One entry per pattern, never per file or per directory, so the array grows by at most the number of built-in patterns on a project of any size. path anchors at the matched directory holding the most excluded files for that pattern, ties broken by the lexicographically first path, so two runs on one tree report the same location. On a nested match it is the DEEPEST segment the pattern matched (build/tools/build, not build), because that is the directory the --root remedy names and re-rooting at a shallower one would leave a matching segment behind. That directory is the largest group and not a majority: a flat monorepo can spread ten excluded files over ten sibling dist/ directories and every one of them is then “the largest”. file_count spans all of them, and directory_count says how many there were, so a reader can tell a single tree from a scattered one without a directory list in the payload.

Three properties of the population are load-bearing and easy to misread:

  • Gitignored trees count zero. Source discovery honors .gitignore, .git/info/exclude, and the global gitignore, and prunes those directories before this check runs. The honest reading is “candidate source files git did not already hide and a built-in pattern then dropped”, which is why a repository that gitignores its own dist/ never sees this diagnostic.
  • A user ignorePatterns entry is not a surprise. The compiled ignore set is the union of ignorePatterns and the built-ins, so a file both matched was an explicit project choice and is attributed to no pattern here. A ! entry in ignorePatterns lifts a built-in for the paths it matches (issue #2940), and a lifted file is discovered, so it is never counted here.
  • The remedy depends on the pattern’s shape. A directory-shaped built-in (**/dist/**) is matched against the path relative to the run root, so re-rooting inside the matched directory removes the matched segment and the files become visible: the message advertises fallow --root <dir>. A file-shaped built-in (**/*.min.js and the three other bundle globs) matches on the file name and keeps matching at any root, so the message says so and points at renaming instead of handing out a command that provably does nothing.

Deliberately NOT one of the Self::source_never_analyzed kinds. These exclusions are the product’s designed behavior on generated output, not a degraded run: answering true would attach IncompleteFileAnalysis and IncompleteImportGraph caveats to findings on nearly every project that keeps a non-gitignored dist/ or coverage/, and make fallow fix withhold delete-file and remove-export actions project-wide.

Fields

§pattern: String

The built-in glob that matched, verbatim (for example **/build/**).

§file_count: u32

Candidate source files this pattern excluded in this walk, across every directory it matched, not just the one path anchors at. Exact: the walk counts each excluded candidate once.

§directory_count: u32

Distinct directories this pattern matched at, path included, and not the number of directories that held the files. A directory-shaped pattern (**/dist/**) matches at the directory it names, so an excluded subtree counts once however many nested directories inside it held source: a dist/ holding files in three sub-directories reports 1. A file-shaped pattern (**/*.min.js) has no directory to collapse to and counts each matched file’s own parent. Exact either way, and anything above 1 says path names one matched location out of several.

§

NoSourceFilesAnalyzed

The walk finished with no source file to analyze at all, so every finding count this run reports is zero because nothing was measured rather than because the project is clean (issue #2686).

Distinct from Self::ExcludedByDefaultIgnore, which reports one pattern’s exclusions and is designed behavior on generated output. The alarm is not the exclusion, it is having nothing left afterwards, and that condition also fires with no exclusion at all: a docs-only repository, a workspace member with no TypeScript, or a path filter that matched nothing. excluded_file_count names the built-in-ignore contribution so the common cause is still attributable, and is 0 when no built-in pattern took part.

This is the kind a CI consumer reads to tell “measured zero” from “measured nothing”: the human report has said so since 3.26.0, but only in human format and only under the built-in-ignore cause, so --quiet --format json saw a clean green either way.

Fields

§excluded_file_count: u32

Candidate source files the built-in ignore patterns removed from this walk, summed across every pattern. 0 when the walk found no candidate to exclude in the first place.

§

FileScoresUnavailable

Per-file health scoring failed, so the score list is empty and the scored-file count is 0 because nothing was measured rather than because the project has no files worth scoring. Every score-derived number (the average maintainability index, the refactoring targets, the hotspot complexity half) is then structurally zero (issue #2689).

Fields

§error: String

Scoring error text.

§

HotspotsSkipped

Churn-based hotspot analysis was skipped, so the hotspots, churn and ownership sections report nothing at all. The remaining health sections are unaffected.

Fields

§cause: String

Which input stopped it, as a kebab-case token: not-a-repository, no-commits, invalid-since or churn-file-unreadable. The set is open.

The cause decides the remedy, which is why it is on the wire: a run outside a repository is fixed by running fallow inside one, a branch without a commit by committing, a malformed --since by respelling the flag, and a churn file that changed under the run by rerunning it. A consumer reading only the kind would offer the first remedy for all four.

§

ShallowClone

The repository is a shallow clone, so churn is measured over the fetched history only and every hotspot figure is incomplete.

Fields

§ownership_requested: bool

true when the run also asked for ownership attribution, which a shallow clone skews further by inflating single-author dominance.

§

UnpinnedClock

No commit timestamp was available, so churn recency and ownership staleness were measured against the wall clock and drift between two runs over the same commit.

§

OwnershipUnavailable

Ownership attribution was requested but its inputs did not load, so hotspot entries carry degraded or absent owner signals.

Fields

§cause: String

Which input failed, as a kebab-case token: invalid-bot-pattern or codeowners-parse-failed. The set is open.

§error: String

Underlying error text.

§

TrendSnapshotUnreadable

A saved health snapshot could not be read or parsed, so the trend is computed over fewer snapshots than the project has on disk and a direction can flip on the missing point alone.

Fields

§error: String

Filesystem or JSON error text.

§

TrendGroupBaselineUnavailable

A grouped health run asked for a trend, but the baseline snapshot holds no group data that matches this run, so the groups carry no trend. The project trend is not affected. path names the baseline snapshot.

Fields

§cause: String

Why the groups have no baseline, as a kebab-case token: snapshot-has-no-groups or grouped-by-mismatch. The set is open.

§

PluginConfigUnreadable

A framework plugin read a build config and could not read one of its keys in full, so part of what the key declares never reached the analysis. path names the config file.

The reader is syntactic, so a key whose value is computed at build time is invisible to it: a Module Federation exposes: makeExposes() or a remotes map spread from an environment module declares entries this run does not know about. The consequence is a finding, not a missing number: an unread exposes target is not registered as an entry point and its file can surface as unused-file, and an unread remotes alias is not treated as provided by a remote container and its import can surface as an unlisted dependency.

Recorded by the plugin stage, which runs before analysis and is not cached, so the entry is present on a warm cache too. It used to be a bare tracing::warn! from inside the plugin, so it reached no envelope and no CI consumer (issue #2736).

A source file that calls the Module Federation runtime API gets the same entry: path names the source file, key names the runtime function (registerRemotes, loadRemote, init, createInstance) and reason is dynamic-argument when the call receives a value that is not a static literal. The analysis records it from the facts of the parse, which a warm cache restores (issue #2795). A .vue or .svelte file gets it for a call in its <script> blocks (issue #2876).

Fields

§plugin: String

The plugin that read the config, as it labels itself: module-federation for a standalone module-federation.config.*, or the bundler plugin (webpack, rspack, rsbuild, vite) that read the same options inline from its own config.

§key: String

The config key that was present and not fully readable (exposes, remotes), or the Module Federation runtime function whose argument was not readable (registerRemotes, loadRemote, init, createInstance). The set is open.

§reason: String

Why it could not be read, as a kebab-case token: not-object-literal, array-form, spread, unreadable-entries, unrecognized-call, import-target-unreadable or dynamic-argument. The set is open.

The reason decides the remedy, which is why it is on the wire: a value that is not an object literal is fixed by writing one, while unreadable entries are fixed by naming those entries in the config option the message points at.

§

PluginEffectNotModeled

A framework plugin read a config key it understands, does not model that key’s effect, and therefore stood a modeled default down. path names the config file.

A file can also be the path: a Nuxt file that reads #components or #imports in a way fallow cannot narrow to names, such as a spread of a namespace import, has key set to that module and reason set to key-effect-not-modeled. Every name of the module then counts as used.

The Nuxt auto-import gate is the case this exists for. With autoImports enabled fallow drops the Nuxt convention entry patterns so a genuinely unreferenced convention file is reported, and a components: or imports: block whose effect it cannot model keeps them, which silently costs the user the findings they opted in for.

Deliberately NOT one of the Self::warns_on_stderr kinds. Nothing was lost that the run could have measured: the patterns stayed, so findings are suppressed rather than invented, and a project in this state would otherwise warn on every run forever with “write different config” as the only remedy, which is the reason boundaries-not-configured is off stderr as well.

Fields

§plugin: String

The plugin that read the config, as it labels itself (nuxt).

§key: String

The config key whose effect is not modeled (components, imports), or the virtual module a file reads (#components, #imports). The set is open.

§reason: String

Why the effect is not modeled, as a kebab-case token: key-effect-not-modeled when the key’s own value is the reason, config-property-unreadable when a top-level property of the same config file could not be read statically, so no surface in it can be classified at all. The set is open.

§

CoverageAutoDetected

Test coverage was auto-detected on disk rather than passed with --coverage, and path names the file that fed the CRAP scores.

Deliberately NOT one of the Self::warns_on_stderr kinds: nothing degraded, the run measured exactly what it found. It is provenance, and it is on the wire because a score computed against a file the user did not name is not reproducible and nothing else says which file it was.

§

FlagAgeShallowClone

fallow flags --retirement asked for flag age, but the repository is a shallow clone. Blame and pickaxe see the fetched history only, so every age would be too young. The report gives no age.

§

FlagAgeUnavailable

fallow flags --retirement asked for flag age, but git history is not available. The report gives no age.

Fields

§cause: String

Why no history is available, as a kebab-case token: not-a-repository or no-commits. The set is open.

§

IgnoreDependenciesGlobUnmatched

A glob in ignoreDependencies matched no dependency that a package.json of the project declares, so the entry had no effect in this run. The usual cause is a typo in the scope or the name.

The dead-code run records it only when the unused-dependency check ran and the run reports dependency findings. A run filtered to other issue types (--unused-files) or scoped to files (--file) does not record it. An exact-name entry never produces it. path is the project root.

Not a degraded run: the analysis measured what it was asked to, so degrades_analysis is false and no finding carries a caveat.

Fields

§pattern: String

The ignoreDependencies entry, as written in the config.

§

IgnoreFindingsPatternUnmatched

A pattern in ignoreFindings matched no finding of the dead-code run, so the entry had no effect in this run. The usual cause is a typo in the path.

The pattern is compared with every finding the analysis produced, before issue-type filters and scope filters, so a filtered run does not make a pattern look unused. path is the project root.

Not a degraded run, for the same reason as Self::IgnoreDependenciesGlobUnmatched.

Fields

§pattern: String

The ignoreFindings entry, as written in the config.

Implementations§

Source§

impl WorkspaceDiagnosticKind

Source

pub const fn id(&self) -> &'static str

Stable kebab-case identifier used in dedupe keys and tracing payloads.

Source

pub fn unmatched_config_pattern(&self) -> Option<(&'static str, &str)>

The config setting and the entry of an unmatched config pattern (ignoreDependencies or ignoreFindings), or None for every other kind.

The human note, the SARIF configuration notifications and the Markdown section all read the unmatched patterns through this one accessor.

Source

pub const fn warns_on_stderr(&self) -> bool

Whether this diagnostic is worth a tracing::warn! line on stderr, on top of its permanent entry in workspace_diagnostics[].

A warning is for a run whose RESULTS are degraded: something the user installed, wrote, or expected did not reach the analysis. The two unconfigured-check kinds are not that. They fire in the product’s default state, on every project that never opted into boundaries or rule packs, and they will keep firing forever, because the remedy they offer is to write configuration in order to silence a warning about not having written configuration. They stay in the structured array, where a consumer that wants to distinguish “measured zero” from “measured nothing” can read them, and off the stderr surface that every other command shares.

coverage-auto-detected answers false for a third reason: it reports the provenance of an input that DID load, so a consumer sentence about a degraded run would state something untrue about it. Its own note is printed by the health pipeline.

plugin-effect-not-modeled answers false for the first reason: the config was readable and nothing the run could have measured was lost, so it would warn forever on a project whose nuxt.config fallow does not model. Its sibling plugin-config-unreadable answers true, because there a declaration the user wrote did not reach the analysis and findings can be wrong in either direction.

Source

pub const fn is_source_discovery(&self) -> bool

Whether this diagnostic is produced by SOURCE discovery (the file walk in discover_files) rather than WORKSPACE discovery (config load). Source- discovery diagnostics are APPENDED to the registry after config load, so stash_workspace_diagnostics must preserve them when it replaces the workspace-discovery set, otherwise the per-analysis config re-loads in combined-mode (fallow with no subcommand re-loads config for check, dupes, and health) wipe them before the JSON envelope is built (issue #1086).

Source

pub const fn is_source_walk_recorded(&self) -> bool

Whether this diagnostic is written by the source file WALK (discover_files), the subset of Self::is_source_discovery that a walk replaces wholesale for its root. source-read-failure is the other source-discovery kind and is NOT one of these: the parse stage records it after the walk, so it has to keep reaching consumers through the registry.

A walk-recorded entry must reach an analysis from its OWN walk’s return value. Combined mode runs the dead-code and duplication walks under rayon::join whenever a per-analysis production split stops them from sharing a file list, so a registry read answers “whichever walk wrote last” and varies between runs of the same command (issue #2366).

Source

pub const fn source_never_analyzed(&self) -> bool

Whether this diagnostic reports a source file whose contents this run never analyzed, so every import and export the file holds is invisible to the module graph.

This is the class reachability_caveats[] exists for. A file the run never read credits nothing, so the modules it imports surface as confident unused-file and unused-export findings carrying delete-file and remove-export actions, and fallow fix would otherwise apply the removal against source that still imports the target.

All four discovery-side kinds qualify, for the same reason and with the same consequence:

  • skipped-large-file and skipped-minified-file: the file is in the project tree and was never opened, so its import list is unknown.
  • skipped-source-dotdir: the directory holds at least one source file the project did not exclude, and none of them were traversed. The diagnostic is capped, so it under-reports rather than over-reports; its presence still proves unseen source exists.
  • source-read-failure: the file was discovered and then could not be read, so nothing was extracted from it at all.

source-parse-degraded is deliberately NOT one of these, though it belongs to the same family. Neither is excluded-by-default-ignore, for a different reason: that one reports designed behavior on generated output rather than a degraded run, and its own doc comment carries the argument.

source-parse-degraded: that file WAS read, so it has a module and a graph node and its reachability is observable, which lets the caveat pass narrow it: a degraded module that is itself unreachable cannot change a reachability verdict. Every kind above has no node to ask (a read failure has one with nothing extracted into it), so no narrowing is available and the caveat they raise is run-level.

The match is exhaustive on purpose: a new “the run did not see this file” kind has to be classified here, and answering true is the only wiring its findings need in order to inherit both the caveat and the fallow fix withholding that follows it.

Source

pub const fn is_analysis_stage(&self) -> bool

Whether this diagnostic is recorded by the ANALYZE stage (the dependency-catalog and override detectors) rather than by workspace or source discovery. Analysis-stage diagnostics reach the registry through record_workspace_diagnostics after config load, so stash_workspace_diagnostics must preserve them across combined-mode’s per-analysis config re-loads, and every analyze pass clears its previous entries before re-recording so a fixed cause drops out on the next run (issue #2366). The match is exhaustive on purpose: a new kind must be classified here before it compiles.

Classify a kind true ONLY when a detector reachable from the dead-code analyze pass (find_dead_code_full) re-records it, because that pass is the single clear site. A kind recorded exclusively by another stage would be cleared by the next dead-code pass and never come back.

Source

pub const fn is_health_stage(&self) -> bool

Whether this diagnostic is recorded by the HEALTH pipeline (scoring, churn, ownership, trend, coverage input resolution) rather than by workspace discovery, source discovery or the analyze stage.

Health-stage diagnostics are appended to the registry after config load, so stash_workspace_diagnostics must preserve them across combined mode’s per-analysis config re-loads, and the health run clears its previous entries before re-recording so a fixed CODEOWNERS drops out on the next run (issue #2689).

They are deliberately NOT Self::is_analysis_stage, although they share both of those properties. That predicate additionally means “the dead-code analyze pass re-records this”, and the pass clears every kind answering it on entry. Health computes file scores by running that same pass, so a health-stage kind classified there would be wiped mid-run by the analysis it is reporting on.

Source

pub const fn is_plugin_stage(&self) -> bool

Whether this diagnostic is recorded by the PLUGIN stage (framework plugins reading their own build configs) rather than by workspace discovery, source discovery, the analyze stage or the health pipeline.

Plugin-stage diagnostics are recorded after config load, so stash_workspace_diagnostics must preserve them across combined mode’s per-analysis config re-loads, and each plugin run replaces the previous run’s set so a fixed config drops out on the next run (issue #2736).

They are deliberately NOT Self::is_analysis_stage, although they share both of those properties. That predicate additionally means “the dead-code analyze pass re-records this”, and the pass clears every kind answering it on entry. Plugins run in the prelude of that same pass, so a plugin-stage kind classified there would be wiped inside the run that produced it.

The match is exhaustive on purpose: a new kind must be classified here before it compiles.

Trait Implementations§

Source§

impl Clone for WorkspaceDiagnosticKind

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WorkspaceDiagnosticKind

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for WorkspaceDiagnosticKind

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for WorkspaceDiagnosticKind

Source§

impl Hash for WorkspaceDiagnosticKind

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for WorkspaceDiagnosticKind

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for WorkspaceDiagnosticKind

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for WorkspaceDiagnosticKind

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<'a, T> FromIn<'a, T> for T

Source§

fn from_in(t: T, _: &'a Allocator) -> T

Converts to this type from the input type within the given allocator.
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<'a, T, U> IntoIn<'a, U> for T
where U: FromIn<'a, T>,

Source§

fn into_in(self, allocator: &'a Allocator) -> U

Converts this type into the (usually inferred) input type within the given allocator.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.