Partly. SonarQube reports unused code that's local or private: a variable, parameter, import, private method or private class that nothing in the same scope uses. Searching its rules, we found none for a public function, class or export that nothing in the project calls, and none for unused files.
What SonarQube's rules cover
These are the rules about unused or unreachable code that SonarSource's own SonarQube instance returned for each language when searched for "unused", "dead", "unreachable" and "reachable" (rule search, 13 October 2026). "None found" means no rule came back for that kind of dead code:
| Kind of dead code | Python | TypeScript | Java |
|---|---|---|---|
| Unused local variables | S1481 | none found | S1481 |
| Unused parameters | S1172 | S1172 | S1172 |
| Unused assignments (dead stores) | S1854 | S1854 | S1854 |
| Unused imports | S1128 | S1128 | S1128 |
| Unused private methods | S1144 (class-private) | S1068 (private class members) | S1144 |
| Unused private classes or fields | S3985, S4487 | S1068 | S3985, S1068 |
| Unused nested definitions | S5603 | none found | none found |
| Unreachable code | S1763 | S1763 | S2583 |
| Unused React props and component methods | n/a | S6767, S6441 | n/a |
| Unused public function, class or export | none found | none found | none found |
| Unused file | none found | none found | none found |
Every rule above works inside one function, class or file. That's why SonarQube is reliable for them: the answer is local.
What it misses, in practice
A public function that nothing calls is invisible to those rules. In this small repository, legacy_slugify, Formatter.render_upper and formatPriceLegacy are all defined and never used:
# app/text.py
def slugify(title: str) -> str: ... # used by Formatter.render
def legacy_slugify(title: str) -> str: ... # never called
class Formatter:
def render(self, title: str) -> str: ... # used by main.py
def render_upper(self, title: str) -> str: ... # never called
// web/format.ts
export function formatPrice(cents: number): string { /* used by index.ts */ }
export function formatPriceLegacy(cents: number): string { /* never imported */ }
None of them is local or private, so no rule in the table applies. Skylos 4.47.2 reports all three, and nothing that is used:
$ skylos . --format concise
app/text.py:8 SKY-U001 unused function: legacy_slugify
app/text.py:16 SKY-U001 unused function: Formatter.render_upper
web/format.ts:5 SKY-U001 unused function: formatPriceLegacy
Why public dead code needs a different tool
Proving that a public function is unused means checking every possible caller:
- Every module that might import it, including re-exports and
__all__. - Framework entry points that are never called directly: routes, views, CLI commands, signal handlers and test fixtures.
- Dynamic use: names passed as strings,
getattr, plugin registries.
A rule that looks at one file can't answer that, so it reports nothing rather than guess. Dedicated dead-code tools build a reference graph of the whole repository instead. Skylos also models framework entry points, such as FastAPI routes and pytest fixtures, so they aren't reported as unused. Python has its deepest coverage. TypeScript/JavaScript, Java, PHP, Rust and Dart are also covered (language support).
Use both
SonarQube's unused-code rules and a whole-project dead-code check don't overlap much, so they work well together:
pip install skylos
skylos . # dead code across the whole project
skylos . --diff-base origin/main --diff origin/main # only what a pull request changed
Skylos's dead-code findings have been the basis of cleanup pull requests that maintainers merged in several open-source projects (the list).