DEV Community

Cover image for The Phantom That Wasn't There: How Unreliable grep Made Us Chase Ghosts
Flude team
Flude team

Posted on Originally published at blog.flude.guide

The Phantom That Wasn't There: How Unreliable grep Made Us Chase Ghosts

One of the hardest stages in developing a documentation generator is alignment with legacy tools. When you migrate from an old, decades-proven proprietary generator to your own custom pipeline, you are obligated to prove that you haven't lost a single class.

The Phantom Bug Grep

We wrote an automated system that compared thousands of entities generated by the old tool with what our new Flude-based pipeline found. And one day, we stumbled upon a terrifying anomaly.

Anomaly in the Matrix

The alignment script happily reported: Flude found and generated documentation for a struct named GraphicsAsset and its smart pointer GraphicsAssetPtr.

The problem was that in the old, reference documentation, these entities didn't exist. At all.

According to our logic, if the legacy generator didn't find the class, it's highly likely this class simply doesn't exist, and our parser made a mistake somewhere, "gluing" someone else's names together. We had to check the source code.

Zero Results

Zero grep results

We did what any engineer would do—we opened a terminal and ran a quick global text search (grep / ripgrep) across the entire multi-gigabyte codebase of our enterprise C++ product:

rg "GraphicsAsset" ./sdk_sources/
Enter fullscreen mode Exit fullscreen mode

Result: 0 matches. Absolute emptiness.

We checked with case-insensitivity, we searched for the smart pointer. Nothing.

Panic: The Hallucinating Parser

It was a moment of genuine horror. The codebase didn't contain the string GraphicsAsset. But somehow, our parser hadn't just found this name; it had built a beautiful HTML page for it, complete with a list of methods, properties, and inheritance!

The conclusion seemed obvious: our parser had gone insane. It had started fabricating C++ classes out of thin air. Perhaps the logic for expanding complex macros had broken, or Doxygen's cache had become so corrupted that it started stitching pieces of different files into a single "Frankenstein".

We were already preparing to open the debugger and rewrite the token-linking logic in the core.

Autopsy of the Cache

Fortunately, before tearing apart the core codebase, we decided to look at the raw intermediate data. We went to the directory where Doxygen dumped its low-level XML, and searched there.

Suddenly, sitting quietly on the disk, was a file named struct_graphics_asset.xml. We opened it.

Inside was perfectly formed XML. It didn't look like a hallucination. Moreover, in the <location> tag, the parser pedantically pointed out exactly where it got this class:
Core/Include/Graphics/MaterialConfig.h:3006-3061

The Phantom Takes Flesh

MaterialConfig.h macro

We opened the MaterialConfig.h file and scrolled down to line 3006.

And there, in the very heart of a massive legacy header, hid a completely real, living struct:

struct CORE_ABSTRACT GRAPHICS_EXPORT GraphicsAsset {
    // ... dozens of lines of valid C++ code
};
Enter fullscreen mode Exit fullscreen mode

It had always been there. The legacy generator simply couldn't parse it because of complex macros, but our Flude could.

So why did the global rg yield zero matches? In enterprise monoliths, there are always plenty of reasons. Perhaps the Core/Include path was listed in a tricky .gitignore, and the smart ripgrep ignored it. Perhaps the file had an exotic Windows encoding that broke the search. Perhaps it was a mounted submodule.

Conclusion

A global text search offered an absence of evidence as evidence of absence.

This case taught us a crucial rule: a text search is not a C++ parser. Trying to analyze the structure of a C++ monolith with regular expressions or grep is a path to disaster and false conclusions.

If you doubt your parser, never use text search as the arbiter. Trust only the raw dumps of the compiler itself or the Abstract Syntax Tree (AST). And most importantly: do not try to "fix" a bug in the core until you prove its existence in the actual source of truth.

But parsing and cache problems paled in comparison to our infrastructural challenges. From day one, our project was built as an ecosystem of several independent repositories. How this architecture turned into Git traps and nearly broke our Continuous Integration—read about it in the next episode.


Originally published on our blog: https://blog.flude.guide/blog/the-phantom-bug-grep

Also read us:

Top comments (0)