Let FinderOptions own its path invariants - #64
Conversation
`rootPath` and `analysisRootPath` are normalized in the constructor rather than at the top of `Ciach.run`, and an assert there requires the analysis root to contain the scanned one. A library caller passing a root that doesn't now fails at construction instead of quietly getting references counted from the wrong tree. The constructor loses `const` — neither `p.absolute` nor an assert over `p.isWithin` can run in one. Nothing constructs it `const` today. bin/ciach.dart keeps its own existence and containment checks, so a bad `--analysis-root` still gives the usage message and exit 2 rather than an assertion failure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok
An assert only runs under --enable-asserts, which `dart test` sets and nothing else does: not `dart run`, and not a compiled binary. The check was inert exactly where a library caller would want it, so throw an ArgumentError instead. bin/ciach.dart validates first, so a bad --analysis-root still gets the usage message and exit 2, never this error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok
`FinderOptions` goes back to normalizing and nothing else. `Ciach.run` rejects an analysis root that doesn't contain the scanned one, before it starts the analysis server. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok
|
Now the normalization and validation of absolutne root path happens twice when using CLI. Maybe we could just remove the validation from the |
|
Both correct. Details I checked, then what I'd do about each. The duplicate containment check. Only containment is duplicated — the CLI's existence check has no counterpart in The two checks also aren't quite the same thing. The CLI's is for the person typing the command; bool analysisRootContains(String analysisRoot, String rootPath) =>
p.equals(analysisRoot, rootPath) || p.isWithin(analysisRoot, rootPath);One definition, CLI keeps its message and exit 2,
The smallest fix that actually unifies: have Both are follow-on changes to a PR that is green and already a bit wider than the thread that spawned it. @PiotrRogulski — happy to fold either or both in here, or take them as the next PR; your call. Generated by Claude Code |
`bin/ciach.dart` builds the FinderOptions before it validates, so its checks, `--verbose` and the run all read the same normalized paths. ResolvedOptions no longer normalizes separately, and its two absolute getters are gone. `analysisRootContains` holds the rule the CLI and `Ciach.run` both enforce — the CLI for a usage message and exit 2, the finder for every other caller. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok
|
Both done in The rule has one definition. One normalization per process. So $ cd /tmp/pathdep && ciach pkgs/core --analysis-root . -v
[ 0.0s] path: /tmp/pathdep/pkgs/core (command line)
[ 0.0s] analysis-root: /tmp/pathdep (command line)Re-checked all of it after the reorder, since the validation moved below the SDK lookup: containment error, missing analysis root, missing scan path, Generated by Claude Code |
The note sat above the whole parameter list, so "both paths" had no antecedent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok
`String.absoluteNormalized` replaces the fourth hand-rolled `p.normalize(p.absolute(…))`; on the nullable analysis root it also collapses the ternary into `?.`. File discovery loses its copy outright — it reads `FinderOptions.rootPath`, which the constructor has already normalized. The CLI's analysis-root checks become an if-case, matching how the rest of the codebase unwraps a nullable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok
Follow-up to the normalization thread on #61.
FinderOptionsmakesrootPathandanalysisRootPathabsolute and normalized,so nothing else in the process normalizes them.
Ciach.runrejects an analysisroot that doesn't contain the scanned one, before it starts the analysis server.
That check is the part that isn't only tidying. It lived only in
bin/ciach.dart, so a library caller could pass an analysis root that doesn'tcontain the scanned path and get references counted from the wrong tree, with
nothing to say so.
One normalization, one rule
Prompted by Komoszek's review:
bin/ciach.dartbuilds theFinderOptionsbefore it validates, so its checks,--verboseand the run all read the same paths.ResolvedOptionsno longernormalizes separately —
absoluteRootPathandabsoluteAnalysisRootPatharegone.
analysisRootContainsinlib/src/paths.dartholds the containment rule. TheCLI and
runboth call it: the CLI so a typo gets a usage message and exit 2instead of the catch-all's stack trace,
runfor every other caller.--verbosenow reports what the run uses, relative input included:Why
runand not the constructorThe constructor only normalizes; validation sits where the roots are used. It
also throws rather than asserting, which matters more than it looks: asserts only
run under
--enable-asserts, whichdart testsets and nothing else does.An assert would have passed its own tests while doing nothing for
dart run ciach, for a compiled binary, or for the library consumer it exists to protect.The
constcostThe constructor is no longer
const—p.absoluteisn't a constant expression.Nothing in the repo constructs
FinderOptionsasconst; it is exported frompackage:ciach/ciach.dart, so this is a public break, pre-1.0.Testing
2 new tests, 278 passing;
dart analyzeanddart formatclean repo-wide. Theycover relative paths coming back absolute and normalized, and a run with an
analysis root beside the scanned one throwing. An analysis root equal to the
scanned one is already covered by the existing run-level test.
Because the CLI's validation moved below the SDK lookup, I re-ran each path by
hand: the containment error, a missing analysis root, a missing scan path,
--verbose, and a working run.Worth noting for a separate change:
assert(concurrency > 0)in the sameconstructor is inert for the same reason, and I left it alone rather than widen
this PR.
🤖 Generated with Claude Code
https://claude.ai/code/session_01SYSJaEbjNiw5KAMXty4Cok