diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt @@ -7,6 +7,7 @@ PortabilityTidyModule.cpp RestrictSystemIncludesCheck.cpp SIMDIntrinsicsCheck.cpp + StdAllocatorConstTCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp --- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModuleRegistry.h" #include "RestrictSystemIncludesCheck.h" #include "SIMDIntrinsicsCheck.h" +#include "StdAllocatorConstTCheck.h" namespace clang { namespace tidy { @@ -23,6 +24,8 @@ "portability-restrict-system-includes"); CheckFactories.registerCheck( "portability-simd-intrinsics"); + CheckFactories.registerCheck( + "portability-std-allocator-const-t"); } }; diff --git a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.h b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.h @@ -0,0 +1,36 @@ +//===--- StdAllocatorConstT.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_PORTABILITY_STDALLOCATORCONSTTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_STDALLOCATORCONSTTCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace portability { + +/// Find undefined container usage due to std::allocator. This is +/// unsupported in libstdc++ (all versions) and future libc++. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/portability-std-allocator-const-t.html +class StdAllocatorConstTCheck : public ClangTidyCheck { +public: + StdAllocatorConstTCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace portability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_STDALLOCATORCONSTTCHECK_H diff --git a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.cpp b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.cpp @@ -0,0 +1,55 @@ +//===-- StdAllocatorConstTCheck.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 "StdAllocatorConstTCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace portability { + +void StdAllocatorConstTCheck::registerMatchers(MatchFinder *Finder) { + // Match std::allocator. + internal::BindableMatcher AllocatorConstT = + recordType(hasDeclaration(classTemplateSpecializationDecl( + hasName("::std::allocator"), + hasTemplateArgument(0, refersToType(qualType(isConstQualified())))))); + + // Match `std::vector var;` and other common containers like deque, + // list, and absl::flat_hash_set. Some containers like forward_list, queue, + // and stack are not caught. Don't bother with std::unordered_set + // since libc++ does not allow it. + Finder->addMatcher( + varDecl( + hasType(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(classTemplateSpecializationDecl( + hasAnyName("::std::vector", "::std::deque", "::std::list", + "::std::multiset", "::std::set", + "::absl::flat_hash_set"), + anyOf(hasTemplateArgument(1, refersToType(AllocatorConstT)), + hasTemplateArgument(2, refersToType(AllocatorConstT)), + hasTemplateArgument( + 3, refersToType(AllocatorConstT))))))))) + .bind("var"), + this); +} + +void StdAllocatorConstTCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Var = Result.Nodes.getNodeAs("var"); + if (!Var) + return; + diag(Var->getBeginLoc(), + "container using std::allocator is " + "undefined; remove const to work with libstdc++ and future libc++"); +} + +} // namespace portability +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -117,6 +117,10 @@ Replaces groups of adjacent macros with an unscoped anonymous enum. +- New :doc:`portability-std-allocator-t ` check. + + Find undefined container usage due to ``std::allocator``. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -289,6 +289,7 @@ `performance-unnecessary-value-param `_, "Yes" `portability-restrict-system-includes `_, "Yes" `portability-simd-intrinsics `_, + `portability-std-allocator-const-t `_, `readability-avoid-const-params-in-decls `_, "Yes" `readability-braces-around-statements `_, "Yes" `readability-const-return-type `_, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability-std-allocator-const-t.rst b/clang-tools-extra/docs/clang-tidy/checks/portability-std-allocator-const-t.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/portability-std-allocator-const-t.rst @@ -0,0 +1,22 @@ +.. title:: clang-tidy - portability-std-allocator-const-t + +portability-std-allocator-const-t +================================= + +Per C++ ``[allocator.requirements.general]``: "T is any cv-unqualified object +type", ``std::allocator`` is undefined. Many standard containers use +``std::allocator`` by default and therefore their ``const T`` instantiations are +undefined. + +libstdc++ never supports ``std::allocator`` and containers using them. +Since GCC 8.0 (`PR48101 `), +there is a better diagnostic for some containers. + +.. code:: c++ + + std::deque deque; // error: static assertion failed: std::deque must have a non-const, non-volatile value_type + std::set set; // error: static assertion failed: std::set must have a non-const, non-volatile value_type + std::vector vector; // error: static assertion failed: std::vector must have a non-const, non-volatile value_type + +However, code bases not compiled with libstdc++ may accrue such undefined usage. +This check finds such code and prevents backsliding while clean-up is ongoing. diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability-std-allocator-const-t.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability-std-allocator-const-t.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability-std-allocator-const-t.cpp @@ -0,0 +1,72 @@ +// RUN: %check_clang_tidy -std=c++11-or-later %s portability-std-allocator-const-t %t -- + +namespace std { +typedef unsigned size_t; + +template +class allocator {}; +template +class hash {}; +template +class equal_to {}; +template +class less {}; + +template > +class deque {}; +template > +class forward_list {}; +template > +class list {}; +template > +class vector {}; + +template , class A = std::allocator> +class multiset {}; +template , class A = std::allocator> +class set {}; + +template > +class stack {}; +} // namespace std + +namespace absl { +template , class Eq = std::equal_to, class A = std::allocator> +class flat_hash_set {}; +} // namespace absl + +template +class allocator {}; + +void simple(std::vector a) { + // CHECK-MESSAGES: [[#@LINE-1]]:13: warning: container using std::allocator is undefined; remove const to work with libstdc++ and future libc++ [portability-std-allocator-const-t] + std::deque d; + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container + std::list l; + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container + + std::multiset ms; + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container + std::set> s; + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container + + absl::flat_hash_set fhs; + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container + + std::vector neg0; + std::vector neg1; // not const T + std::vector> neg2; // not use std::allocator + std::forward_list forward; // rare, don't bother + std::stack stack; // not caught, but rare +} + +template +void temp() { + std::vector v; + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container + + std::vector neg; +} +void use_temp() { + temp(); +}