# Linter

## Vad det är

En linter läser källkoden som text och rapporterar de ställen som ser ut som misstag, innan
programmet ens har byggts eller startats. Varje rapport anger en regel, en fil och en rad: en
variabel som tilldelas men aldrig används, en jämförelse som alltid är sann, en skalvariabel som
står utan citattecken, en formatsträng som byggs av indata från användaren.

Namnet kommer från lint, ett granskningsprogram för C, och NetBSD levereras fortfarande med ett
sådant. Manualsidan beskriver uppgiften som att hitta de drag i C-filer som sannolikt är buggar, som
inte är portabla eller som slösar resurser, och tillägger att lint gör en striktare typkontroll än
de traditionella kompilatorerna från tiden före C90. Arbetet sker i två pass: först granskas varje
fil som en egen enhet, sedan jämförs resultaten för att hitta motsägelser mellan filerna.

De flesta språk har i dag egna linters, ofta flera:

- **ESLint** för JavaScript.
- **Ruff** för Python.
- **ShellCheck** för skalskript. Projektets uttalade mål sträcker sig från syntaxfel som nybörjare
  gör till subtila fallgropar som kan få ett fungerande skript att fallera under framtida
  omständigheter.
- **Stylelint** för CSS, **markdownlint** för Markdown och **yamllint** för YAML.

Varje linter är uppbyggd av regler. Varje regel har en identifierare, t.ex. F841 i Ruff eller SC2059
i ShellCheck, och en allvarlighetsgrad. En konfigurationsfil avgör vilka regler som är påslagna och
hur allvarlig var och en är, och många regler har en automatisk rättning.

En formaterare skriver om layouten och har ingen åsikt om innebörden; en typkontroll resonerar om
vad varje värde är. En linter står mellan de två och tittar på hur koden är skriven: felaktig
användning av språket, riskabla mönster och de konventioner som ett projekt har kommit överens om.

## Varför det spelar roll

En linter läser varje gren, även felhanteraren som körs en gång om året och som inget test når fram
till. Den behöver bara sekunder för ett helt repo, så den kan köras vid varje sparning och varje
commit, och ett fynd kostar som minst i samma stund som raden skrivs.

Reglerna avgör diskussioner en gång för alla. Om ett bart `except:` är godtagbart, eller om varje
skalvariabel måste stå inom citattecken, bestäms i konfigurationen och tillämpas sedan likadant på
varje författare och i varje granskning. Mänskliga granskare kan då lägga sin uppmärksamhet på de
frågor som en regel inte kan uttrycka.

Slutkoden är gränssnittet mellan en linter och det som kör den. ShellChecks README framhåller att
verktyget använder slutkoder på det vedertagna sättet, så att ett bygge kan anropa det som ett
kommando bland andra. Ruff och ESLint dokumenterar samma tredelade konvention: 0 när granskningen är
ren, 1 när den har hittat överträdelser och 2 när själva verktyget har fallerat, t.ex. på en ogiltig
konfiguration. En kvalitetsgrind kan då skilja ”koden är fel” från ”granskaren är trasig”, och båda
bör stoppa den.

Allvarlighetsgraden avgör vilka fynd som når fram till slutkoden. ESLint avslutar med framgång när
det hittar varningar men inga fel, om inte `--max-warnings` sätter en gräns, och lint(1) har `-w`
för att behandla varningar som fel. Ett projekt som vill att dess linters ska stoppa misstag, och
inte bara beskriva dem, skriver ned det valet där varje körning använder det:

```sh
# every finding fails the run, warnings included
lint -w -abcFghprSxz source.c
eslint --max-warnings 0 src/
ruff check src/
shellcheck scripts/*.sh
```

Den här webbplatsen har valt den strikta änden av det spektrumet. Dess egen kvalitetsgrind räknar
varje diagnostik som ett fel och förbjuder undertryckningar på raden, som `# noqa`, så att ett fynd
åtgärdas vid sin orsak.

## Så ser det ut i praktiken

En linters konfiguration börjar ofta som de regler som råkade vara tysta den dag någon satte upp
den. En sida här vänder på det, med
[varje regel i en Python-linter aktiverad som fel](/sv/portfolio/enabled-every-python-linter-rule-as-an-error-147/).
Den första körningen gav 1 815 fynd. De arbetades igenom kategori för kategori snarare än fil för
fil, eftersom kategorierna bär information: oanvända argument och skuggade inbyggda namn var brus,
medan kategorierna för säkerhet, muterbara standardvärden och undantagshantering var och en pekade
på verkligt beteende. De få undantagen har ett skriftligt skäl på raden, och två av dem backas upp
av ett test som säkerställer den egenskap som den avstängda regeln skulle ha upprätthållit. Strikt
typkontroll kördes vid sidan av och fångade en annan sorts fel, som gällde vad ett värde är snarare
än hur det är skrivet. Kostnaden redovisas också: någon måste göra en bedömning 1 815 gånger.

Ett annat arbete börjar med en obekväm upptäckt. Webbplatsens kvalitetsgrind, trettiotvå skript på
sammanlagt 10 242 rader JavaScript, var den största kodmassan i repot och den enda som ingen
formaterare, linter eller typkontroll läste. Arbetet med att
[föra in grindens egen kod under en linter](/sv/portfolio/brought-the-quality-gate-code-under-a-linter-143/)
började med formateraren, inställd på två mellanslags indrag för de filerna, så att 38 rader
ändrades i stället för 2 173. Lintern gav sedan tretton verkliga fynd, alla åtgärdade och inget
undertryckt, och en typkontroll hittade två fel som lintern hade släppt igenom. Skälet till arbetet
står bredvid: ett fel i ett kontrollprogram visar sig som en kontroll som går igenom när den inte
borde.

Idén fungerar också på prosa. En tredje sida beskriver
[en linter för repots egen dokumentation](/sv/portfolio/put-the-documentation-under-its-own-linter-142/),
med gränser som fallerar i stället för att ge råd. Bland dess åtta mekaniska policyer finns en
radbudget per fil som bara kan sänkas, en regel om att statusen i en plan måste stämma med planens
egna kryssrutor och en kontroll av att varje sökväg och kodsymbol som ett dokument nämner faktiskt
finns i trädet. Den första körningen hittade sex planer som var markerade som klara men hade
okryssade rutor, fjorton uppgifter som spärrade varje commit utan att nämnas i något dokument, tre
trasiga länkar, ett föräldralöst dokument och tre döda sökvägar.

## Var det går fel

Det typiska problemet med en linter är tyst: en körning som avslutas med 0 när den borde ha stoppat
något.

**Fynd som aldrig påverkar slutkoden.** ESLints `--max-warnings` har värdet −1 som standard, vilket
stänger av gränsen, och manualsidan för lint(1) säger att passet över flera filer alltid avslutas
med framgång. Ett fynd som inte kan fälla körningen läses en gång och rullas sedan förbi. Bestäm per
regel: ett fel som fäller körningen, eller avstängd, och lägg varje inställning som gör varningar
till fel i den gemensamma konfigurationen.

**En rättning som rapporterar framgång.** Ruff avslutar med 0 när varje överträdelse som det hittade
rättades automatiskt. Det är praktiskt vid skrivbordet, men en grind som körs med rättningar
påslagna lagar bara sin egen arbetskopia, och den commit som kom in innehåller fortfarande varje
överträdelse. Kör grinden utan rättningar, eller lägg till `--exit-non-zero-on-fix` så att en
rättning räknas som ett misslyckande. Sök också efter `--exit-zero`: med den avslutar Ruff med 0
även när överträdelser hittas, och en flagga som lades till för en enda bullrig migrering brukar bli
kvar.

**Ett mönster som väljer noll filer.** En omdöpt katalog eller en flyttad konfiguration kan lämna en
linter riktad mot en tom mängd, och att granska noll filer ger ett rent resultat. ESLint behandlar
som standard ett citerat glob-mönster som matchar noll filer som ett fel. Flaggan
`--no-error-on-unmatched-pattern` gör det till ett tyst godkännande, och `--pass-on-no-patterns`
avslutar med 0 när inga filmönster anges. Behåll standarden, och behåll en fil med en känd
överträdelse som grinden måste rapportera.

**En regeluppsättning vald efter vad som var tyst.** En konfiguration som byggts genom att stänga av
allt som klagade första dagen dokumenterar kodens vanor, inte en standard. Börja från ett medvetet
urval, antingen allt på en gång eller Ruffs föreslagna lilla bas av `E` och `F` med en grupp i taget
tillagd, och skriv ned varför varje avstängd regel är avstängd. Ruff varnar för att `ALL` i det
tysta slår på nya regler varje gång verktyget uppgraderas, så lås linterns version och uppgradera
den som en egen ändring.

**Undertryckningar sprider sig en rad i taget.** Varje linter har en nödutgång: `/* LINTED */` i
lint(1), `# noqa: F841` i Ruff, `# shellcheck disable=SC2059` i ShellCheck och `//
eslint-disable-next-line` i ESLint. Var och en registrerar vad som tystades, inte varför. Kräv ett
skriftligt skäl bredvid varje undantag, och för de undantag som spelar roll: stäng av regeln och
lägg till ett test för den egenskap som den skyddade. Det hjälper också att hålla undantagen på ett
ställe. ESLints `--no-inline-config` gör kommentarer som stänger av regler verkningslösa, och
ShellCheck läser `disable`-rader för hela projektet från en `.shellcheckrc`-fil.

## Läs mer

- [lint(1)](https://man.netbsd.org/lint.1) — NetBSD:s manualsida för det C-verktyg som ordet kommer
  från: dess två pass, `-w` och dess kommentarer för undertryckning.
- [The Ruff Linter](https://docs.astral.sh/ruff/linter/) — val av regler, hur säkra rättningarna är,
  kommentarer för undertryckning och slutkoder.
- [Command Line Interface Reference](https://eslint.org/docs/latest/use/command-line-interface) —
  ESLints `--max-warnings`, konfiguration på raden, mönster utan träff och slutkoder.
- [ShellCheck](https://github.com/koalaman/shellcheck) — projektets README: vad verktyget letar
  efter och hur det passar in i ett bygge.
- [Ignore](https://github.com/koalaman/shellcheck/wiki/Ignore) — ShellChecks sätt att ignorera ett
  fynd per rad, per körning och per projekt.

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