Linting
Ur lints every language it reads with the same rules. It lowers each file to one intermediate form and normalizes it, under the rules of the language version the project targets, so code that does the same thing comes out the same however it is written. Its clone engine fingerprints that normal form to find every copy, and the other rules read the same form.
Checking a project
ur check
ur check lints the directory it is given, the current one by default, and reports the findings in
the files changed since its last clean check, so a check after a small edit is quick. --all
reports the findings in every file, and --fix writes the fixes of findings that have one instead
of reporting them.
Ur caches what it learns about each file by its content, in a store every checkout shares, so later runs redo the work only for files that changed, and a copy of a checkout reuses the work done in the original.
ur lint takes files and directories and reports every finding in them, without comparing with an
earlier check:
ur lint src tests
ur lint --rule semantic-clone .
--rule runs only the rules it names, and --rules lists every rule with the instruction it gives.
--format sarif --output ur.sarif writes the report as SARIF, for code scanning. Both commands exit
with status 1 when they report a finding, so either can fail a CI job.
The rules
The rules fall into groups:
- Code: code that does nothing, says the same thing twice, or misuses what it
touches, such as a second copy of a function (
semantic-clone), an expression that always gives the same answer (trivial-expression), and environment variables read all over a project (stray-env-var). - Tests: tests that cannot fail, or whose failure would not say where to look, after the advice of Google’s Testing Blog. Each finding links to the post its rule is based on.
- Lean proofs: theorems that rest on proofs Lean has not checked.
- Urscal grammars: grammar rules that never match, and conditions that change nothing.
- Text: optional rules over a file’s text, such as long files, deep nesting, TODO markers, and emoji.
- Opportunities: code of one shape written out by hand in many
places, which
ur reportlists.
Each rule’s page says what it reports and why, whether it can fix its findings, and shows an example with what Ur prints for it.
Optional rules
Some rules are off by default, for the projects that want them: the text rules, no-comments, and
hand-written-dual. A project turns one on in its ur.toml, with no-comments = true under
[lint] or a table for a text rule, such as [lint.long-file], as the
configuration reference gives. --rule runs one for a single run:
ur lint --rule stray-todo --rule emoji .
Fixing
ur check --fix and ur lint --fix write the fixes of the findings that have one, such as an
expression replaced with its answer, then report the findings left. ur lint --undo puts back the
files the last --fix changed, except those changed since.
Accepting a finding
An ur-allow comment above a finding’s location accepts it, with an optional reason after the
rule’s name:
# ur-allow: semantic-clone kept apart until the old API is removed
def legacy_total(items):
...
ur lint --suppress writes such a comment above each location of every finding left, which accepts
a project’s findings as they stand so that only new ones are reported. ur lint --suppressions
lists the findings the comments accept, and Ur reports a comment that no longer suppresses anything.
Opportunities
ur report
ur report lists code that could be shared, ranked by how many lines sharing it would save: blocks
that differ only in a few names or literals, recursive walks written out case by case, the same
nested constructor calls at many sites, the same list of names in several places, and many
ur-allow comments on code of one shape. The report is advisory and never fails.
Coding agents
ur init makes ur check each coding agent’s stop hook, so an agent keeps working until the check
is clean. Coding agents says how.