# Linter

## Hvad det er

En linter er et program, der læser kildekode og melder de steder, der ligner
fejl, uden at bygge programmet eller køre det. Hver melding nævner en regel, en
fil og en linje: en variabel, der tildeles og aldrig bruges, en sammenligning,
der altid er sand, en shell-variabel uden anførselstegn, en formatstreng bygget
af brugerinput.

Navnet kommer fra lint, et tjekværktøj til C-programmer, og NetBSD leverer
stadig et. Dets manual beskriver opgaven som at finde de træk ved
C-programfiler, der sandsynligvis er fejl, ikke er portable eller er spild, og
tilføjer, at det typetjekker strengere end traditionelle C-oversættere fra før
C90. Det arbejder i to gennemløb: først tjekkes hver fil som en enhed for sig,
derefter krydstjekkes resultaterne for uoverensstemmelser mellem filerne.

De fleste sprog har nu deres egne lintere, ofte flere:

- **ESLint** til JavaScript.
- **Ruff** til Python.
- **ShellCheck** til shellscripts. Værktøjets erklærede mål spænder fra
  begynderes syntaksfejl til subtile faldgruber, der kan få et fungerende script
  til at fejle under fremtidige forhold.
- **Stylelint** til CSS, **markdownlint** til Markdown og **yamllint** til
  YAML.

Enhver linter er bygget af regler. Hver regel har en identifikator, for eksempel
F841 i Ruff eller SC2059 i ShellCheck, og en alvorsgrad. En konfigurationsfil
afgør, hvilke regler der er slået til, og hvor alvorlig hver enkelt er, og mange
regler har en automatisk rettelse.

En formatter omskriver layout og har ingen holdning til betydning; en
typetjekker ræsonnerer om, hvad hver værdi er. En linter ligger imellem og ser
på, hvordan koden er skrevet: misbrug af sproget, risikable mønstre og de
konventioner, et projekt har aftalt.

## Hvorfor det betyder noget

En linter læser hver gren, også den fejlhåndtering, der kører én gang om året,
og som ingen test når. Den tager sekunder, så den kan køre ved hvert commit, og
et fund er billigst, mens linjen skrives.

Regler afgør en diskussion én gang. Om en bar `except:` er acceptabel, eller om
hver shell-variabel skal i anførselstegn, besluttes i konfigurationen og
anvendes derefter ens på hver forfatter og hver review.

Exitkoden er grænsefladen mellem en linter og det, der kører den. ShellChecks
README siger, at værktøjet bruger exitkoder på den kanoniske måde, så et build
kan kalde det som én kommando mere. Ruff og ESLint dokumenterer den samme
tredelte konvention: 0, når tjekket er rent, 1, når det fandt overtrædelser, og
2, når selve værktøjet fejlede, for eksempel på en ugyldig konfiguration. En
port kan så skelne »koden er forkert« fra »tjekket er i stykker«, og begge bør
stoppe den.

Alvorsgraden afgør, hvilke fund der når exitkoden. ESLint afslutter med succes,
når det finder advarsler og ingen fejl, medmindre `--max-warnings` sætter en
grænse, og lint(1) har `-w` til at behandle advarsler som fejl. Et projekt, der
vil have sine lintere til at blokere fejl og ikke bare beskrive dem, skriver
valget ned dér, hvor hver kørsel bruger det:

```sh
# hvert fund får kørslen til at fejle, også advarsler
lint -w -abcFghprSxz source.c
eslint --max-warnings 0 src/
ruff check src/
shellcheck scripts/*.sh
```

Dette site har valgt den strikse ende. Dets egen port tæller hver diagnostik som
en fejl og forbyder inline-undertrykkelser som `# noqa`, så et fund bliver
rettet ved årsagen.

## Sådan ser det ud i praksis

En linters konfiguration begynder ofte som de regler, der tilfældigvis var
stille den dag, nogen satte den op. En side vender det om:
[hver regel i en Python-linter slået til som fejl](/da/portfolio/enabled-every-python-linter-rule-as-an-error-147/).
Første kørsel gav 1.815 fund. De blev gennemgået efter kategori og ikke efter
fil, fordi kategorierne fortæller noget: ubrugte argumenter og skyggede
indbyggede navne var støj, mens kategorierne for sikkerhed, mutable
standardværdier og fejlhåndtering hver pegede på reel adfærd. De få undtagelser
har en skreven begrundelse ved linjen, og to af dem er bakket op af en test, der
fastslår den egenskab, som den frakoblede regel ville have håndhævet. Strengt
typetjek kørte ved siden af og fangede en anden slags defekt, om hvad en værdi
er snarere end hvordan den er skrevet. Prisen står der også: nogen skal foretage
en vurdering 1.815 gange.

En anden side begynder med en ubehagelig opdagelse. Sitets kvalitetsport,
toogtredive scripts og 10.242 linjer JavaScript, var den største samling kode i
repositoriet og den eneste, som hverken formatter, linter eller typetjekker
læste. Arbejdet med at
[lægge portens egen kode under en linter](/da/portfolio/brought-the-quality-gate-code-under-a-linter-143/)
begyndte med formatteren, sat til to mellemrums indrykning for de filer, så 38
linjer ændrede sig i stedet for 2.173. Linteren gav derefter tretten reelle
fund, alle rettet og ingen undertrykt, og en typetjekker fandt to defekter, som
linteren havde ladet passere. Begrundelsen står ved siden af: en defekt i et
tjek viser sig som et tjek, der består, når det burde fejle.

Idéen virker også på prosa. En tredje side beskriver
[en linter til repositoriets egen dokumentation](/da/portfolio/put-the-documentation-under-its-own-linter-142/)
med grænser, der får noget til at fejle i stedet for at give råd. Dens otte
mekaniske politikker omfatter et linjebudget pr. fil, der kun kan skrumpe, en
regel om, at en plans status skal stemme med dens egne afkrydsningsfelter, og et
tjek af, at hver sti og hvert kodesymbol, et dokument nævner, findes i træet.
Første kørsel fandt seks planer markeret som færdige med uafkrydsede felter,
fjorten opgaver, der gater hvert commit uden at stå i noget dokument, tre døde
links, et forældreløst dokument og tre døde stier.

## Hvor det går galt

En linter fejler sjældent højlydt. Den typiske fejl er en kørsel, der afslutter
med 0, når den burde have stoppet noget.

**Fund, der aldrig ændrer exitkoden.** ESLints `--max-warnings` har -1 som
standard, hvilket slår grænsen fra, og ifølge manualen til lint(1) afslutter
krydstjekket mellem filer altid med succes. Beslut pr. regel: en fejl, der
stopper kørslen, eller slået fra, og sæt en eventuel indstilling for advarsler
som fejl i den fælles konfiguration.

**En rettelse, der melder succes.** Ruff afslutter med 0, når hver overtrædelse,
det fandt, blev rettet automatisk. En port, der kører med rettelser slået til,
reparerer kun sin egen arbejdskopi, og det commit, der kom ind, indeholder
stadig hver overtrædelse. Kør porten uden rettelser, eller tilføj
`--exit-non-zero-on-fix`, så en rettelse tæller som en fejl. Søg også efter
`--exit-zero`: med det afslutter Ruff med 0, selv når der er overtrædelser.

**Et mønster, der rammer nul filer.** En omdøbt mappe eller en flyttet
konfiguration kan efterlade en linter rettet mod en tom mængde, og at tjekke nul
filer er et rent resultat. ESLint behandler som standard en glob i
anførselstegn, der rammer nul filer, som en fejl.
`--no-error-on-unmatched-pattern` gør det til et stille bestået, og
`--pass-on-no-patterns` afslutter med 0, når der slet ikke angives filmønstre.
Behold standarden, og hold én fil med en kendt overtrædelse, som porten skal
melde.

**Et regelsæt valgt efter, hvad der var stille.** En konfiguration, der er
bygget ved at slå alt fra, der gav udslag den første dag, registrerer kodens
vaner og ikke en standard. Begynd med et bevidst valg, enten alt på én gang
eller Ruffs forslag om en lille base af `E` og `F` med én gruppe tilføjet ad
gangen, og skriv ned, hvorfor hver frakoblet regel er slået fra. Ruff advarer
om, at `ALL` stiltiende slår nye regler til, hver gang værktøjet opgraderes, så
lås linterens version, og opgradér den som en ændring for sig.

**Undertrykkelser, der spredes én linje ad gangen.** Hver linter har en
nødudgang: `/* LINTED */` i lint(1), `# noqa: F841` i Ruff,
`# shellcheck disable=SC2059` i ShellCheck og `// eslint-disable-next-line` i
ESLint. Hver af dem registrerer, hvad der blev gjort tavst, men ikke hvorfor.
Kræv en skreven begrundelse ved hver undtagelse, og for dem, der betyder noget,
slå reglen fra, og tilføj en test for den egenskab, den beskyttede. Saml
undtagelserne ét sted: ESLints `--no-inline-config` gør disable-kommentarer
virkningsløse, og ShellCheck læser `disable`-linjer for hele projektet fra en
`.shellcheckrc`-fil.

## Læs mere

- [lint(1)](https://man.netbsd.org/lint.1) — NetBSD's manual for det
  C-tjekværktøj, ordet stammer fra: de to gennemløb, `-w` og kommentarerne, der
  undertrykker meldinger.
- [The Ruff Linter](https://docs.astral.sh/ruff/linter/) — regelvalg, sikre og
  usikre rettelser, undertrykkende kommentarer og exitkoder.
- [Command Line Interface Reference](https://eslint.org/docs/latest/use/command-line-interface)
  — ESLints `--max-warnings`, inline-konfiguration, umatchede mønstre og
  exitkoder.
- [ShellCheck](https://github.com/koalaman/shellcheck) — projektets README: hvad
  værktøjet leder efter, og hvordan det passer ind i et build.
- [Ignore](https://github.com/koalaman/shellcheck/wiki/Ignore) — ShellChecks
  måder at ignorere et fund på pr. linje, pr. kørsel og pr. projekt.

<https://engineer.company/da/glossary/linter/>
