Around all of it

Static Analysis & Language Tooling

The frontend is the IDE. Abstract interpretation, interprocedural analysis, linters, formatters, concrete syntax trees and the language server that serves them all.

Static Analysis
▶ lab

Answering questions about every possible execution without running any of them. A compiler frontend is already a static analyser; the interesting part is not the machinery but the two ways it fails — noise you turn off, and silence you trust.

Q · What can a tool tell me about my program before it runs, and how much should I believe it?
Abstract Interpretation
▶ lab

Execute the program over a deliberately impoverished set of values — signs, nullability, intervals — so that the analysis terminates and covers every input at once. Widening is the part that makes loops finish, and it is where the precision goes.

Q · How can a tool reason about every possible input without enumerating any of them?
Control-Flow Analysis
▶ lab

Which statements can run in which order, and — the genuinely hard case — which functions a call site can actually reach when the callee is a value. In a higher-order language you cannot build the call graph without the analysis, and cannot run the analysis without the call graph.

Q · When the thing being called is a variable, how does any tool know what runs?
Interprocedural Analysis
▶ lab

Facts that cross a function boundary. The dial is context sensitivity — whether two call sites of the same function get one answer or two — and every notch of precision is paid for in compile time. Summaries are the compromise everything real is built on.

Q · How does a tool know anything about a value that was computed in a different function?
Linters
▶ lab

A compiler error means the code violates the language rules. A lint means the code is suspicious, unidiomatic, or probably-wrong-but-legal. The boundary between them is not fixed — it moves by ecosystem, and knowing where yours put it explains most of your tooling.

Q · Why is one problem a compiler error, an almost identical one a warning, and a third one something I have to install a separate tool to find?
Formatters
▶ lab

Parse the source, throw the layout away, and print it again from the tree. It only works if the tree kept the comments and blank lines the AST discards — which is why a formatter is the first tool that forces you to build a concrete syntax tree.

Q · Why does writing a code formatter require a different parser than writing a compiler?
The Concrete Syntax Tree
▶ lab

A tree in which every byte of the source appears exactly once — whitespace, comments and the literal text of every token included. It is what you need the moment a tool has to write code back out rather than only read it.

Q · Why do IDEs and refactoring tools build a second, bigger tree when the compiler already has one?
The Language Server
▶ lab

A compiler frontend rebuilt under three constraints a batch compiler never has: it must be incremental, it must produce answers about code that does not compile, and it may never throw information away. That is a different engineering problem, not the same one with a socket attached.

Q · Why can I not just run the compiler in a loop and send its output to my editor?
The Language Server Protocol

JSON-RPC over a pipe, and one decision that mattered more than any of its message types: standardizing the interface turned M editors times N languages into M plus N. The gotcha worth knowing is that its positions are UTF-16 code units.

Q · Why did every editor suddenly get good support for every language at roughly the same time?
Semantic Refactoring
▶ lab

A rename is not a text replacement. It is a query against the symbol table for every reference bound to one declaration, plus a check that the new name does not collide anywhere those references live — and the difference between those two operations is a class of silent bug.

Q · Why does a find-and-replace rename break code in ways an IDE rename does not?