| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| //===-- SwapBinaryOperandsTests.cpp -----------------------------*- 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "TweakTesting.h" | ||
| #include "gmock/gmock-matchers.h" | ||
| #include "gmock/gmock.h" | ||
| #include "gtest/gtest.h" | ||
|
|
||
| namespace clang { | ||
| namespace clangd { | ||
| namespace { | ||
|
|
||
| TWEAK_TEST(SwapBinaryOperands); | ||
|
|
||
| TEST_F(SwapBinaryOperandsTest, Test) { | ||
| Context = Function; | ||
| EXPECT_EQ(apply("int *p = nullptr; bool c = ^p == nullptr;"), | ||
| "int *p = nullptr; bool c = nullptr == p;"); | ||
| EXPECT_EQ(apply("int *p = nullptr; bool c = p ^== nullptr;"), | ||
| "int *p = nullptr; bool c = nullptr == p;"); | ||
| EXPECT_EQ(apply("int x = 3; bool c = ^x >= 5;"), | ||
| "int x = 3; bool c = 5 <= x;"); | ||
| EXPECT_EQ(apply("int x = 3; bool c = x >^= 5;"), | ||
| "int x = 3; bool c = 5 <= x;"); | ||
| EXPECT_EQ(apply("int x = 3; bool c = x >=^ 5;"), | ||
| "int x = 3; bool c = 5 <= x;"); | ||
| EXPECT_EQ(apply("int x = 3; bool c = x >=^ 5;"), | ||
| "int x = 3; bool c = 5 <= x;"); | ||
| EXPECT_EQ(apply("int f(); int x = 3; bool c = x >=^ f();"), | ||
| "int f(); int x = 3; bool c = f() <= x;"); | ||
| EXPECT_EQ(apply(R"cpp( | ||
| int f(); | ||
| #define F f | ||
| int x = 3; bool c = x >=^ F(); | ||
| )cpp"), | ||
| R"cpp( | ||
| int f(); | ||
| #define F f | ||
| int x = 3; bool c = F() <= x; | ||
| )cpp"); | ||
| EXPECT_EQ(apply(R"cpp( | ||
| int f(); | ||
| #define F f() | ||
| int x = 3; bool c = x >=^ F; | ||
| )cpp"), | ||
| R"cpp( | ||
| int f(); | ||
| #define F f() | ||
| int x = 3; bool c = F <= x; | ||
| )cpp"); | ||
| EXPECT_EQ(apply(R"cpp( | ||
| int f(bool); | ||
| #define F(v) f(v) | ||
| int x = 0; | ||
| bool c = F(x^ < 5); | ||
| )cpp"), | ||
| R"cpp( | ||
| int f(bool); | ||
| #define F(v) f(v) | ||
| int x = 0; | ||
| bool c = F(5 > x); | ||
| )cpp"); | ||
| ExtraArgs = {"-std=c++20"}; | ||
| Context = CodeContext::File; | ||
| EXPECT_UNAVAILABLE(R"cpp( | ||
| namespace std { | ||
| struct strong_ordering { | ||
| int val; | ||
| static const strong_ordering less; | ||
| static const strong_ordering equivalent; | ||
| static const strong_ordering equal; | ||
| static const strong_ordering greater; | ||
| }; | ||
| inline constexpr strong_ordering strong_ordering::less {-1}; | ||
| inline constexpr strong_ordering strong_ordering::equivalent {0}; | ||
| inline constexpr strong_ordering strong_ordering::equal {0}; | ||
| inline constexpr strong_ordering strong_ordering::greater {1}; | ||
| }; | ||
| #define F(v) v | ||
| int x = 0; | ||
| auto c = F(5^ <=> x); | ||
| )cpp"); | ||
| } | ||
|
|
||
| } // namespace | ||
| } // namespace clangd | ||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| ============================ | ||
| External Clang-Tidy Examples | ||
| ============================ | ||
|
|
||
| Introduction | ||
| ============ | ||
|
|
||
| This page provides examples of what people have done with :program:`clang-tidy` that | ||
| might serve as useful guides (or starting points) to develop your own checks. | ||
| They may be helpful for necessary things such as how to write the `CMakeLists.txt` | ||
| for an out-of-tree plugin of :program:`clang-tidy` checks. | ||
|
|
||
| If you know of (or wrote!) a tool or project using :program:`clang-tidy`, please share it | ||
| on `the Discourse forums (Clang Frontend category) | ||
| <https://discourse.llvm.org/c/clang/6>`_ for wider visibility and open a | ||
| pull-request on `LLVM Github`_ to have it added here. Since the primary purpose of | ||
| this page is to provide examples that can help developers, the listed projects should | ||
| have code available. | ||
|
|
||
| As :program:`clang-tidy` is using, for example, the AST Matchers and diagnostics of Clang, | ||
| `External Clang Examples`_ may also be useful to look at for such examples. | ||
|
|
||
| .. _LLVM Github: https://github.com/llvm/llvm-project | ||
| .. _External Clang Examples: https://clang.llvm.org/docs/ExternalClangExamples.html | ||
|
|
||
| List of projects and tools | ||
| ========================== | ||
|
|
||
| `<https://github.com/coveooss/clang-tidy-plugin-examples>`_ | ||
| "This folder contains :program:`clang-tidy` plugins." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| .. title:: clang-tidy - bugprone-bitwise-pointer-cast | ||
|
|
||
| bugprone-bitwise-pointer-cast | ||
| ============================= | ||
|
|
||
| Warns about code that tries to cast between pointers by means of | ||
| ``std::bit_cast`` or ``memcpy``. | ||
|
|
||
| The motivation is that ``std::bit_cast`` is advertised as the safe alternative | ||
| to type punning via ``reinterpret_cast`` in modern C++. However, one should not | ||
| blindly replace ``reinterpret_cast`` with ``std::bit_cast``, as follows: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| int x{}; | ||
| -float y = *reinterpret_cast<float*>(&x); | ||
| +float y = *std::bit_cast<float*>(&x); | ||
|
|
||
| The drop-in replacement behaves exactly the same as ``reinterpret_cast``, and | ||
| Undefined Behavior is still invoked. ``std::bit_cast`` is copying the bytes of | ||
| the input pointer, not the pointee, into an output pointer of a different type, | ||
| which may violate the strict aliasing rules. However, simply looking at the | ||
| code, it looks "safe", because it uses ``std::bit_cast`` which is advertised as | ||
| safe. | ||
|
|
||
| The solution to safe type punning is to apply ``std::bit_cast`` on value types, | ||
| not on pointer types: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| int x{}; | ||
| float y = std::bit_cast<float>(x); | ||
|
|
||
| This way, the bytes of the input object are copied into the output object, which | ||
| is much safer. Do note that Undefined Behavior can still occur, if there is no | ||
| value of type ``To`` corresponding to the value representation produced. | ||
| Compilers may be able to optimize this copy and generate identical assembly to | ||
| the original ``reinterpret_cast`` version. | ||
|
|
||
| Code before C++20 may backport ``std::bit_cast`` by means of ``memcpy``, or | ||
| simply call ``memcpy`` directly, which is equally problematic. This is also | ||
| detected by this check: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| int* x{}; | ||
| float* y{}; | ||
| std::memcpy(&y, &x, sizeof(x)); | ||
|
|
||
| Alternatively, if a cast between pointers is truly wanted, ``reinterpret_cast`` | ||
| should be used, to clearly convey the intent and enable warnings from compilers | ||
| and linters, which should be addressed accordingly. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,280 @@ | ||
| .. title:: clang-tidy - bugprone-tagged-union-member-count | ||
|
|
||
| bugprone-tagged-union-member-count | ||
| ================================== | ||
|
|
||
| Gives warnings for tagged unions, where the number of tags is | ||
| different from the number of data members inside the union. | ||
|
|
||
| A struct or a class is considered to be a tagged union if it has | ||
| exactly one union data member and exactly one enum data member and | ||
| any number of other data members that are neither unions or enums. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum Tags { | ||
| Tag1, | ||
| Tag2, | ||
| }; | ||
|
|
||
| struct TaggedUnion { // warning: tagged union has more data members (3) than tags (2) | ||
| enum Tags Kind; | ||
| union { | ||
| int I; | ||
| float F; | ||
| char *Str; | ||
| } Data; | ||
| }; | ||
| How enum constants are counted | ||
| ------------------------------ | ||
|
|
||
| The main complicating factor when counting the number of enum constants is that | ||
| some of them might be auxiliary values that purposefully don't have a corresponding union | ||
| data member and are used for something else. For example the last enum constant | ||
| sometimes explicitly "points to" the last declared valid enum constant or | ||
| tracks how many enum constants have been declared. | ||
|
|
||
| For an illustration: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithLast { | ||
| Tag1 = 0, | ||
| Tag2 = 1, | ||
| Tag3 = 2, | ||
| LastTag = 2 | ||
| }; | ||
|
|
||
| enum TagWithCounter { | ||
| Tag1, // is 0 | ||
| Tag2, // is 1 | ||
| Tag3, // is 2 | ||
| TagCount, // is 3 | ||
| }; | ||
|
|
||
| The check counts the number of distinct values among the enum constants and not the enum | ||
| constants themselves. This way the enum constants that are essentially just aliases of other | ||
| enum constants are not included in the final count. | ||
|
|
||
| Handling of counting enum constants (ones like :code:`TagCount` in the previous code example) | ||
| is done by decreasing the number of enum values by one if the name of the last enum constant | ||
| starts with a prefix or ends with a suffix specified in :option:`CountingEnumPrefixes`, | ||
| :option:`CountingEnumSuffixes` and it's value is one less than the total number of distinct | ||
| values in the enum. | ||
|
|
||
| When the final count is adjusted based on this heuristic then a diagnostic note is emitted | ||
| that shows which enum constant matched the criteria. | ||
|
|
||
| The heuristic can be disabled entirely (:option:`EnableCountingEnumHeuristic`) or | ||
| configured to follow your naming convention (:option:`CountingEnumPrefixes`, :option:`CountingEnumSuffixes`). | ||
| The strings specified in :option:`CountingEnumPrefixes`, :option:`CountingEnumSuffixes` are matched | ||
| case insensitively. | ||
|
|
||
| Example counts: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| // Enum count is 3, because the value 2 is counted only once | ||
| enum TagWithLast { | ||
| Tag1 = 0, | ||
| Tag2 = 1, | ||
| Tag3 = 2, | ||
| LastTag = 2 | ||
| }; | ||
|
|
||
| // Enum count is 3, because TagCount is heuristically excluded | ||
| enum TagWithCounter { | ||
| Tag1, // is 0 | ||
| Tag2, // is 1 | ||
| Tag3, // is 2 | ||
| TagCount, // is 3 | ||
| }; | ||
|
|
||
|
|
||
| Options | ||
| ------- | ||
|
|
||
| .. option:: EnableCountingEnumHeuristic | ||
|
|
||
| This option enables or disables the counting enum heuristic. | ||
| It uses the prefixes and suffixes specified in the options | ||
| :option:`CountingEnumPrefixes`, :option:`CountingEnumSuffixes` to find counting enum constants by | ||
| using them for prefix and suffix matching. | ||
|
|
||
| This option is enabled by default. | ||
|
|
||
| When :option:`EnableCountingEnumHeuristic` is `false`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounter { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| TagCount, | ||
| }; | ||
|
|
||
| struct TaggedUnion { | ||
| TagWithCounter Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| When :option:`EnableCountingEnumHeuristic` is `true`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounter { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| TagCount, | ||
| }; | ||
|
|
||
| struct TaggedUnion { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounter Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| .. option:: CountingEnumPrefixes | ||
|
|
||
| See :option:`CountingEnumSuffixes` below. | ||
|
|
||
| .. option:: CountingEnumSuffixes | ||
|
|
||
| CountingEnumPrefixes and CountingEnumSuffixes are lists of semicolon | ||
| separated strings that are used to search for possible counting enum constants. | ||
| These strings are matched case insensitively as prefixes and suffixes | ||
| respectively on the names of the enum constants. | ||
| If :option:`EnableCountingEnumHeuristic` is `false` then these options do nothing. | ||
|
|
||
| The default value of :option:`CountingEnumSuffixes` is `count` and of | ||
| :option:`CountingEnumPrefixes` is the empty string. | ||
|
|
||
| When :option:`EnableCountingEnumHeuristic` is `true` and :option:`CountingEnumSuffixes` | ||
| is `count;size`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounterCount { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| TagCount, | ||
| }; | ||
|
|
||
| struct TaggedUnionCount { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterCount Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| enum TagWithCounterSize { | ||
| Tag11, | ||
| Tag22, | ||
| Tag33, | ||
| TagSize, | ||
| }; | ||
|
|
||
| struct TaggedUnionSize { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterSize Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| When :option:`EnableCountingEnumHeuristic` is `true` and :option:`CountingEnumPrefixes` is `maxsize;last_` | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounterLast { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| last_tag, | ||
| }; | ||
|
|
||
| struct TaggedUnionLast { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterLast tag; | ||
| union { | ||
| int I; | ||
| short S; | ||
| char *C; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| enum TagWithCounterMaxSize { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| MaxSizeTag, | ||
| }; | ||
|
|
||
| struct TaggedUnionMaxSize { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterMaxSize tag; | ||
| union { | ||
| int I; | ||
| short S; | ||
| char *C; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| .. option:: StrictMode | ||
|
|
||
| When enabled, the check will also give a warning, when the number of tags | ||
| is greater than the number of union data members. | ||
|
|
||
| This option is disabled by default. | ||
|
|
||
| When :option:`StrictMode` is `false`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| struct TaggedUnion { | ||
| enum { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| } Tags; | ||
| union { | ||
| int I; | ||
| float F; | ||
| } Data; | ||
| }; | ||
|
|
||
| When :option:`StrictMode` is `true`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| struct TaggedUnion { // warning: tagged union has fewer data members (2) than tags (3) | ||
| enum { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| } Tags; | ||
| union { | ||
| int I; | ||
| float F; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_NULLABLEVALUE_H_ | ||
| #define LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_NULLABLEVALUE_H_ | ||
|
|
||
| #include "bsl_optional.h" | ||
|
|
||
| /// Mock of `bdlb::NullableValue`. | ||
| namespace BloombergLP::bdlb { | ||
|
|
||
| template <typename T> | ||
| class NullableValue : public bsl::optional<T> { | ||
| public: | ||
| constexpr NullableValue() noexcept; | ||
|
|
||
| constexpr NullableValue(bsl::nullopt_t) noexcept; | ||
|
|
||
| NullableValue(const NullableValue &) = default; | ||
|
|
||
| NullableValue(NullableValue &&) = default; | ||
|
|
||
| const T &value() const &; | ||
| T &value() &; | ||
|
|
||
| // 'operator bool' is inherited from bsl::optional | ||
|
|
||
| constexpr bool isNull() const noexcept; | ||
|
|
||
| template <typename U> | ||
| constexpr T valueOr(U &&v) const &; | ||
|
|
||
| // 'reset' is inherited from bsl::optional | ||
|
|
||
| template <typename U> NullableValue &operator=(const U &u); | ||
| }; | ||
|
|
||
|
|
||
| } // namespace BloombergLP::bdlb | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_NULLABLEVALUE_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_ | ||
| #define LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_ | ||
|
|
||
| /// Mock of `bsl::optional`. | ||
| namespace bsl { | ||
|
|
||
| // clang-format off | ||
| template <typename T> struct remove_reference { using type = T; }; | ||
| template <typename T> struct remove_reference<T&> { using type = T; }; | ||
| template <typename T> struct remove_reference<T&&> { using type = T; }; | ||
| // clang-format on | ||
|
|
||
| template <typename T> | ||
| using remove_reference_t = typename remove_reference<T>::type; | ||
|
|
||
| template <typename T> | ||
| constexpr T &&forward(remove_reference_t<T> &t) noexcept; | ||
|
|
||
| template <typename T> | ||
| constexpr T &&forward(remove_reference_t<T> &&t) noexcept; | ||
|
|
||
| template <typename T> | ||
| constexpr remove_reference_t<T> &&move(T &&x); | ||
|
|
||
| struct nullopt_t { | ||
| constexpr explicit nullopt_t() {} | ||
| }; | ||
|
|
||
| constexpr nullopt_t nullopt; | ||
|
|
||
| template <typename T> | ||
| class optional { | ||
| public: | ||
| constexpr optional() noexcept; | ||
|
|
||
| constexpr optional(nullopt_t) noexcept; | ||
|
|
||
| optional(const optional &) = default; | ||
|
|
||
| optional(optional &&) = default; | ||
|
|
||
| const T &operator*() const &; | ||
| T &operator*() &; | ||
| const T &&operator*() const &&; | ||
| T &&operator*() &&; | ||
|
|
||
| const T *operator->() const; | ||
| T *operator->(); | ||
|
|
||
| const T &value() const &; | ||
| T &value() &; | ||
| const T &&value() const &&; | ||
| T &&value() &&; | ||
|
|
||
| constexpr explicit operator bool() const noexcept; | ||
| constexpr bool has_value() const noexcept; | ||
|
|
||
| template <typename U> | ||
| constexpr T value_or(U &&v) const &; | ||
| template <typename U> | ||
| T value_or(U &&v) &&; | ||
|
|
||
| template <typename... Args> | ||
| T &emplace(Args &&...args); | ||
|
|
||
| void reset() noexcept; | ||
|
|
||
| void swap(optional &rhs) noexcept; | ||
|
|
||
| template <typename U> optional &operator=(const U &u); | ||
| }; | ||
|
|
||
| } // namespace bsl | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // RUN: %check_clang_tidy -std=c++20 %s bugprone-bitwise-pointer-cast %t | ||
|
|
||
| void memcpy(void* to, void* dst, unsigned long long size) | ||
| { | ||
| // Dummy implementation for the purpose of the test | ||
| } | ||
|
|
||
| namespace std | ||
| { | ||
| template <typename To, typename From> | ||
| To bit_cast(From from) | ||
| { | ||
| // Dummy implementation for the purpose of the test | ||
| To to{}; | ||
| return to; | ||
| } | ||
|
|
||
| using ::memcpy; | ||
| } | ||
|
|
||
| void pointer2pointer() | ||
| { | ||
| int x{}; | ||
| float bad = *std::bit_cast<float*>(&x); // UB, but looks safe due to std::bit_cast | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use 'std::bit_cast' to cast between pointers [bugprone-bitwise-pointer-cast] | ||
| float good = std::bit_cast<float>(x); // Well-defined | ||
|
|
||
| using IntPtr = int*; | ||
| using FloatPtr = float*; | ||
| IntPtr x2{}; | ||
| float bad2 = *std::bit_cast<FloatPtr>(x2); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: do not use 'std::bit_cast' to cast between pointers [bugprone-bitwise-pointer-cast] | ||
| } | ||
|
|
||
| void pointer2pointer_memcpy() | ||
| { | ||
| int x{}; | ||
| int* px{}; | ||
| float y{}; | ||
| float* py{}; | ||
|
|
||
| memcpy(&py, &px, sizeof(px)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] | ||
| std::memcpy(&py, &px, sizeof(px)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] | ||
|
|
||
| std::memcpy(&y, &x, sizeof(x)); | ||
| } | ||
|
|
||
| // Pointer-integer conversions are allowed by this check | ||
| void int2pointer() | ||
| { | ||
| unsigned long long addr{}; | ||
| float* p = std::bit_cast<float*>(addr); | ||
| std::memcpy(&p, &addr, sizeof(addr)); | ||
| } | ||
|
|
||
| void pointer2int() | ||
| { | ||
| float* p{}; | ||
| auto addr = std::bit_cast<unsigned long long>(p); | ||
| std::memcpy(&addr, &p, sizeof(p)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-bitwise-pointer-cast %t | ||
|
|
||
| void memcpy(void* to, void* dst, unsigned long long size) | ||
| { | ||
| // Dummy implementation for the purpose of the test | ||
| } | ||
|
|
||
| namespace std | ||
| { | ||
| using ::memcpy; | ||
| } | ||
|
|
||
| void pointer2pointer() | ||
| { | ||
| int x{}; | ||
| int* px{}; | ||
| float y{}; | ||
| float* py{}; | ||
|
|
||
| memcpy(&py, &px, sizeof(px)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] | ||
| std::memcpy(&py, &px, sizeof(px)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] | ||
|
|
||
| std::memcpy(&y, &x, sizeof(x)); | ||
| } | ||
|
|
||
| // Pointer-integer conversions are allowed by this check | ||
| void int2pointer() | ||
| { | ||
| unsigned long long addr{}; | ||
| float* p{}; | ||
| std::memcpy(&p, &addr, sizeof(addr)); | ||
| } | ||
|
|
||
| void pointer2int() | ||
| { | ||
| unsigned long long addr{}; | ||
| float* p{}; | ||
| std::memcpy(&addr, &p, sizeof(p)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \ | ||
| // RUN: }}' | ||
|
|
||
| // Warn when the heuristic is disabled and a suffix or a prefix is set explicitly. | ||
|
|
||
| // CHECK-MESSAGES: warning: bugprone-tagged-union-member-count: Counting enum heuristic is disabled but CountingEnumPrefixes is set | ||
| // CHECK-MESSAGES: warning: bugprone-tagged-union-member-count: Counting enum heuristic is disabled but CountingEnumSuffixes is set |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: false, \ | ||
| // RUN: }}' -- | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (3) than tags (4) | ||
| struct IncorrectBecauseHeuristicIsDisabledPrefixCase { | ||
| enum { | ||
| tags11, | ||
| tags22, | ||
| tags33, | ||
| lasttag, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsDisabledPrefixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| lasttags, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| long D; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (3) than tags (4) | ||
| struct IncorrectBecauseHeuristicIsDisabledSuffixCase { | ||
| enum { | ||
| tags11, | ||
| tags22, | ||
| tags33, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsDisabledSuffixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| long D; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \ | ||
| // RUN: }}' -- | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct IncorrectBecauseHeuristicIsEnabledPrefixCase { | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| lasttag, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsEnabledPrefixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| lasttag, | ||
| } Tags; | ||
| union { | ||
| int A; | ||
| int B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct IncorrectBecauseHeuristicIsEnabledSuffixCase { | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsEnabledSuffixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| int A; | ||
| int B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct CountingEnumCaseInsensitivityTest1 { | ||
| enum { | ||
| node_type_loop, | ||
| node_type_branch, | ||
| node_type_function, | ||
| node_type_count, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct CountingEnumCaseInsensitivityTest2 { | ||
| enum { | ||
| NODE_TYPE_LOOP, | ||
| NODE_TYPE_BRANCH, | ||
| NODE_TYPE_FUNCTION, | ||
| NODE_TYPE_COUNT, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TagWhereCountingEnumIsAliased { | ||
| enum { | ||
| tag_alias_counter1 = 1, | ||
| tag_alias_counter2 = 2, | ||
| tag_alias_counter3 = 3, | ||
| tag_alias_other_count = 3, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (2) | ||
| struct TagWithCountingEnumButOtherValueIsAliased { | ||
| enum { | ||
| tag_alias_other1 = 1, | ||
| tag_alias_other2 = 1, | ||
| tag_alias_other3 = 3, | ||
| tag_alias_other_count = 2, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TagWhereCounterIsTheSmallest { | ||
| enum { | ||
| tag_large1 = 1000, | ||
| tag_large2 = 1001, | ||
| tag_large3 = 1002, | ||
| tag_large_count = 3, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // No warnings expected, only the last enum constant can be a counting enum constant | ||
| struct TagWhereCounterLikeNameIsNotLast { | ||
| enum { | ||
| kind_count, | ||
| kind2, | ||
| last_kind1, | ||
| kind3, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \ | ||
| // RUN: }}' -- | ||
|
|
||
| union Union3 { | ||
| short *Shorts; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| // The heuristic only considers the last enum constant | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionPrefixAndSuffixMatch { | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tagscount, | ||
| lasttags | ||
| } Kind; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct TaggedUnionOnlyPrefixMatch { | ||
| enum { | ||
| prefixtag1, | ||
| prefixtag2, | ||
| lastprefixtag | ||
| } Kind; | ||
| Union3 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct TaggedUnionOnlySuffixMatch { | ||
| enum { | ||
| suffixtag1, | ||
| suffixtag2, | ||
| suffixtagcount | ||
| } Kind; | ||
| Union3 Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "maxsize;last", \ | ||
| // RUN: }}' -- | ||
|
|
||
| union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithMaxsizeAsCounterPrefix { | ||
| enum { | ||
| twc1, | ||
| twc2, | ||
| twc3, | ||
| maxsizetwc, | ||
| } Kind; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithLastAsCounterPrefix { | ||
| enum { | ||
| twc11, | ||
| twc22, | ||
| twc33, | ||
| lasttwc, | ||
| } Kind; | ||
| Union4 Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count;size", \ | ||
| // RUN: }}' -- | ||
|
|
||
| typedef union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| } union4; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithCounterCountSuffix { | ||
| enum { | ||
| twc1, | ||
| twc2, | ||
| twc3, | ||
| twc_count, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithCounterSizeSuffix { | ||
| enum { | ||
| twc11, | ||
| twc22, | ||
| twc33, | ||
| twc_size, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: }}' -- | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (2) than tags (1) | ||
| struct Incorrect { | ||
| enum { | ||
| tags1, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseStrictModeIsDisabled { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: true, \ | ||
| // RUN: }}' -- | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (2) than tags (3) | ||
| struct IncorrectBecauseStrictmodeIsEnabled { | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct Correct { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t | ||
|
|
||
| typedef enum Tags3 { | ||
| tags3_1, | ||
| tags3_2, | ||
| tags3_3, | ||
| } Tags3; | ||
|
|
||
| typedef enum Tags4 { | ||
| tags4_1, | ||
| tags4_2, | ||
| tags4_3, | ||
| tags4_4, | ||
| } Tags4; | ||
|
|
||
| typedef union Union3 { | ||
| short *Shorts; | ||
| int *Ints; | ||
| float *Floats; | ||
| } Union3; | ||
|
|
||
| typedef union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| } Union4; | ||
|
|
||
| // It is not obvious which enum is the tag for the union. | ||
| struct maybeTaggedUnion1 { // No warnings expected. | ||
| enum Tags3 TagA; | ||
| enum Tags4 TagB; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // It is not obvious which union does the tag belong to. | ||
| struct maybeTaggedUnion2 { // No warnings expected. | ||
| enum Tags3 Tag; | ||
| union Union3 DataB; | ||
| union Union3 DataA; | ||
| }; | ||
|
|
||
| // It is not obvious which union does the tag belong to. | ||
| struct maybeTaggedUnion3 { // No warnings expected. | ||
| enum Tags3 Tag; | ||
| union { | ||
| int I1; | ||
| int I2; | ||
| int I3; | ||
| }; | ||
| union { | ||
| float F1; | ||
| float F2; | ||
| float F3; | ||
| }; | ||
| }; | ||
|
|
||
| // No warnings expected, because LastATag is just an alias | ||
| struct TaggedUnionWithAliasedEnumConstant { | ||
| enum { | ||
| ATag1, | ||
| ATag2, | ||
| ATag3, | ||
| LastATag = ATag3, | ||
| } Tag; | ||
| union { | ||
| float F; | ||
| int *Ints; | ||
| char Key[8]; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithPredefinedTagAndPredefinedUnion { | ||
| enum Tags3 Tag; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithPredefinedTagAndInlineUnion { | ||
| enum Tags3 Tag; | ||
| union { | ||
| int *Ints; | ||
| char Characters[13]; | ||
| struct { | ||
| double Re; | ||
| double Im; | ||
| } Complex; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithInlineTagAndPredefinedUnion { | ||
| enum { | ||
| TaggedUnion7tag1, | ||
| TaggedUnion7tag2, | ||
| TaggedUnion7tag3, | ||
| } Tag; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithInlineTagAndInlineUnion { | ||
| enum { | ||
| TaggedUnion8tag1, | ||
| TaggedUnion8tag2, | ||
| TaggedUnion8tag3, | ||
| } Tag; | ||
| union { | ||
| int *Ints; | ||
| char Characters[13]; | ||
| struct { | ||
| double Re; | ||
| double Im; | ||
| } Complex; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructNesting { | ||
| enum Tags3 Tag; | ||
| union { | ||
| float F; | ||
| int I; | ||
| long L; | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: tagged union has more data members (4) than tags (3) | ||
| struct innerdecl { | ||
| enum Tags3 Tag; | ||
| union Union4 Data; | ||
| } Inner; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithTypedefedTagAndTypedefedUnion { | ||
| Tags3 Tag; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| #define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\ | ||
| struct Name {\ | ||
| Tag Kind;\ | ||
| Union Data;\ | ||
| } | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3) | ||
| DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,310 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t | ||
| // Test check with C++ features | ||
|
|
||
| typedef enum Tags3 { | ||
| tags3_1, | ||
| tags3_2, | ||
| tags3_3, | ||
| } Tags3; | ||
|
|
||
| typedef enum Tags4 { | ||
| tags4_1, | ||
| tags4_2, | ||
| tags4_3, | ||
| tags4_4, | ||
| } Tags4; | ||
|
|
||
| enum class Classtags3 { | ||
| classtags3_1, | ||
| classtags3_2, | ||
| classtags3_3, | ||
| }; | ||
|
|
||
| enum class Typedtags3 : unsigned int { | ||
| typedtags3_1, | ||
| typedtags3_2, | ||
| typedtags3_3, | ||
| }; | ||
|
|
||
| typedef union Union3 { | ||
| short *Shorts; | ||
| int *Ints; | ||
| float *Floats; | ||
| } Union3; | ||
|
|
||
| typedef union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| } Union4; | ||
|
|
||
| // It is not obvious which enum is the tag for the union. | ||
| class MaybeTaggedUnion1 { // No warnings expected. | ||
| enum Tags3 TagA; | ||
| enum Tags4 TagB; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // It is not obvious which union does the tag belong to. | ||
| class MaybeTaggedUnion2 { // No warnings expected. | ||
| enum Tags3 Tag; | ||
| union Union3 DataB; | ||
| union Union3 DataA; | ||
| }; | ||
|
|
||
| // It is not obvious which union does the tag belong to. | ||
| class MaybeTaggedUnion3 { // No warnings expected. | ||
| enum Tags3 Tag; | ||
| union { | ||
| int I1; | ||
| int I2; | ||
| int I3; | ||
| }; | ||
| union { | ||
| float F1; | ||
| float F2; | ||
| float F3; | ||
| }; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassPredefinedTagAndPredefinedUnion { | ||
| enum Tags3 Tag; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassPredefinedTagAndInlineUnion { | ||
| enum Tags3 Tag; | ||
| union { | ||
| int *Ints; | ||
| char Characters[13]; | ||
| class { | ||
| double Re; | ||
| double Im; | ||
| } Complex; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassInlineTagAndPredefinedUnion { | ||
| enum { | ||
| tag1, | ||
| tag2, | ||
| tag3, | ||
| } Tag; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassInlineTagAndInlineUnion { | ||
| enum { | ||
| tag1, | ||
| tag2, | ||
| tag3, | ||
| } Tag; | ||
| union { | ||
| int *Ints; | ||
| char Characters[13]; | ||
| class { | ||
| double Re; | ||
| double Im; | ||
| } Complex; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassWithNestedTaggedUnionClass { | ||
| enum Tags3 Tag; | ||
| union { | ||
| float F; | ||
| int I; | ||
| long L; | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: tagged union has more data members (4) than tags (3) | ||
| class Innerdecl { | ||
| enum Tags3 Tag; | ||
| union Union4 Data; | ||
| } Inner; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassWithTypedefedTag { | ||
| Tags3 Tag; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithEnumClass { | ||
| enum Classtags3 Tag; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClasswithEnumClass { | ||
| enum Classtags3 Tag; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithTypedEnum { | ||
| Typedtags3 Tag; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassWithTypedEnum { | ||
| Typedtags3 Tag; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct AnonymousTaggedUnionStruct { | ||
| Tags3 Tag; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| long D; | ||
| }; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassWithAnonymousUnion { | ||
| Tags3 Tag; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| long D; | ||
| }; | ||
| }; | ||
|
|
||
| namespace testnamespace { | ||
|
|
||
| enum Tags3 { | ||
| tags3_1, | ||
| tags3_2, | ||
| tags3_3, | ||
| }; | ||
|
|
||
| union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructInNamespace { | ||
| Tags3 Tags; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassInNamespace { | ||
| Tags3 Tags; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| } // namespace testnamespace | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithNamespacedTagAndUnion { | ||
| testnamespace::Tags3 Tags; | ||
| testnamespace::Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| class TaggedUnionClassWithNamespacedTagAndUnion { | ||
| testnamespace::Tags3 Tags; | ||
| testnamespace::Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| template <typename Tag, typename Union> | ||
| struct TemplatedStructWithNamespacedTagAndUnion { | ||
| Tag Kind; | ||
| Union Data; | ||
| }; | ||
|
|
||
| TemplatedStructWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedStruct3; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| template <typename Tag, typename Union> | ||
| class TemplatedClassWithNamespacedTagAndUnion { | ||
| Tag Kind; | ||
| Union Data; | ||
| }; | ||
|
|
||
| TemplatedClassWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedClass3; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| template <typename Tag, typename Union> | ||
| struct TemplatedStruct { | ||
| Tag Kind; | ||
| Union Data; | ||
| }; | ||
|
|
||
| TemplatedStruct<Tags3, Union3> TemplatedStruct1; // No warning expected | ||
| TemplatedStruct<Tags3, Union4> TemplatedStruct2; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| template <typename Tag, typename Union> | ||
| class TemplatedClass { | ||
| Tag Kind; | ||
| Union Data; | ||
| }; | ||
|
|
||
| TemplatedClass<Tags3, Union3> TemplatedClass1; // No warning expected | ||
| TemplatedClass<Tags3, Union4> TemplatedClass2; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| template <typename T> | ||
| struct TemplatedStructButTaggedUnionPartIsNotTemplated { | ||
| Tags3 Kind; | ||
| Union4 Data; | ||
| T SomethingElse; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3) | ||
| template <typename T> | ||
| class TemplatedClassButTaggedUnionPartIsNotTemplated { | ||
| Tags3 Kind; | ||
| Union4 Data; | ||
| T SomethingElse; | ||
| }; | ||
|
|
||
| #define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\ | ||
| struct Name {\ | ||
| Tag Kind;\ | ||
| Union Data;\ | ||
| } | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3) | ||
| DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro); | ||
|
|
||
| #define DECLARE_TAGGED_UNION_CLASS(Tag, Union, Name)\ | ||
| class Name {\ | ||
| Tag Kind;\ | ||
| Union Data;\ | ||
| } | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:43: warning: tagged union has more data members (4) than tags (3) | ||
| DECLARE_TAGGED_UNION_CLASS(Tags3, Union4, TaggedUnionClassFromMacro); | ||
|
|
||
| // Lambdas implicitly compile down to an unnamed CXXRecordDecl and if they have captures, | ||
| // then those become unnamed fields. | ||
| void DoNotMatchLambdas() { | ||
| enum { | ||
| A | ||
| } e; | ||
| union { | ||
| long A; | ||
| char B; | ||
| } u; | ||
| auto L = [e, u] () {}; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t | ||
|
|
||
| typedef enum Tags3 { | ||
| tags3_1, | ||
| tags3_2, | ||
| tags3_3, | ||
| } Tags3; | ||
|
|
||
| typedef enum Tags4 { | ||
| tags4_1, | ||
| tags4_2, | ||
| tags4_3, | ||
| tags4_4, | ||
| } Tags4; | ||
|
|
||
| typedef union Union3 { | ||
| short *Shorts; | ||
| int *Ints; | ||
| float *Floats; | ||
| } Union3; | ||
|
|
||
| typedef union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| } Union4; | ||
|
|
||
| // It is not obvious which enum is the tag for the union. | ||
| struct maybeTaggedUnion1 { // No warnings expected. | ||
| enum Tags3 TagA; | ||
| enum Tags4 TagB; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // It is not obvious which union does the tag belong to. | ||
| struct maybeTaggedUnion2 { // No warnings expected. | ||
| enum Tags3 Tag; | ||
| union Union3 DataB; | ||
| union Union3 DataA; | ||
| }; | ||
|
|
||
| // It is not obvious which union does the tag belong to. | ||
| struct maybeTaggedUnion3 { // No warnings expected. | ||
| enum Tags3 Tag; | ||
| union { | ||
| int I1; | ||
| int I2; | ||
| int I3; | ||
| }; | ||
| union { | ||
| float F1; | ||
| float F2; | ||
| float F3; | ||
| }; | ||
| }; | ||
|
|
||
| // No warnings expected, because LastATag is just an alias | ||
| struct TaggedUnionWithAliasedEnumConstant { | ||
| enum { | ||
| ATag1, | ||
| ATag2, | ||
| ATag3, | ||
| LastATag = ATag3, | ||
| } Tag; | ||
| union { | ||
| float F; | ||
| int *Ints; | ||
| char Key[8]; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithPredefinedTagAndPredefinedUnion { | ||
| enum Tags3 Tag; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithPredefinedTagAndInlineUnion { | ||
| enum Tags3 Tag; | ||
| union { | ||
| int *Ints; | ||
| char Characters[13]; | ||
| struct { | ||
| double Re; | ||
| double Im; | ||
| } Complex; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithInlineTagAndPredefinedUnion { | ||
| enum { | ||
| TaggedUnion7tag1, | ||
| TaggedUnion7tag2, | ||
| TaggedUnion7tag3, | ||
| } Tag; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithInlineTagAndInlineUnion { | ||
| enum { | ||
| TaggedUnion8tag1, | ||
| TaggedUnion8tag2, | ||
| TaggedUnion8tag3, | ||
| } Tag; | ||
| union { | ||
| int *Ints; | ||
| char Characters[13]; | ||
| struct { | ||
| double Re; | ||
| double Im; | ||
| } Complex; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructNesting { | ||
| enum Tags3 Tag; | ||
| union { | ||
| float F; | ||
| int I; | ||
| long L; | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: tagged union has more data members (4) than tags (3) | ||
| struct innerdecl { | ||
| enum Tags3 Tag; | ||
| union Union4 Data; | ||
| } Inner; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionStructWithTypedefedTagAndTypedefedUnion { | ||
| Tags3 Tag; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| #define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\ | ||
| struct Name {\ | ||
| Tag Kind;\ | ||
| Union Data;\ | ||
| } | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3) | ||
| DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro); |