diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7108f0e..01f0e66 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangTidyRawSpeedModule + NoStdOptionalCheck.cpp StdArrayNoOperatorAtCheck.cpp RawSpeedTidyModule.cpp diff --git a/src/NoStdOptionalCheck.cpp b/src/NoStdOptionalCheck.cpp new file mode 100644 index 0000000..e68af05 --- /dev/null +++ b/src/NoStdOptionalCheck.cpp @@ -0,0 +1,49 @@ +//===--- NoStdOptionalCheck.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 "NoStdOptionalCheck.h" +#include +#include + +using namespace clang; +using namespace clang::ast_matchers; + +namespace { + +AST_MATCHER(clang::TypeLoc, hasValidBeginLoc) { + return Node.getBeginLoc().isValid(); +} + +} // namespace + +namespace clang::tidy::rawspeed { + +NoStdOptionalCheck::NoStdOptionalCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + +void NoStdOptionalCheck::registerMatchers(MatchFinder *Finder) { + auto StdOptionalClass = cxxRecordDecl(isSameOrDerivedFrom( + cxxRecordDecl(isInStdNamespace(), hasName("::std::optional")))); + auto Matcher = elaboratedTypeLoc( + hasValidBeginLoc(), loc(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(StdOptionalClass))))); + Finder->addMatcher(Matcher.bind("type"), this); +} + +void NoStdOptionalCheck::check(const MatchFinder::MatchResult &Result) { + const auto *TL = Result.Nodes.getNodeAs("type"); + if (!TL) + return; + + diag(TL->getBeginLoc(), + "Do not use 'std::optional' directly, use 'Optional' wrapper") + << TL->getSourceRange(); +} + +} // namespace clang::tidy::rawspeed diff --git a/src/NoStdOptionalCheck.h b/src/NoStdOptionalCheck.h new file mode 100644 index 0000000..4d51a65 --- /dev/null +++ b/src/NoStdOptionalCheck.h @@ -0,0 +1,27 @@ +//===--- NoStdOptionalCheck.h - clang-tidy -------------00000000-*- 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_RAWSPEED_NOSTDOPTIONAL_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RAWSPEED_NOSTDOPTIONAL_H + +#include + +namespace clang::tidy::rawspeed { + +/// Do not use `std::optional<>` directly, use `rawspeed::Optional<>`. +class NoStdOptionalCheck : public ClangTidyCheck { +public: + NoStdOptionalCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::rawspeed + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RAWSPEED_NOSTDOPTIONAL_H diff --git a/src/RawSpeedTidyModule.cpp b/src/RawSpeedTidyModule.cpp index 14e3ef2..51feedb 100644 --- a/src/RawSpeedTidyModule.cpp +++ b/src/RawSpeedTidyModule.cpp @@ -10,6 +10,7 @@ #include #include +#include "NoStdOptionalCheck.h" #include "StdArrayNoOperatorAtCheck.h" using namespace clang::ast_matchers; @@ -21,6 +22,8 @@ namespace rawspeed { class RawSpeedModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "rawspeed-no-std-optional"); CheckFactories.registerCheck( "rawspeed-std-array-no-operator-at"); } diff --git a/test/no-std-optional.cpp b/test/no-std-optional.cpp new file mode 100644 index 0000000..f168648 --- /dev/null +++ b/test/no-std-optional.cpp @@ -0,0 +1,222 @@ +// RUN: %check_clang_tidy -std=c++17-or-later %s rawspeed-no-std-optional %t + +namespace std { + +template class optional { +public: + optional() = default; + + template optional(U); +}; + +template class not_an_optional { +public: + not_an_optional() = default; + + template not_an_optional(U); +}; + +template struct tuple { + T t; +}; + +}; // namespace std + +namespace some_other_namespace { + +template class optional { +public: + optional() = default; + + template optional(U); +}; + +template class not_an_optional { +public: + not_an_optional() = default; + + template not_an_optional(U); +}; + +template struct tuple { + T t; +}; + +}; // namespace some_other_namespace + +template using my_optional_alias = std::optional; + +namespace { + +template class my_another_optional : public std::optional { +// CHECK-MESSAGES: :[[@LINE-1]]:58: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +public: + my_another_optional() = default; + + template my_another_optional(U); +}; + +} // namespace + +//------------------------------------------------------------------------------ + +std::optional f0_return_type() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +void f0_argument_type(std::optional) {} +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +std::tuple> f0_other_return_type() {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + +struct S0 { + std::optional f; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + std::optional a, b; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +}; + +void exprrs_f0() { + std::optional(1); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + std::optional a(1); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + std::optional b = 1; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + + f0_argument_type(1); + f0_argument_type(std::optional(1)); +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + auto c = f0_return_type(); + auto [d] = f0_other_return_type(); +} + +//------------------------------------------------------------------------------ + +std::not_an_optional f1_return_type() {} +void f1_argument_type(std::not_an_optional) {} +std::tuple> f1_other_return_type() {} + +struct S1 { + std::not_an_optional f; + std::not_an_optional a, b; +}; + +void exprrs_f1() { + std::not_an_optional(1); + std::not_an_optional a(1); + std::not_an_optional b = 1; + + f1_argument_type(1); + f1_argument_type(std::not_an_optional(1)); + auto c = f1_return_type(); + auto [d] = f1_other_return_type(); +} + +//------------------------------------------------------------------------------ + +some_other_namespace::optional f2_return_type() {} +void f2_argument_type(some_other_namespace::optional) {} +some_other_namespace::tuple> +f2_other_return_type() {} + +struct S2 { + some_other_namespace::optional f; + some_other_namespace::optional a, b; +}; + +void exprrs_f2() { + some_other_namespace::optional(1); + some_other_namespace::optional a(1); + some_other_namespace::optional b = 1; + + f2_argument_type(1); + f2_argument_type(some_other_namespace::optional(1)); + auto c = f2_return_type(); + auto [d] = f2_other_return_type(); +} + +//------------------------------------------------------------------------------ + +some_other_namespace::not_an_optional f3_return_type() {} +void f3_argument_type(some_other_namespace::not_an_optional) {} +some_other_namespace::tuple> +f3_other_return_type() {} + +struct S3 { + some_other_namespace::not_an_optional f; + some_other_namespace::not_an_optional a, b; +}; + +void exprrs_f3() { + some_other_namespace::not_an_optional(1); + some_other_namespace::not_an_optional a(1); + some_other_namespace::not_an_optional b = 1; + + f3_argument_type(1); + f3_argument_type(some_other_namespace::not_an_optional(1)); + auto c = f3_return_type(); + auto [d] = f3_other_return_type(); +} + +//------------------------------------------------------------------------------ + +my_optional_alias f4_return_type() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +void f4_argument_type(my_optional_alias) {} +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +std::tuple> f4_other_return_type() {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + +struct S4 { + my_optional_alias f; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + my_optional_alias a, b; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +}; + +void exprrs_f4() { + my_optional_alias(1); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + my_optional_alias a(1); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + my_optional_alias b = 1; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + + f4_argument_type(1); + f4_argument_type(my_optional_alias(1)); +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + auto c = f4_return_type(); + auto [d] = f4_other_return_type(); +} + +//------------------------------------------------------------------------------ + +my_another_optional f5_return_type() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +void f5_argument_type(my_another_optional) {} +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +std::tuple> f5_other_return_type() {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + +struct S5 { + my_another_optional f; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + my_another_optional a, b; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} +}; + +void exprrs_f5() { + my_another_optional(1); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + my_another_optional a(1); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + my_another_optional b = 1; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + + f5_argument_type(1); + f5_argument_type(my_another_optional(1)); +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: Do not use 'std::optional' directly, use 'Optional' wrapper [rawspeed-no-std-optional]{{$}} + auto c = f5_return_type(); + auto [d] = f5_other_return_type(); +} + +//------------------------------------------------------------------------------