logan-5 updated this revision to Diff 238064. logan-5 marked 6 inline comments as done. logan-5 added a comment.
Renamed check option from "Whitelist" to "AllowedIdentifiers". Added note about missing checks in documentation. Changed to use a %select for diagnostic text. Some nits. The check does due diligence when run over LLVM (after whitelisting a couple things), correctly flagging a few suspicious names scattered about. The inverted mode is a bit cumbersome at the moment to use on libc++ (since libc++ legitimately defines lots of non-reserved names (that's its job)). One could whitelist every name the standard defines, but I'd like to add some logic in a future patch that is smarter about which names need to be reserved in inverted mode -- something like, flagging private class members but not public ones, for example. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D72378/new/ 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/clang-tidy/cert/CERTTidyModule.cpp clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/bugprone-reserved-identifier.rst clang-tools-extra/docs/clang-tidy/checks/cert-dcl37-c.rst clang-tools-extra/docs/clang-tidy/checks/cert-dcl51-cpp.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-c.c 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,206 @@ +// 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 identifier '_MACRO', which is a reserved identifier [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 a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}namespace Ns {{{$}} + +class _Object { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '_Object', which is a reserved identifier [bugprone-reserved-identifier] + // CHECK-FIXES: {{^}}class Object {{{$}} + int _Member; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '_Member', which is a reserved identifier [bugprone-reserved-identifier] + // CHECK-FIXES: {{^}} int Member;{{$}} +}; + +float _Global; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '_Global', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}float Global;{{$}} + +void _Function() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier '_Function', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}void Function() {}{{$}} + +using _Alias = int; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '_Alias', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}using Alias = int;{{$}} + +template <typename _TemplateParam> +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: declaration uses identifier '_TemplateParam', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}template <typename TemplateParam>{{$}} +struct S {}; + +} // namespace _Ns + +// + +#define __macro(m) int m = 0 +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: declaration uses identifier '__macro', which is a reserved identifier [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 a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}namespace ns {{{$}} +class __object { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '__object', which is a reserved identifier [bugprone-reserved-identifier] + // CHECK-FIXES: {{^}}class _object {{{$}} + int __member; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '__member', which is a reserved identifier [bugprone-reserved-identifier] + // CHECK-FIXES: {{^}} int _member;{{$}} +}; + +float __global; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '__global', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}float _global;{{$}} + +void __function() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier '__function', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}void _function() {}{{$}} + +using __alias = int; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier '__alias', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}using _alias = int;{{$}} + +template <typename __templateParam> +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: declaration uses identifier '__templateParam', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}template <typename _templateParam>{{$}} +struct S {}; + +} // namespace __ns + +// + +#define macro___m(m) int m = 0 +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: declaration uses identifier 'macro___m', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}#define macro_m(m) int m = 0{{$}} + +namespace ns___n { +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: declaration uses identifier 'ns___n', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}namespace ns_n {{{$}} +class object___o { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier 'object___o', which is a reserved identifier [bugprone-reserved-identifier] + // CHECK-FIXES: {{^}}class object_o {{{$}} + int member___m; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier 'member___m', which is a reserved identifier [bugprone-reserved-identifier] + // CHECK-FIXES: {{^}} int member_m;{{$}} +}; + +float global___g; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier 'global___g', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}float global_g;{{$}} + +void function___f() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier 'function___f', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}void function_f() {}{{$}} + +using alias___a = int; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration uses identifier 'alias___a', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}using alias_a = int;{{$}} + +template <typename templateParam___t> +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: declaration uses identifier 'templateParam___t', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}template <typename templateParam_t>{{$}} +struct S {}; + +} // 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 [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 [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 [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 [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 [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 [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}using alias = int;{{$}} +template <typename _templateParam> // no warning, template params are not in the global namespace +struct S {}; + +void _float() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier '_float', which is reserved in the global namespace; 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 identifier 'SOME__MACRO', which is a reserved identifier; 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 identifier '_TWO__PROBLEMS', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}void TWO_PROBLEMS() {}{{$}} +void _two__problems() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier '_two__problems', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}void two_problems() {}{{$}} + +int __; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration uses identifier '__', which is a reserved identifier; cannot be fixed automatically [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}int __;{{$}} + +int _________; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: declaration uses identifier '_________', which is a reserved identifier; 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; 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; +template <typename TemplateParam> +struct S {}; +} // namespace Ns +namespace ns_ { +class object_ { + int member_; +}; +float global_; +void function_() {} +using alias_ = int; +template <typename templateParam_> +struct S {}; +} // namespace ns_ + +class object_ { + int member_; +}; +float global_; +void function_() {} +using alias_ = int; +template <typename templateParam_> +struct S_ {}; 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.AllowedIdentifiers, 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/bugprone-reserved-identifier-c.c =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/bugprone-reserved-identifier-c.c @@ -0,0 +1,10 @@ +// RUN: %check_clang_tidy %s bugprone-reserved-identifier %t + +// in C, double underscores are fine except at the beginning + +void foo__(); +void f__o__o(); +void f_________oo(); +void __foo(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration uses identifier '__foo', which is a reserved identifier [bugprone-reserved-identifier] +// CHECK-FIXES: {{^}}void foo();{{$}} 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/cert-dcl51-cpp.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/cert-dcl51-cpp.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-dcl51-cpp +.. meta:: + :http-equiv=refresh: 5;URL=bugprone-reserved-identifier.html + +cert-dcl51-cpp +============== + +The cert-dcl51-cpp check is an alias, please see +`bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_ for more +information. Index: clang-tools-extra/docs/clang-tidy/checks/cert-dcl37-c.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/cert-dcl37-c.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-dcl37-c +.. meta:: + :http-equiv=refresh: 5;URL=bugprone-reserved-identifier.html + +cert-dcl37-c +============ + +The cert-dcl37-c check is an alias, please see +`bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_ for more +information. 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,56 @@ +.. title:: clang-tidy - bugprone-reserved-identifier + +bugprone-reserved-identifier +============================ + +`cert-dcl37-c` and `cert-dcl51-cpp` redirect here as an alias for this check. + +Checks for usages of identifiers reserved for use by the implementation. + +The C and C++ standards both reserve the following names for such use: +* identifiers that begin with an underscore followed by an uppercase letter; +* identifiers in the global namespace that begin with an underscore. + +The C standard additionally reserves names beginning with a double underscore, +while the C++ standard strengthens this to reserve names with a double +underscore occurring anywhere. + +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. + +This check does not (yet) check for other reserved names, e.g. macro names +identical to language keywords, and names specifically reserved by language +standards, e.g. C++ 'zombie names' and C future library directions. + +This check corresponds to CERT C Coding Standard rule `DCL37-C. Do not declare +or define a reserved identifier +<https://wiki.sei.cmu.edu/confluence/display/c/DCL37-C.+Do+not+declare+or+define+a+reserved+identifier>`_ +as well as its C++ counterpart, `DCL51-CPP. Do not declare or define a reserved +identifier +<https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL51-CPP.+Do+not+declare+or+define+a+reserved+identifier>`_. + +Options +------- + +.. option:: Invert + + If non-zero, inverts the check, i.e. flags names that are not reserved. + Default is `0`. + +.. option:: AllowedIdentifiers + + 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,11 @@ Finds function calls where it is possible to cause a not null-terminated result. +- New :doc:`bugprone-reserved-identifier + <clang-tidy/checks/bugprone-reserved-identifier>` check. + + Checks for usages of identifiers reserved for use by the C++ implementation. + - New :doc:`bugprone-signed-char-misuse <clang-tidy/checks/bugprone-signed-char-misuse>` check. @@ -174,6 +179,16 @@ New aliases ^^^^^^^^^^^ +- New alias :doc:`cert-dcl37-c + <clang-tidy/checks/cert-dcl37-c>` to + :doc:`bugprone-reserved-identifier + <clang-tidy/checks/bugprone-reserved-identifier>` was added. + +- New alias :doc:`cert-dcl51-cpp + <clang-tidy/checks/cert-dcl51-cpp>` to + :doc:`bugprone-reserved-identifier + <clang-tidy/checks/bugprone-reserved-identifier>` was added. + - New alias :doc:`cert-pos44-c <clang-tidy/checks/cert-pos44-c>` to :doc:`bugprone-bad-signal-to-kill-thread Index: clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp +++ clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp @@ -271,7 +271,6 @@ return; } -[clang-tidy] Factor out renaming-specific logic from readability-identifier-naming, for easy future reuse in other 'renamer' checks. if (const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl")) { if (!Decl->getIdentifier() || Decl->getName().empty() || Decl->isImplicit()) return; Index: clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -9,8 +9,9 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" -#include "../bugprone/UnhandledSelfAssignmentCheck.h" #include "../bugprone/BadSignalToKillThreadCheck.h" +#include "../bugprone/ReservedIdentifierCheck.h" +#include "../bugprone/UnhandledSelfAssignmentCheck.h" #include "../google/UnnamedNamespaceInHeaderCheck.h" #include "../misc/NewDeleteOverloadsCheck.h" #include "../misc/NonCopyableObjects.h" @@ -44,6 +45,8 @@ CheckFactories.registerCheck<PostfixOperatorCheck>( "cert-dcl21-cpp"); CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp"); + CheckFactories.registerCheck<bugprone::ReservedIdentifierCheck>( + "cert-dcl51-cpp"); CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>( "cert-dcl54-cpp"); CheckFactories.registerCheck<DontModifyStdNamespaceCheck>( @@ -78,6 +81,8 @@ CheckFactories.registerCheck<misc::StaticAssertCheck>("cert-dcl03-c"); CheckFactories.registerCheck<readability::UppercaseLiteralSuffixCheck>( "cert-dcl16-c"); + CheckFactories.registerCheck<bugprone::ReservedIdentifierCheck>( + "cert-dcl37-c"); // ENV CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c"); // FLP Index: clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.h @@ -0,0 +1,57 @@ +//===--- 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 implementation. +/// +/// The C and C++ standards both reserve the following names for such use: +/// * identifiers that begin with an underscore followed by an uppercase letter; +/// * identifiers in the global namespace that begin with an underscore. +/// +/// The C standard additionally reserves names beginning with a double +/// underscore, while the C++ standard strengthens this to reserve names with a +/// double underscore occurring anywhere. +/// +/// 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> AllowedIdentifiers; + +public: + ReservedIdentifierCheck(StringRef Name, ClangTidyContext *Context); + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + 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,178 @@ +//===--- 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 { + +static const char DoubleUnderscoreTag[] = "du"; +static const char UnderscoreCapitalTag[] = "uc"; +static const char GlobalUnderscoreTag[] = "global-under"; +static const char NonReservedTag[] = "non-reserved"; + +static const char Message[] = + "declaration uses identifier '%0', which is %select{a reserved " + "identifier|not a reserved identifier|reserved in the global namespace}1"; + +static int getMessageSelectIndex(StringRef Tag) { + if (Tag == NonReservedTag) + return 1; + if (Tag == GlobalUnderscoreTag) + return 2; + return 0; +} + +ReservedIdentifierCheck::ReservedIdentifierCheck(StringRef Name, + ClangTidyContext *Context) + : RenamerClangTidyCheck(Name, Context), + Invert(Options.get("Invert", false)), + AllowedIdentifiers(utils::options::parseStringList( + Options.get("AllowedIdentifiers", ""))) {} + +void ReservedIdentifierCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "Invert", Invert); + Options.store(Opts, "AllowedIdentifiers", + utils::options::serializeStringList(AllowedIdentifiers)); +} + +static std::string collapseConsecutive(StringRef Str, char C) { + std::string Result; + std::unique_copy(Str.begin(), Str.end(), std::back_inserter(Result), + [C](char A, char B) { return A == C && B == C; }); + return Result; +} + +static bool hasReservedDoubleUnderscore(StringRef Name, + const LangOptions &LangOpts) { + if (LangOpts.CPlusPlus) + return Name.find("__") != StringRef::npos; + return Name.startswith("__"); +} + +static Optional<std::string> +getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts) { + if (hasReservedDoubleUnderscore(Name, LangOpts)) + return collapseConsecutive(Name, '_'); + return None; +} + +static bool startsWithUnderscoreCapital(StringRef Name) { + return Name.size() >= 2 && Name[0] == '_' && std::isupper(Name[1]); +} + +static Optional<std::string> getUnderscoreCapitalFixup(StringRef Name) { + if (startsWithUnderscoreCapital(Name)) + return std::string(Name.drop_front(1)); + return None; +} + +static bool startsWithUnderscoreInGlobalNamespace(StringRef Name, + bool IsInGlobalNamespace) { + return IsInGlobalNamespace && Name.size() >= 1 && Name[0] == '_'; +} + +static Optional<std::string> +getUnderscoreGlobalNamespaceFixup(StringRef Name, 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(StringRef Name, bool IsInGlobalNamespace, + const LangOptions &LangOpts, bool Invert, + ArrayRef<std::string> AllowedIdentifiers) { + assert(!Name.empty()); + if (llvm::is_contained(AllowedIdentifiers, Name)) + return None; + + // TODO: Check for names identical to language keywords, and other names + // specifically reserved by language standards, e.g. C++ 'zombie names' and C + // future library directions + + 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(), LangOpts)) + AppendFailure(DoubleUnderscoreTag, *std::move(Fixup)); + if (auto Fixup = getUnderscoreCapitalFixup(InProgressFixup())) + AppendFailure(UnderscoreCapitalTag, *std::move(Fixup)); + if (auto Fixup = getUnderscoreGlobalNamespaceFixup(InProgressFixup(), + IsInGlobalNamespace)) + AppendFailure(GlobalUnderscoreTag, *std::move(Fixup)); + + return Info; + } + if (!(hasReservedDoubleUnderscore(Name, LangOpts) || + startsWithUnderscoreCapital(Name) || + startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace))) + return FailureInfo{NonReservedTag, 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()), + getLangOpts(), Invert, AllowedIdentifiers); +} + +Optional<RenamerClangTidyCheck::FailureInfo> +ReservedIdentifierCheck::GetMacroFailureInfo(const Token &MacroNameTok, + const SourceManager &) const { + return getFailureInfoImpl(MacroNameTok.getIdentifierInfo()->getName(), true, + getLangOpts(), Invert, AllowedIdentifiers); +} + +RenamerClangTidyCheck::DiagInfo +ReservedIdentifierCheck::GetDiagInfo(const NamingCheckId &ID, + const NamingCheckFailure &Failure) const { + return DiagInfo{Message, [&](DiagnosticBuilder &diag) { + diag << ID.second + << getMessageSelectIndex(Failure.Info.KindName); + }}; +}; + +} // 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 SignedCharMisuseCheck.cpp SizeofContainerCheck.cpp SizeofExpressionCheck.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 "SignedCharMisuseCheck.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" @@ -120,6 +121,8 @@ "bugprone-parent-virtual-call"); CheckFactories.registerCheck<PosixReturnCheck>( "bugprone-posix-return"); + CheckFactories.registerCheck<ReservedIdentifierCheck>( + "bugprone-reserved-identifier"); CheckFactories.registerCheck<SignedCharMisuseCheck>( "bugprone-signed-char-misuse"); CheckFactories.registerCheck<SizeofContainerCheck>(
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits