[clang] [Clang] Add #pragma clang deprecated_header (PR #168041)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 8 10:38:04 PST 2025


AaronBallman wrote:

> > Overall, I like the idea, but I wonder if a pragma is really the best way forward in 2025. I can imagine standards committees wanting this functionality, and a pragma would likely be just fine in C, but not acceptable in C++.
> > Did you consider using an attribute instead? e.g., `[[clang::deprecated("this header is deprecated")]];` on a null statement in a header file could be a viable approach that both committees would be interested in standardizing under the existing `[[deprecated]]` attribute.
> 
> I didn't consider this before, but I've done that now and I think a `#pragma` is the right tool for the job. IMO deprecating a header is fundamentally part of the preprocessor, so it should be written in a way that looks like it is in fact part of preprocessing. A pragma seems like the natural tool to use in this case. I guess it'd be possible to write it as `#deprecated`, but I don't see a good reason it should be written like that.

That's compelling rationale, thanks! I agree that the preprocessor *alone* should be able to handle emitting the diagnostic and that would mean an attribute would be the wrong tool for the job because that requires the compiler to get involved.

> > Some things the PR needs to consider:
> > ```
> > * This needs documentation in the language extensions file. In particular, where within a header does the marking show up? Can it be anywhere? Just the "first semantic line" (non-comment, non-header guard)?
> > ```
> 
> I don't think there is a reason to restrict it to a particular position in the header. Whether it's a good idea for readability is questionable, but in general I think there are good reasons to not put it right at the top. e.g. in libc++ we'd put it after at least a `#pragma GCC system_header` and an include, since these are always the first things in a header right after the header guard. I don't see why this would need to change for this pragma.

The situation I'm worried about is where we don't see the deprecation pragma and the user includes the header file transitively. e.g.,
```
// Foobar.h
#ifndef FOOBAR_H
#define FOOBAR_H

#include "Baz.h" // Assume this happens through lots of nested includes

#endif

// Baz.h
#ifndef BAZ_H
#define BAZ_H

#include "Foobar.h"

#pragma clang deprecate_header("don't use Baz.h")
#endif

// Main.c
#include "Baz.h"
```
We'd want a warning in Main.c because of the include, but how would we diagnose the include from `Foobar.h`?

Is the basic idea for the implementation to wait until the end of the TU and see if the header is included in the TU and if so, diagnose? I was assuming the implementation would be "while processing a `#include`, see if the header is deprecated and if so, diagnose." but that wouldn't work here, right?

> 
> > ```
> > * What should happen if the deprecated header ends up including another deprecated header? Normally, deprecated things can reference other deprecated things on the assumption they're all being deprecated together. So perhaps the same is true here? Is it true if deprecated header A includes non-deprecated header B which then includes deprecated header C though?
> > ```
> 
> This is a good question and something I definitely didn't consider before. I'm not sure what the answer is TBH. For the libc++ use-case it's definitely not necessary, and I'm struggling a bit to come up with a scenario where you'd have multiple levels of deprecated headers. I think you'd usually deprecate a header because you either have a new header that should be used, or your header doesn't provide anything meaningful anymore and should just not be included anymore. Either way I don't know why you'd include another deprecated header. You're _not_ deprecating the contents of the header after all.

Contriving a situation: You have a library to handle cryptography; there are headers for cryptographic algorithms and there are headers for implementation details. Say you want to deprecate one particular algorithm -- you might deprecate the public interface (`InsecureHash.h`) as well as the private interface (`InsecureHashImpl.h`). Does the public implementation file (`InsecureHash.c`) which includes the private interface (`InsecureHashImpl.h`) get a diagnostic? The user probably doesn't want one. But if the user includes `InsecureHashImpl.h` from `SecureHash.c`, they probably do want to know they're using the deprecated header.

> 
> > ```
> > * We usually suppress diagnostics coming from system headers but this one kind of spans the idea. The system header specifies the diagnostic should happen but the diagnostic itself happens on the inclusion line which may be user code. We need test coverage to make sure user code does get the diagnostic and system headers do not.
> > 
> > * What happens if the marking is used in a module that is imported? I would imagine we'd want the same diagnostic behavior? Perhaps another reason the name should be under `deprecated` rather than `deprecated_header`?
> > ```
> 
> Did I miss the part where you suggested to use `deprecated` instead of `deprecated_header`?

This was when I was thinking about `[[deprecated]];` as the way to surface it.

> What module are you talking about? A Clang module or a C++ module? Or are you taking about C++ header units? FWIW in my header C++ header units and Clang modules are basically just headers with fancy names, so I think `deprecated_header` still applies and should behave the same. With C++ modules IMO the right place to add a deprecation note would be on the module declaration itself, and the pragma should be rejected there.

I was thinking about a C++ module which has a global module fragment that includes a deprecated header file.

https://github.com/llvm/llvm-project/pull/168041


More information about the cfe-commits mailing list