The aim is to warn on cycles in header-files, which causes problems
with clang modules (and can result in confusing errors), and makes the
headers not C++20 header-units. But wait, how can this happen?
Consider:
// Hdr foo.h .#ifndef FOO_H .#define FOO_H // understand this is more likely to be transitive through some set of // intermediate headers .#include "foo.h" // Body of header .#endif
This preprocesses just fine, as by the time we recursively include the
header, it has already defined the protection macro (the compiler
won't know it's an idempotent header at that point, because it will
not yet have observed that the whole header is inside the #ifndef).
You only get a compilation problem if one of the transitive headers
makes reference to an entity that is declared in the body of the
header.
It turns out to be stunningly easy to create such silent header loops,
and they are devilishly hard to detect. One has to preprocess the
file and examine the #line markings. Except, that's not robust as if
the header is either #imported or #pragma once'd, then the recursive
show the problem. That's why this warning is emitted *before* the
idempotency optimization check.
This adds a dense set to Preprocessor, and uses that to record
FileEntry's as include files are entered. Just before determining
whether to enter a #include, we check this set and warn, if a loop is
found. This means we can warn on circular #imports and #pragma once
headers too (which are just as problematic):
In file included from .../llvm/me/hdr-cyc/clang/test/Preprocessor/warn-loop-main.c:4:
In file included from .../llvm/me/hdr-cyc/clang/test/Preprocessor/warn-loop-macro-2.h:3:
.../llvm/me/hdr-cyc/clang/test/Preprocessor/warn-loop-macro-2a.h:4:10: warning: #include cycle [-Winclude-cycle]
In order to detect multiple loops with the same outer header, we add a
flag to PreprocessorLexer, to record whether circularity was detected
upon creation. We only remove the associated FileEntry from the map
at the outermost level. (You'll see the test case has two loops
involving warn_loop_macro_2.h.)
The boost headers are full of loops. Some are real, some are not
actual loops due to anticoherence in their controlling conditionals.
The iterator is somewhat costly (at least 2 pointers?) and since we're not accessing them, copying them into the structured binding object probably isn't valuable?