Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -356,6 +356,10 @@
   /// a C++ class.
   bool isCXXInstanceMember() const;
 
+  /// Determine if the declaration obeys the reserved identifier rules of the
+  /// given language
+  bool isReserved(const LangOptions &) const;
+
   /// Determine what kind of linkage this entity has.
   ///
   /// This is not the linkage as defined by the standard or the codegen notion
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -768,6 +768,9 @@
 def DuplicateArgDecl : DiagGroup<"duplicate-method-arg">;
 def SignedEnumBitfield : DiagGroup<"signed-enum-bitfield">;
 
+
+def ReservedIdentifier : DiagGroup<"reserved-identifier", [ReservedIdAsMacro]>;
+
 // Unreachable code warning groups.
 //
 //  The goal is make -Wunreachable-code on by default, in -Wall, or at
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -372,6 +372,10 @@
   "%select{used|required to be captured for this use}1">,
   InGroup<UnusedLambdaCapture>, DefaultIgnore;
 
+def warn_reserved_identifier: Warning<
+  "%0 is a reserved identifier">,
+  InGroup<ReservedIdentifier>, DefaultIgnore;
+
 def warn_parameter_size: Warning<
   "%0 is a large (%1 bytes) pass-by-value argument; "
   "pass it by reference instead ?">, InGroup<LargeByValueCopy>;
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2463,6 +2463,8 @@
                                           SourceLocation Less,
                                           SourceLocation Greater);
 
+  void warnOnReservedIdentifier(NamedDecl const *D);
+
   Decl *ActOnDeclarator(Scope *S, Declarator &D);
 
   NamedDecl *HandleDeclarator(Scope *S, Declarator &D,
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -1078,6 +1078,41 @@
   return L == getCachedLinkage();
 }
 
+bool NamedDecl::isReserved(const LangOptions &LangOpts) const {
+  IdentifierInfo *II = getIdentifier();
+  if (!II)
+    return false;
+
+  StringRef Name = II->getName();
+  // '_' is not a valid identifier, but it's use is so common (e.g. to store
+  // ignored values) that we don't warn on it.
+  if (Name.size() <= 1)
+    return false;
+
+  // 17.4.3.1.2 Global names [lib.global.names]
+  if (Name[0] == '_') {
+
+    // Each name that begins with an underscore is reserved to the
+    // implementation for use as a name in the global namespace.
+    if (getDeclContext()->isTranslationUnit()) {
+      StorageClass SC = getStorageClass(this);
+      if (SC == SC_Extern || SC == SC_PrivateExtern || SC == SC_None)
+        return true;
+    }
+
+    // Each name that begins with an underscore followed by an uppercase letter
+    // (2.11) is reserved.
+    if (Name[1] == '_' || ('A' <= Name[1] && Name[1] <= 'Z'))
+      return true;
+  }
+
+  // Each name that contains a double underscore (__) is reserved.
+  if (LangOpts.CPlusPlus && Name.contains("__"))
+    return true;
+
+  return false;
+}
+
 ObjCStringFormatFamily NamedDecl::getObjCFStringFormattingFamily() const {
   StringRef name = getName();
   if (name.empty()) return SFF_None;
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -5542,6 +5542,12 @@
   return false;
 }
 
+void Sema::warnOnReservedIdentifier(const NamedDecl *D) {
+  if (!Context.getSourceManager().isInSystemHeader(D->getLocation()) &&
+      D->isReserved(getLangOpts()))
+    Diag(D->getLocation(), diag::warn_reserved_identifier) << D;
+}
+
 Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) {
   D.setFunctionDefinitionKind(FunctionDefinitionKind::Declaration);
   Decl *Dcl = HandleDeclarator(S, D, MultiTemplateParamsArg());
@@ -5887,6 +5893,7 @@
   if (isInOpenMPDeclareTargetContext())
     checkDeclIsAllowedInOpenMPTarget(nullptr, New);
 
+  warnOnReservedIdentifier(New);
   return New;
 }
 
@@ -13622,6 +13629,8 @@
   if (getLangOpts().OpenCL)
     deduceOpenCLAddressSpace(New);
 
+  warnOnReservedIdentifier(New);
+
   return New;
 }
 
@@ -16276,6 +16285,7 @@
   } else if (SkipBody && SkipBody->ShouldSkip) {
     return SkipBody->Previous;
   } else {
+    warnOnReservedIdentifier(New);
     return New;
   }
 }
@@ -17088,6 +17098,8 @@
        i != end; ++i) {
     FieldDecl *FD = cast<FieldDecl>(*i);
 
+    warnOnReservedIdentifier(FD);
+
     // Get the type for the field.
     const Type *FDTy = FD->getType().getTypePtr();
 
@@ -17813,6 +17825,8 @@
 
   ActOnDocumentableDecl(New);
 
+  warnOnReservedIdentifier(New);
+
   return New;
 }
 
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -10012,8 +10012,9 @@
   DeclContext *LookupDC = dyn_cast<DeclContext>(D);
 
   if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) {
-    for (unsigned i = 0; i < DD->getNumTemplateParameterLists(); ++i)
+    for (unsigned i = 0; i < DD->getNumTemplateParameterLists(); ++i) {
       ParameterLists.push_back(DD->getTemplateParameterList(i));
+    }
 
     if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
       if (FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate())
@@ -10985,6 +10986,9 @@
   // for the namespace has the declarations that showed up in that particular
   // namespace definition.
   PushDeclContext(NamespcScope, Namespc);
+
+  warnOnReservedIdentifier(Namespc);
+
   return Namespc;
 }
 
@@ -12659,6 +12663,9 @@
 
   PushOnScopeChains(NewND, S);
   ActOnDocumentableDecl(NewND);
+
+  warnOnReservedIdentifier(NewND);
+
   return NewND;
 }
 
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -541,6 +541,10 @@
     return SubStmt;
   }
 
+  if (!Context.getSourceManager().isInSystemHeader(IdentLoc) &&
+      TheDecl->isReserved(getLangOpts()))
+    Diag(IdentLoc, diag::warn_reserved_identifier) << TheDecl;
+
   // Otherwise, things are good.  Fill in the declaration and return it.
   LabelStmt *LS = new (Context) LabelStmt(IdentLoc, TheDecl, SubStmt);
   TheDecl->setStmt(LS);
Index: clang/lib/Sema/SemaTemplate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplate.cpp
+++ clang/lib/Sema/SemaTemplate.cpp
@@ -1677,6 +1677,9 @@
   if (ExportLoc.isValid())
     Diag(ExportLoc, diag::warn_template_export_unsupported);
 
+  for (NamedDecl *P : Params)
+    warnOnReservedIdentifier(P);
+
   return TemplateParameterList::Create(
       Context, TemplateLoc, LAngleLoc,
       llvm::makeArrayRef(Params.data(), Params.size()),
@@ -8668,6 +8671,7 @@
   }
 
   ActOnDocumentableDecl(NewDecl);
+  warnOnReservedIdentifier(NewDecl);
   PushOnScopeChains(NewDecl, S);
   return NewDecl;
 }
Index: clang/test/Sema/reserved-identifier.c
===================================================================
--- /dev/null
+++ clang/test/Sema/reserved-identifier.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wreserved-identifier %s
+
+#define __oof foo__ // expected-warning {{macro name is a reserved identifier}}
+
+int foo__bar() { return 0; }    // no-warning
+static int _bar() { return 0; } // no-warning
+static int _Bar() { return 0; } // expected-warning {{'_Bar' is a reserved identifier}}
+int _foo() { return 0; }        // expected-warning {{'_foo' is a reserved identifier}}
+
+// This one is explicitly skipped by -Wreserved-identifier
+void *_; // no-warning
+
+void foo(unsigned int _Reserved) { // expected-warning {{'_Reserved' is a reserved identifier}}
+  unsigned int __1 =               // expected-warning {{'__1' is a reserved identifier}}
+      _Reserved;                   // no-warning
+  goto __reserved;
+__reserved: // expected-warning {{'__reserved' is a reserved identifier}}
+            ;
+}
+
+enum __menu { // expected-warning {{'__menu' is a reserved identifier}}
+  __some,     // expected-warning {{'__some' is a reserved identifier}}
+  _Other,     // expected-warning {{'_Other' is a reserved identifier}}
+  _other      // no-warning
+};
+
+struct __babar { // expected-warning {{'__babar' is a reserved identifier}}
+};
+
+typedef struct {
+  int _Field; // expected-warning {{'_Field' is a reserved identifier}}
+  int _field; // no-warning
+} _Typedef;   // expected-warning {{'_Typedef' is a reserved identifier}}
+
+int foobar() {
+  return foo__bar(); // no-warning
+}
Index: clang/test/Sema/reserved-identifier.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/reserved-identifier.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify -Wreserved-identifier %s
+
+int foo__bar() { return 0; }    // expected-warning {{'foo__bar' is a reserved identifier}}
+static int _bar() { return 0; } // no-warning
+static int _Bar() { return 0; } // expected-warning {{'_Bar' is a reserved identifier}}
+int _barbouille() { return 0; } // expected-warning {{'_barbouille' is a reserved identifier}}
+
+void foo(unsigned int _Reserved) { // expected-warning {{'_Reserved' is a reserved identifier}}
+  unsigned int __1 =               // expected-warning {{'__1' is a reserved identifier}}
+      _Reserved;                   // no-warning
+}
+
+// This one is explicitly skipped by -Wreserved-identifier
+void *_; // no-warning
+
+template <class T> constexpr bool __toucan = true; // expected-warning {{'__toucan' is a reserved identifier}}
+
+template <class T>
+concept _Barbotine = __toucan<T>; // expected-warning {{'_Barbotine' is a reserved identifier}}
+
+template <class __> // expected-warning {{'__' is a reserved identifier}}
+struct BarbeNoire {};
+
+template <class __> // expected-warning {{'__' is a reserved identifier}}
+void BarbeRousse() {}
+
+namespace _Barbidur { // expected-warning {{'_Barbidur' is a reserved identifier}}
+
+struct __barbidou {}; // expected-warning {{'__barbidou' is a reserved identifier}}
+struct _barbidou {};  // no-warning
+
+int __barbouille; // expected-warning {{'__barbouille' is a reserved identifier}}
+int _barbouille;  // no-warning
+
+int __babar() { return 0; } // expected-warning {{'__babar' is a reserved identifier}}
+int _babar() { return 0; }  // no-warning
+
+} // namespace _Barbidur
+
+class __barbapapa {     // expected-warning {{'__barbapapa' is a reserved identifier}}
+  void _barbabelle() {} // no-warning
+  int _Barbalala;       // expected-warning {{'_Barbalala' is a reserved identifier}}
+};
+
+enum class __menu { // expected-warning {{'__menu' is a reserved identifier}}
+  __some,           // expected-warning {{'__some' is a reserved identifier}}
+  _Other            // expected-warning {{'_Other' is a reserved identifier}}
+};
+
+using _Barbamama = __barbapapa; // expected-warning {{'_Barbamama' is a reserved identifier}}
+
+int foobar() {
+  return foo__bar(); // no-warning
+}