It follows the value. It does not search for the word.
Everything else on this site depends on that difference, so it is worth being precise about what actually happens to your code.
Why grep cannot do this.
Five stages, one pass.
Each file becomes a syntax tree, not a string. That is what makes the rest possible: a variable is an identity that can be followed, rather than a word that can be matched.
Fields that look like personal data are proposed, with a type. You confirm or reject each one once, and that decision is what every later stage uses.
From each classified field, the analysis follows assignments, arguments and returns across functions and files, to wherever the value comes to rest.
A resting place is evaluated in context. A log call, an unencrypted store and an outbound request to a third party are three different findings from the same path.
Each finding names the obligation it touches, so nobody has to translate a rule id into a clause before deciding whether it matters.
On your runner, if you prefer.
The scan is a container. Run it in your pipeline and the analysis happens on your infrastructure, with findings returned as SARIF and your source never leaving the build. Scanning in CI needs no account and no API key.
The honest boundary.
Static analysis cannot follow a destination the program computes at runtime, cannot see a system that was never in your repository, and cannot judge whether a purpose is lawful. It reports how many outbound calls it failed to resolve rather than presenting a partial map as a complete one, and every organisational obligation stays with the humans who own it. Across 12 languages the depth varies, and the coverage page says where.
Watch it read a repository.
A public repository, read once and not kept. You get the data map: every personal-data field, where it lands, and who receives it. Or run the container on your own runner, and the source never reaches us.