{"slug": "find-duplicate-code-in-c-deslop-adds-c-support", "title": "Find Duplicate Code in C: Deslop Adds C Support", "summary": "Deslop added C language support, parsing .c and .h files with tree-sitter-c and running them through the same fingerprint, sibling-window, token and ranking stages used for its other languages, the tool's documentation announced. The first scan of the Osprey compiler's hand-written C runtime — around 190 files and 46,000 lines — measured 7.5% duplication, mostly repeated test set-up, and surfaced two false positives now pinned by fixtures. Deslop deliberately refuses to count macro definitions, prototypes and flat for-loop headers as clone evidence, treating duplicated macro bodies as a documented false negative.", "body_md": "# Find Duplicate Code in C: Deslop Adds C Support\n\nC support runs through the same structural pipeline as every other Deslop language: tree-sitter parsing instead of regex, identifier and literal normalization instead of raw line matching, worst-offender-first ranking, and live LSP and MCP feedback while the code is changing. `.c` and `.h` files are discovered, parsed with [`tree-sitter-c`](https://github.com/tree-sitter/tree-sitter-c), and fed into the shared fingerprint, sibling-window, token and ranking stages described in [How It Works](https://deslop.live/docs/how-it-works/).\n\n## [Why C, and why now](#why-c-and-why-now)\n\nThe [Osprey](https://deslop.live/docs/osprey/) compiler ships a hand-written C runtime: around 190 files and 46,000 lines, compiled with warnings as errors. Its maintainers wanted the same duplication gate over that runtime that they already had over the Osprey and Python sources, and they named the shape they cared about most: copied error-path and cleanup blocks. A loader opens a file, allocates a buffer, reads it, and at each step has an `if (x == NULL) { fprintf(...); free(...); return NULL; }` rung. The next loader is written by copying the first. One of them later gets the fix.\n\nThe first scan of that runtime measured 7.5% duplication, mostly repeated test set-up, and it also measured two false positives that are now pinned by fixtures. Both are described below, because what a detector refuses to report matters as much as what it finds.\n\n## [What the C parser sees](#what-the-c-parser-sees)\n\nThe normalized tree keeps structure and drops spelling:\n\n- `identifier` ,`field_identifier` ,`type_identifier` ,`statement_identifier` and`primitive_type` collapse to one marker, so renaming a function, a struct member or a typedef, or swapping`int` for`long` , never hides a copy;\n- numbers, characters, string contents, escape sequences, `<system.h>` paths,`true` ,`false` and`NULL` collapse to one literal marker, while string and character wrappers stay structural so concatenated strings keep their own shape;\n- comments disappear;\n- pointer, array and function declarators, casts, `sizeof` , compound literals, initializer lists, designators, storage classes, type qualifiers and every statement form remain structural.\n\nA copied function with every local renamed and a buffer size changed is still the same tree.\n\n## [What the C parser refuses to count](#what-the-c-parser-refuses-to-count)\n\n**Macros.** The grammar never sees what a macro expands to. A `#define` body is one opaque leaf, so two unrelated macros would fingerprint identically. `#include`, `#define` and function-like macro definitions are therefore scaffolding, like `using` in C# or `import` in Python: never clone evidence, never a cluster on their own. Six files that share the same include block and constants and nothing else report no clusters. Duplicated macro bodies are a documented false negative, not a tolerated false positive.\n\n**Prototypes.** After normalization every one-argument prototype is the same shape. A header declaring `sqrt`, `exp` and `log` would otherwise match any header declaring three unrelated functions, and Osprey's `math.h` shim did exactly that against an unrelated test header. A prototype is a promise about a body that lives elsewhere; the body is the evidence. A header made of an include guard and prototypes reports nothing.\n\n**Loop headers.** In C the initializer, condition and update of a `for` sit directly beside the body, so a run of neighbouring nodes can be just `for (int i = 0; i < n; i++)`. That is an idiom every loop shares, not a copy. Deslop no longer forms a sibling window over a flat `for` header in any language that lays it out that way.\n\n## [What it does report](#what-it-does-report)\n\nThe fixture written for Osprey's request holds two loaders in two files. Each opens a handle, allocates, reads, and checks every step with a copied log-free-return rung. Deslop reports the shared rung across the two files as **Nearly identical code**, and inside one file it reports the short-read error path and the destructor releasing the same resources in the same order as **Identical code**. Those are the two findings a reviewer wants: the copy that will drift between files, and the cleanup sequence that should be one function.\n\n## [Find duplicate C functions and copy-paste blocks](#find-duplicate-c-functions-and-copy-paste-blocks)\n\nStart with the whole repository so matching blocks in separate `.c` and `.h` files can be compared. Review both identical functions and near-duplicates with renamed variables or changed constants. A useful match can be a repeated block inside a larger function, such as the error handling shared by two loaders.\n\nFor each finding, compare the occurrences side by side. Check the return values, resource ownership, pointer lifetimes and side effects before extracting a shared function. Similar structure is a reason to review the code. Deciding what to share also requires understanding its behavior.\n\n## [Refactor duplicated cleanup code safely](#refactor-duplicated-cleanup-code-safely)\n\nA cleanup helper should make ownership explicit. Identify which function owns each allocation or file handle, and which failure paths have already released it. Pass the resources the helper needs and preserve the order of cleanup operations. Consolidating two copied error paths must preserve each caller's error code and logging behavior.\n\nAfter the extraction, run the project's tests and scan again. Review the remaining clusters for smaller repeated blocks that the first refactor exposed. Keeping this loop small makes it easier to spot a changed failure path or a resource released twice.\n\n## [Prevention beats the cleanup sprint](#prevention-beats-the-cleanup-sprint)\n\nDeslop's MCP server gives coding agents a `find-similar` tool backed by the live workspace. Before an agent writes another loader, another error ladder or another test fixture, it submits the proposed shape and asks whether the repository already contains it. A strong match turns \"write a new implementation\" into \"reuse or extend the canonical one\". See [AI Integration](https://deslop.live/docs/ai-integration/) for the MCP setup and [For AI](https://deslop.live/docs/for-ai/) for the agent workflow.\n\n## [Generated C](#generated-c)\n\nGenerated parsers are the biggest C files in many repositories, and the tree-sitter parser generator writes no \"generated\" banner, so Deslop analyses them like any other source. Osprey's own generated parser is 86,000 lines and 29% self-similar, enough on its own to breach a repository gate. The right answer is [`report_hide`](https://deslop.live/docs/configuration/) for that directory, not `exclude`: the generated code stays in the analysis, so a hand-written file that duplicates it is still reported, while the generated file duplicating itself stays out of the headline number.\n\n## [Try it on a C repository](#try-it-on-a-c-repository)\n\nInstall Deslop from the [Getting Started guide](https://deslop.live/docs/), open a C workspace, and run:\n\n```\ndeslop .\n```\n\nThe CLI writes JSON, text and HTML reports. The VS Code extension adds live findings and a worst-first cluster view. Start with the first cluster: that is the duplicate with the largest measured impact, not merely the first one the scanner happened to encounter.", "url": "https://wpnews.pro/news/find-duplicate-code-in-c-deslop-adds-c-support", "canonical_source": "https://deslop.live/blog/find-duplicate-code-c/", "published_at": "2026-11-08 00:00:00+00:00", "updated_at": "2026-11-09 01:30:52.535986+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Deslop", "Osprey", "tree-sitter-c"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/find-duplicate-code-in-c-deslop-adds-c-support", "markdown": "https://wpnews.pro/news/find-duplicate-code-in-c-deslop-adds-c-support.md", "text": "https://wpnews.pro/news/find-duplicate-code-in-c-deslop-adds-c-support.txt", "jsonld": "https://wpnews.pro/news/find-duplicate-code-in-c-deslop-adds-c-support.jsonld"}}