New issue 1235 by kadircet: New IWYU pragma for extension point patterns
https://github.com/include-what-you-use/include-what-you-use/issues/1235
# IWYU always_keep pragma proposal
## Background
In clang-tooling space there is a new focus around getting more automated & safe include management, similar to IWYU with a new “use” policy focused around spelling of symbols (hence also called as include-what-you-spell, IWYS). See [clang-include-cleaner](
https://github.com/llvm/llvm-project/tree/main/clang-tools-extra/include-cleaner/)
## Problem
It’s common in C++ to have extension points for types to enable their usage in certain generic algorithms, e.g. one common example could look like:
foo.h:
```cpp
struct Foo { … };
```
foo_ext.h:
```cpp
#include “foo.h”
bool isLess(Foo&, Foo&);
```
sort.h:
```cpp
template<typename Range> void sort(Range) { /* uses isLess(T&,T&) */ }
```
application.cc:
```cpp
#include “foo.h” // provides vector<Foo>& getStats();
#include “sort.h”
#include “foo_ext.h” // unused, there’s no "direct" usage of isLess(Foo&,Foo&)
void app() {
auto &f = getStats(); // returns a vector<Foo>&.
sort(f);
}
```
Here, removing `foo_ext.h` will prevent the code from compiling, so we need to avoid suggesting that. However no symbol from the file is "used" in our usual sense.
## Current ways to tackle such usages
### Analyze template instantiations and mark the usage of such extension points
This is close to the current IWYU tool and powerful, but doesn’t align with IWYS’s definition of “use”. It complicates the implementation drastically and is impossible in certain tools (e.g. clangd, which doesn’t keep template bodies from headers around). Moreover, deducing intent fully is still error-prone. Most of these arguments and complexity is also mentioned in [
https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/WhyIWYUIsDifficult.md#who-is-responsible-for-dependent-template-types](https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/WhyIWYUIsDifficult.md#who-is-responsible-for-dependent-template-types). So having all this complexity and still getting certain cases wrong goes against our automation goals.
### Restrict such patterns
We can call such patterns IWYU violations (and they are), and forbid people from using them. But this is really hard to enforce in practice. Most of the common alternatives require:
* extensions to go with type, which is not always possible (e.g. type might be generated, provider of the extension point and type might be completely different sets of parties, etc.)
* introduce these symbols at linking time via registries or forward declaring them.
All of these are drastic changes to the way people write C++ code today, hence it’ll make adoption unlikely on existing codebases and new code because people still need to use existing libraries that violate these new style guidelines.
### Use existing IWYU pragmas
IWYU export and IWYU keep pragmas are relevant in this case.
In theory an IWYU export pragma inside foo_ext.h on forward declaration or the type could help in certain cases. But:
* from IWYS perspective, the above application doesn’t really have a dependency on type Foo as it’s never spelled. This gives the library provider the ability to change their APIs easily (e.g. return type of getStats could change from Foo to Bar without breaking the application). So this doesn’t work in all cases.
* this allows the application to include foo_ext.h and not foo.h, even when Foo is used directly
IWYU keep pragma on the inclusion of foo_ext.h definitely works, but it pushes too much work to many end-users with completely obscure reasons. Hence creates a hard to understand model of C++ include management.
## Proposal, a new IWYU pragma
Considering these alternatives, we’d like to introduce a new pragma, IWYU pragma: always_keep (bikeshedding welcome), to be used on such extension headers and disable deletion of any include of a header that has an always_keep pragma.
This has the benefit of:
* centralizing the IWYU pragma management, only the extension provider needs to introduce a single pragma.
* no possibility of breaking the builds or semantics of a program.
* making it more obvious when an extension provider is working against IWYU principles and have them explicitly consider alternatives.
But it comes at the cost of less fine grained analysis. The include needs to be cleaned up by the application manually even if all the usages in code go away. We believe this cost is acceptable, since the suggested pragma also makes such headers easy to find and clean up centrally.
## Follow up work
We’d like to get this mentioned in [
https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md](https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md) documentation as an official one and help introducing it into the current IWYU tool.