logan-5 created this revision.
logan-5 added reviewers: JonasToth, alexfh, aaron.ballman.
logan-5 added a project: clang-tools-extra.
Herald added subscribers: cfe-commits, xazax.hun, mgorny.
Herald added a project: clang.

This patch adds `bugprone-reserved-identifier`, which flags uses of `__names` 
`_Like` `::_this`, which are reserved for the implementation. The check can 
optionally be inverted, i.e. configured to flag any names that are _not_ 
reserved, which may be useful for e.g. standard library implementors.

This diff is relative to, and dependent on, https://reviews.llvm.org/D72284. 
Not sure if there's a way to chain it or flag it as such within Phabricator, if 
so let me know.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D72378

Files:
  clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
  clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
  clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp
  clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/bugprone-reserved-identifier.rst
  
clang-tools-extra/test/clang-tidy/checkers/Inputs/bugprone-reserved-identifier/system/system-header.h
  
clang-tools-extra/test/clang-tidy/checkers/Inputs/bugprone-reserved-identifier/user-header.h
  
clang-tools-extra/test/clang-tidy/checkers/bugprone-reserved-identifier-invert.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone-reserved-identifier.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-reserved-identifier.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-reserved-identifier.cpp
@@ -0,0 +1,183 @@
+// RUN: %check_clang_tidy %s bugprone-reserved-identifier %t -- -- \
+// RUN:   -I%S/Inputs/bugprone-reserved-identifier \
+// RUN:   -isystem %S/Inputs/bugprone-reserved-identifier/system
+
+// no warnings expected without -header-filter=
+#include "user-header.h"
+#include <system-header.h>
+
+#define _MACRO(m) int m = 0
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: declaration uses reserved identifier '_MACRO', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}#define MACRO(m) int m = 0{{$}}
+
+namespace _Ns {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: declaration uses reserved identifier '_Ns', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}namespace Ns {{{$}}
+
+class _Object {
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '_Object', which causes undefined behavior [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}class Object {{{$}}
+  int _Member;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '_Member', which causes undefined behavior [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}  int Member;{{$}}
+};
+
+float _Global;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '_Global', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}float Global;{{$}}
+
+void _Function() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses reserved identifier '_Function', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void Function() {}{{$}}
+
+using _Alias = int;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '_Alias', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}using Alias = int;{{$}}
+
+} // namespace _Ns
+
+//
+
+#define __macro(m) int m = 0
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: declaration uses reserved identifier '__macro', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}#define macro(m) int m = 0{{$}}
+
+namespace __ns {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: declaration uses reserved identifier '__ns', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}namespace ns {{{$}}
+class __object {
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '__object', which causes undefined behavior [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}class object {{{$}}
+  int __member;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '__member', which causes undefined behavior [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}  int member;{{$}}
+};
+
+float __global;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '__global', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}float global;{{$}}
+
+void __function() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses reserved identifier '__function', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void function() {}{{$}}
+
+using __alias = int;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier '__alias', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}using alias = int;{{$}}
+
+} // namespace __ns
+
+//
+
+#define macro___m(m) int m = 0
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: declaration uses reserved identifier 'macro___m', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}#define macro_m(m) int m = 0{{$}}
+
+namespace ns___n {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: declaration uses reserved identifier 'ns___n', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}namespace ns_n {{{$}}
+class object___o {
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier 'object___o', which causes undefined behavior [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}class object_o {{{$}}
+  int member___m;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier 'member___m', which causes undefined behavior [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}  int member_m;{{$}}
+};
+
+float global___g;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier 'global___g', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}float global_g;{{$}}
+
+void function___f() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses reserved identifier 'function___f', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void function_f() {}{{$}}
+
+using alias___a = int;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses reserved identifier 'alias___a', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}using alias_a = int;{{$}}
+
+} // namespace ns___n
+
+//
+
+#define _macro(m) int m = 0
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: declaration uses identifier '_macro', which is reserved in the global namespace; this causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}#define macro(m) int m = 0{{$}}
+
+namespace _ns {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: declaration uses identifier '_ns', which is reserved in the global namespace; this causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}namespace ns {{{$}}
+int _i;
+// no warning
+} // namespace _ns
+class _object {
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '_object', which is reserved in the global namespace; this causes undefined behavior [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}class object {{{$}}
+  int _member;
+  // no warning
+};
+float _global;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '_global', which is reserved in the global namespace; this causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}float global;{{$}}
+void _function() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier '_function', which is reserved in the global namespace; this causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void function() {}{{$}}
+using _alias = int;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '_alias', which is reserved in the global namespace; this causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}using alias = int;{{$}}
+
+void _float() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier '_float', which is reserved in the global namespace; this causes undefined behavior; cannot be fixed because 'float' would conflict with a keyword [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void _float() {}{{$}}
+
+#define SOME_MACRO
+int SOME__MACRO;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration uses reserved identifier 'SOME__MACRO', which causes undefined behavior; cannot be fixed because 'SOME_MACRO' would conflict with a macro definition [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}int SOME__MACRO;{{$}}
+
+void _TWO__PROBLEMS() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses reserved identifier '_TWO__PROBLEMS', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void TWO_PROBLEMS() {}{{$}}
+void _two__problems() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses reserved identifier '_two__problems', which causes undefined behavior [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void two_problems() {}{{$}}
+
+int __;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration uses reserved identifier '__', which causes undefined behavior; cannot be fixed automatically [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}int __;{{$}}
+
+int _________;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration uses reserved identifier '_________', which causes undefined behavior; cannot be fixed automatically [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}int _________;{{$}}
+
+int _;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration uses identifier '_', which is reserved in the global namespace; this causes undefined behavior; cannot be fixed automatically [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}int _;{{$}}
+
+// these should pass
+#define MACRO(m) int m = 0
+
+namespace Ns {
+class Object {
+  int Member;
+};
+float Global;
+
+void Function() {}
+using Alias = int;
+} // namespace Ns
+namespace ns_ {
+class object_ {
+  int member_;
+};
+float global_;
+void function_() {}
+using alias_ = int;
+} // namespace ns_
+
+class object_ {
+  int member_;
+};
+float global_;
+void function_() {}
+using alias_ = int;
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-reserved-identifier-invert.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-reserved-identifier-invert.cpp
@@ -0,0 +1,70 @@
+// RUN: %check_clang_tidy %s bugprone-reserved-identifier %t -- \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: bugprone-reserved-identifier.Invert, value: 1}, \
+// RUN:     {key: bugprone-reserved-identifier.Whitelist, value: std;reference_wrapper;ref;cref;type;get}, \
+// RUN:   ]}' -- \
+// RUN:   -I%S/Inputs/bugprone-reserved-identifier \
+// RUN:   -isystem %S/Inputs/bugprone-reserved-identifier/system
+
+namespace std {
+
+void __f() {}
+
+void f();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier 'f', which is not a reserved identifier [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}void __f();{{$}}
+struct helper {};
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: declaration uses identifier 'helper', which is not a reserved identifier [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}struct __helper {};{{$}}
+struct Helper {};
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: declaration uses identifier 'Helper', which is not a reserved identifier [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}struct _Helper {};{{$}}
+struct _helper2 {};
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: declaration uses identifier '_helper2', which is not a reserved identifier [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}struct __helper2 {};{{$}}
+
+template <class _Tp>
+class reference_wrapper {
+public:
+  typedef _Tp type;
+
+private:
+  type *__f_;
+
+public:
+  reference_wrapper(type &__f)
+      : __f_(&__f) {}
+  // access
+  operator type &() const { return *__f_; }
+  type &get() const { return *__f_; }
+};
+
+template <class _Tp>
+inline reference_wrapper<_Tp>
+ref(_Tp &__t) noexcept {
+  return reference_wrapper<_Tp>(__t);
+}
+
+template <class _Tp>
+inline reference_wrapper<_Tp>
+ref(reference_wrapper<_Tp> __t) noexcept {
+  return ref(__t.get());
+}
+
+template <class Up>
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: declaration uses identifier 'Up', which is not a reserved identifier [bugprone-reserved-identifier]
+// CHECK-FIXES: {{^}}template <class _Up>{{$}}
+inline reference_wrapper<const Up>
+cref(const Up &u) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: declaration uses identifier 'u', which is not a reserved identifier [bugprone-reserved-identifier]
+  // CHECK-FIXES: {{^}}cref(const Up &__u) noexcept {{{$}}
+  return reference_wrapper<const Up>(u);
+}
+
+template <class _Tp>
+inline reference_wrapper<_Tp>
+cref(reference_wrapper<const _Tp> __t) noexcept {
+  return cref(__t.get());
+}
+
+} // namespace std
Index: clang-tools-extra/test/clang-tidy/checkers/Inputs/bugprone-reserved-identifier/user-header.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/Inputs/bugprone-reserved-identifier/user-header.h
@@ -0,0 +1,58 @@
+#define _HEADER_MACRO(m) int m = 0
+
+namespace _Header_Ns {
+class _Header_Object {
+  int _Header_Member;
+};
+
+float _Header_Global;
+
+void _Header_Function() {}
+
+using _Header_Alias = int;
+} // namespace _Header_Ns
+
+//
+
+#define __header_macro(m) int m = 0
+
+namespace __header_ns {
+class __header_object {
+  int __header_member;
+};
+
+float __header_global;
+
+void __header_function() {}
+
+using __header_alias = int;
+} // namespace __header_ns
+
+//
+
+#define header_macro__m(m) int m = 0
+
+namespace header_ns__n {
+class header_object__o {
+  int header_member__m;
+};
+
+float header_global__g;
+
+void header_function__f() {}
+
+using header_alias__a = int;
+} // namespace header_ns__n
+
+//
+
+#define _header_macro(m) int m = 0
+
+namespace _header_ns {}
+class _header_object {};
+
+float _header_global;
+
+void _header_function() {}
+
+using _header_alias = int;
Index: clang-tools-extra/test/clang-tidy/checkers/Inputs/bugprone-reserved-identifier/system/system-header.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/Inputs/bugprone-reserved-identifier/system/system-header.h
@@ -0,0 +1,33 @@
+namespace std {
+
+void __f() {}
+
+template <class _Tp>
+class reference_wrapper {
+public:
+  typedef _Tp type;
+
+private:
+  type *__f_;
+
+public:
+  reference_wrapper(type &__f)
+      : __f_(&__f) {}
+  // access
+  operator type &() const { return *__f_; }
+  type &get() const { return *__f_; }
+};
+
+template <class _Tp>
+inline reference_wrapper<_Tp>
+ref(_Tp &__t) noexcept {
+  return reference_wrapper<_Tp>(__t);
+}
+
+template <class _Tp>
+inline reference_wrapper<_Tp>
+ref(reference_wrapper<_Tp> __t) noexcept {
+  return ref(__t.get());
+}
+
+} // namespace std
Index: clang-tools-extra/docs/clang-tidy/checks/bugprone-reserved-identifier.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/bugprone-reserved-identifier.rst
@@ -0,0 +1,39 @@
+.. title:: clang-tidy - bugprone-reserved-identifier
+
+bugprone-reserved-identifier
+============================
+
+Checks for usages of identifiers reserved for use by the C++ implementation. 
+The C++ standard reserves the following names for such use:
+* identifiers with a double underscore anywhere;
+* identifiers that begin with an underscore followed by an uppercase letter;
+* identifiers in the global namespace that begin with an underscore.
+
+Violating the naming rules above results in undefined behavior.
+
+.. code-block:: c++
+
+  namespace NS { 
+    void __f(); // name is not allowed in user code
+    using _Int = int; // same with this
+    #define cool__macro // also this
+  }
+  int _g(); // disallowed in global namespace only
+
+The check can also be inverted, i.e. it can be configured to flag any 
+identifier that is _not_ a reserved identifier. This mode is for use by e.g. 
+standard library implementors, to ensure they don't infringe on the user 
+namespace.
+
+Options
+-------
+
+.. option:: Invert
+
+   If non-zero, inverts the check, i.e. flags names that are not reserved. 
+   Default is `0`.
+
+.. option:: Whitelist
+
+   Semicolon-separated list of names that the check ignores. Default is an 
+   empty list.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -94,6 +94,17 @@
   Without the null terminator it can result in undefined behaviour when the
   string is read.
 
+- New :doc:`bugprone-reserved-identifier
+  <clang-tidy/checks/bugprone-reserved-identifier>` check.
+
+  Checks for usages of identifiers reserved for use by the C++ implementation. 
+  The C++ standard reserves the following names for such use:
+  * identifiers with a double underscore anywhere;
+  * identifiers that begin with an underscore followed by an uppercase letter;
+  * identifiers in the global namespace that begin with an underscore.
+  
+  Violating the naming rules above results in undefined behavior.
+
 - New :doc:`cert-mem57-cpp
   <clang-tidy/checks/cert-mem57-cpp>` check.
 
Index: clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.h
@@ -0,0 +1,55 @@
+//===--- ReservedIdentifierCheck.h - clang-tidy -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_RESERVEDIDENTIFIERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_RESERVEDIDENTIFIERCHECK_H
+
+#include "../utils/RenamerClangTidyCheck.h"
+#include "llvm/ADT/Optional.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Checks for usages of identifiers reserved for use by the C++ implementation.
+/// The C++ standard reserves the following names for such use:
+/// * identifiers with a double underscore anywhere;
+/// * identifiers that begin with an underscore followed by an uppercase letter;
+/// * identifiers in the global namespace that begin with an underscore.
+///
+/// Violating the naming rules above results in undefined behavior.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-reserved-identifier.html
+class ReservedIdentifierCheck final : public RenamerClangTidyCheck {
+  const bool Invert;
+  const std::vector<std::string> Whitelist;
+
+public:
+  ReservedIdentifierCheck(StringRef Name, ClangTidyContext *Context);
+
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+protected:
+  llvm::Optional<FailureInfo>
+  GetDeclFailureInfo(const NamedDecl *Decl,
+                     const SourceManager &SM) const override;
+  llvm::Optional<FailureInfo>
+  GetMacroFailureInfo(const Token &MacroNameTok,
+                      const SourceManager &SM) const override;
+  DiagInfo GetDiagInfo(const NamingCheckId &ID,
+                       const NamingCheckFailure &Failure) const override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_RESERVEDIDENTIFIERCHECK_H
Index: clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp
@@ -0,0 +1,173 @@
+//===--- ReservedIdentifierCheck.cpp - clang-tidy -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReservedIdentifierCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+ReservedIdentifierCheck::ReservedIdentifierCheck(StringRef Name,
+                                                 ClangTidyContext *Context)
+    : RenamerClangTidyCheck(Name, Context),
+      Invert(Options.get("Invert", false)),
+      Whitelist(utils::options::parseStringList(Options.get("Whitelist", ""))) {
+}
+
+void ReservedIdentifierCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "Invert", Invert);
+  Options.store(Opts, "Whitelist",
+                utils::options::serializeStringList(Whitelist));
+}
+
+static std::string collapseConsecutive(const StringRef str, const char c) {
+  std::string result;
+  std::unique_copy(
+      str.begin(), str.end(), std::back_inserter(result),
+      [c](const char a, const char b) { return a == c && b == c; });
+  return result;
+}
+
+static bool hasDoubleUnderscore(const StringRef Name) {
+  return Name.find("__") != StringRef::npos;
+}
+
+static Optional<std::string> getDoubleUnderscoreFixup(StringRef Name) {
+  if (hasDoubleUnderscore(Name)) {
+    Name.consume_front("__");
+    return collapseConsecutive(Name, '_');
+  }
+  return None;
+}
+
+static bool startsWithUnderscoreCapital(const StringRef Name) {
+  return Name.size() >= 2 && Name[0] == '_' && std::isupper(Name[1]);
+}
+
+static Optional<std::string> getUnderscoreCapitalFixup(const StringRef Name) {
+  if (startsWithUnderscoreCapital(Name)) {
+    return std::string(Name.drop_front(1));
+  }
+  return None;
+}
+
+static bool
+startsWithUnderscoreInGlobalNamespace(const StringRef Name,
+                                      const bool IsInGlobalNamespace) {
+  return IsInGlobalNamespace && Name.size() >= 1 && Name[0] == '_';
+}
+
+static Optional<std::string>
+getUnderscoreGlobalNamespaceFixup(const StringRef Name,
+                                  const bool IsInGlobalNamespace) {
+  if (startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace)) {
+    return std::string(Name.drop_front(1));
+  }
+  return None;
+}
+
+static std::string getNonReservedFixup(std::string Name) {
+  assert(!Name.empty());
+  if (Name[0] == '_' || std::isupper(Name[0])) {
+    Name.insert(Name.begin(), '_');
+  } else {
+    Name.insert(Name.begin(), 2, '_');
+  }
+  return Name;
+}
+
+static Optional<RenamerClangTidyCheck::FailureInfo>
+getFailureInfoImpl(const StringRef Name, const bool IsInGlobalNamespace,
+                   const bool Invert, const ArrayRef<std::string> Whitelist) {
+  assert(!Name.empty());
+  if (std::find(Whitelist.begin(), Whitelist.end(), Name) != Whitelist.end())
+    return None;
+
+  using FailureInfo = RenamerClangTidyCheck::FailureInfo;
+  if (!Invert) {
+    Optional<FailureInfo> Info;
+    auto appendFailure = [&](StringRef Kind, std::string &&Fixup) {
+      if (!Info) {
+        Info = FailureInfo{Kind, std::move(Fixup)};
+      } else {
+        Info->KindName += Kind;
+        Info->Fixup = std::move(Fixup);
+      }
+    };
+    auto inProgressFixup = [&] {
+      return Info
+          .map([](const FailureInfo &Info) { return StringRef(Info.Fixup); })
+          .getValueOr(Name);
+    };
+    if (auto Fixup = getDoubleUnderscoreFixup(inProgressFixup())) {
+      appendFailure("du", *std::move(Fixup));
+    }
+    if (auto Fixup = getUnderscoreCapitalFixup(inProgressFixup())) {
+      appendFailure("uc", *std::move(Fixup));
+    }
+    if (auto Fixup = getUnderscoreGlobalNamespaceFixup(inProgressFixup(),
+                                                       IsInGlobalNamespace)) {
+      appendFailure("global-under", *std::move(Fixup));
+    }
+    if (Info) {
+      return Info;
+    }
+  } else {
+    if (!(hasDoubleUnderscore(Name) || startsWithUnderscoreCapital(Name) ||
+          startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace))) {
+      return FailureInfo{"non-reserved", getNonReservedFixup(Name)};
+    }
+  }
+  return None;
+}
+
+Optional<RenamerClangTidyCheck::FailureInfo>
+ReservedIdentifierCheck::GetDeclFailureInfo(const NamedDecl *Decl,
+                                            const SourceManager &) const {
+  assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() &&
+         !Decl->isImplicit() &&
+         "Decl must be an explicit identifier with a name.");
+  return getFailureInfoImpl(Decl->getName(),
+                            isa<TranslationUnitDecl>(Decl->getDeclContext()),
+                            Invert, Whitelist);
+}
+
+Optional<RenamerClangTidyCheck::FailureInfo>
+ReservedIdentifierCheck::GetMacroFailureInfo(const Token &MacroNameTok,
+                                             const SourceManager &) const {
+  return getFailureInfoImpl(MacroNameTok.getIdentifierInfo()->getName(), true,
+                            Invert, Whitelist);
+}
+
+RenamerClangTidyCheck::DiagInfo
+ReservedIdentifierCheck::GetDiagInfo(const NamingCheckId &ID,
+                                     const NamingCheckFailure &Failure) const {
+  return DiagInfo{
+      Failure.Info.KindName == "non-reserved"
+          ? "declaration uses identifier '%0', which is not a reserved "
+            "identifier"
+          : Failure.Info.KindName == "global-under"
+                ? "declaration uses identifier '%0', which is reserved in "
+                  "the global "
+                  "namespace; this causes undefined behavior"
+                : "declaration uses reserved identifier '%0', which causes "
+                  "undefined "
+                  "behavior",
+      [&](DiagnosticBuilder &diag) { diag << ID.second; }};
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -28,6 +28,7 @@
   NotNullTerminatedResultCheck.cpp
   ParentVirtualCallCheck.cpp
   PosixReturnCheck.cpp
+  ReservedIdentifierCheck.cpp
   SizeofContainerCheck.cpp
   SizeofExpressionCheck.cpp
   StringConstructorCheck.cpp
Index: clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -36,6 +36,7 @@
 #include "NotNullTerminatedResultCheck.h"
 #include "ParentVirtualCallCheck.h"
 #include "PosixReturnCheck.h"
+#include "ReservedIdentifierCheck.h"
 #include "SizeofContainerCheck.h"
 #include "SizeofExpressionCheck.h"
 #include "StringConstructorCheck.h"
@@ -119,6 +120,8 @@
         "bugprone-parent-virtual-call");
     CheckFactories.registerCheck<PosixReturnCheck>(
         "bugprone-posix-return");
+    CheckFactories.registerCheck<ReservedIdentifierCheck>(
+        "bugprone-reserved-identifier");
     CheckFactories.registerCheck<SizeofContainerCheck>(
         "bugprone-sizeof-container");
     CheckFactories.registerCheck<SizeofExpressionCheck>(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to