Unverified Commit aafad2d2 authored by cor3ntin's avatar cor3ntin Committed by GitHub
Browse files

[Clang] Warn on deprecated specializations used in system headers. (#70353)

When the top of the instantiation stack is in user code.

The goal of this PR is to allow deprecation of some char_traits
specializations in libc++ as done in https://reviews.llvm.org/D157058
which was later reverted by
https://github.com/llvm/llvm-project/pull/66153#issuecomment-1719578384
as Clang never emitted the libc++ warnings.

Because Clang likes to eagerly instantiate, we can look for the location
of the top of the instantiation stack, and emit a warning if that
location is in user code.

The warning emission is forced by temporarily instructing the diag
engine not to silence warning in system headers.
parent 764c3afd
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -449,6 +449,8 @@ Improvements to Clang's diagnostics
- ``-Wzero-as-null-pointer-constant`` diagnostic is no longer emitted when using ``__null``
  (or, more commonly, ``NULL`` when the platform defines it as ``__null``) to be more consistent
  with GCC.
- Clang will warn on deprecated specializations used in system headers when their instantiation
  is caused by user code.

Improvements to Clang's time-trace
----------------------------------
+2 −0
Original line number Diff line number Diff line
@@ -8464,6 +8464,8 @@ public:
      ArrayRef<TemplateArgument> SugaredConverted,
      ArrayRef<TemplateArgument> CanonicalConverted, bool &HasDefaultArg);
  SourceLocation getTopMostPointOfInstantiation(const NamedDecl *) const;
  /// Specifies the context in which a particular template
  /// argument is being checked.
  enum CheckTemplateArgumentKind {
+23 −0
Original line number Diff line number Diff line
@@ -536,6 +536,29 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
    }
  }

  // We emit deprecation warning for deprecated specializations
  // when their instantiation stacks originate outside
  // of a system header, even if the diagnostics is suppresed at the
  // point of definition.
  SourceLocation InstantiationLoc =
      S.getTopMostPointOfInstantiation(ReferringDecl);
  bool ShouldAllowWarningInSystemHeader =
      InstantiationLoc != Loc &&
      !S.getSourceManager().isInSystemHeader(InstantiationLoc);
  struct AllowWarningInSystemHeaders {
    AllowWarningInSystemHeaders(DiagnosticsEngine &E,
                                bool AllowWarningInSystemHeaders)
        : Engine(E), Prev(E.getSuppressSystemWarnings()) {
      E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders);
    }
    ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); }

  private:
    DiagnosticsEngine &Engine;
    bool Prev;
  } SystemWarningOverrideRAII(S.getDiagnostics(),
                              ShouldAllowWarningInSystemHeader);

  if (!Message.empty()) {
    S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;
    if (ObjCProperty)
+22 −0
Original line number Diff line number Diff line
@@ -11601,3 +11601,25 @@ void Sema::checkSpecializationReachability(SourceLocation Loc,
                                          Sema::AcceptableKind::Reachable)
      .check(Spec);
}

/// Returns the top most location responsible for the definition of \p N.
/// If \p N is a a template specialization, this is the location
/// of the top of the instantiation stack.
/// Otherwise, the location of \p N is returned.
SourceLocation Sema::getTopMostPointOfInstantiation(const NamedDecl *N) const {
  if (!getLangOpts().CPlusPlus || CodeSynthesisContexts.empty())
    return N->getLocation();
  if (const auto *FD = dyn_cast<FunctionDecl>(N)) {
    if (!FD->isFunctionTemplateSpecialization())
      return FD->getLocation();
  } else if (!isa<ClassTemplateSpecializationDecl,
                  VarTemplateSpecializationDecl>(N)) {
    return N->getLocation();
  }
  for (const CodeSynthesisContext &CSC : CodeSynthesisContexts) {
    if (!CSC.isInstantiationRecord() || CSC.PointOfInstantiation.isInvalid())
      continue;
    return CSC.PointOfInstantiation;
  }
  return N->getLocation();
}
+28 −0
Original line number Diff line number Diff line
// RUN: %clang_cc1 -fsyntax-only -verify %s

#ifdef BE_THE_HEADER
#pragma clang system_header

template <typename T>
struct traits;

template <>
struct [[deprecated]] traits<int> {}; // expected-note {{'traits<int>' has been explicitly marked deprecated here}}

template<typename T, typename Trait = traits<T>>  // expected-warning {{'traits<int>' is deprecated}}
struct basic_string {};

// should not warn, defined and used in system headers
using __do_what_i_say_not_what_i_do  = traits<int> ;

template<typename T, typename Trait = traits<double>>
struct should_not_warn {};

#else
#define BE_THE_HEADER
#include __FILE__

basic_string<int> test1; // expected-note {{in instantiation of default argument for 'basic_string<int>' required here}}
should_not_warn<int> test2;

#endif