Skip to main content

Module conditional

Module conditional 

Source
Expand description

Which arm of a preprocessor conditional a unit sits in.

C and C++ sources are parsed unexpanded, so both arms of an #if are in the IR at once. That is deliberate — the mode resolves no build conditions, and dropping the arm a default configuration would not take would silently hide code. It has a consequence for clone detection: the two arms of

#ifdef _WIN32
void sleep_ms(int ms) { Sleep(ms); }
#else
void sleep_ms(int ms) { usleep(ms * 1000); }
#endif

measure as near-identical, and a report that calls them duplicates is telling the reader to remove one. They cannot: exactly one of them exists in any build, and which one is a build condition this mode does not resolve. Reporting the pair would also put two build variants in one finding, which the analysis does not do anywhere else.

So the relation is recorded here and the pair is dropped before verification, exactly as a unit nested inside another is: not because the finding would rank badly, but because it is not a statement about any one program.

§What this does not claim

Only syntactic exclusion is recognised: two units under arms of the same conditional. Two units guarded by separate #ifs that happen to be mutually exclusive — #ifdef A here and #ifndef A there — are not related by this, because relating them means evaluating the conditions, and the conditions are what this mode does not have.

§Only as good as the parse

The arms are read off the tree, so a conditional the parser could not follow has no trustworthy arms. That is not hypothetical: a C++ header parsed by the C grammar — which happens whenever a project puts C++ in .h and the header policy says C — recovers into a shape where one #if swallows the rest of the file and its #else holds everything after it. Measured on one such header, that turned ten genuine arm pairs into eight hundred.

Dropping a pair hides a finding, so the mistake is not symmetric: a missed exclusion costs a noisy line in a report, an invented one costs a clone nobody will ever see. A conditional is therefore only believed when the parser stumbled nowhere inside it; one that encloses an error region still nests, but relates none of the units under it.

The judgement is per conditional rather than per file because error recovery is not local to what broke. A single unparsable construct puts an error region in the file, and a header whose include guard encloses everything gets one spanning the whole of it, which says nothing about the #if twenty lines further down. Measured across three C++ projects, believing a whole file only when it is error-free left 69% to 77% of the arms the parser had in fact read cleanly unused.

Structs§

ArmPath
The conditionals enclosing a unit, outermost first.
ArmTracker
Tracks the lexical arm active at each token in a C-family source file.

Enums§

StaticCondition
A literal condition that lexical preprocessing can establish without expanding macros or evaluating an expression.