| 1 | // RUN: %check_clang_tidy %s bugprone-parent-virtual-call %t |
| 2 | |
| 3 | extern int foo(); |
| 4 | |
| 5 | class A { |
| 6 | public: |
| 7 | A() = default; |
| 8 | virtual ~A() = default; |
| 9 | |
| 10 | virtual int virt_1() { return foo() + 1; } |
| 11 | virtual int virt_2() { return foo() + 2; } |
| 12 | |
| 13 | int non_virt() { return foo() + 3; } |
| 14 | static int stat() { return foo() + 4; } |
| 15 | }; |
| 16 | |
| 17 | class B : public A { |
| 18 | public: |
| 19 | B() = default; |
| 20 | |
| 21 | // Nothing to fix: calls to direct parent. |
| 22 | int virt_1() override { return A::virt_1() + 3; } |
| 23 | int virt_2() override { return A::virt_2() + 4; } |
| 24 | }; |
| 25 | |
| 26 | class C : public B { |
| 27 | public: |
| 28 | int virt_1() override { return A::virt_1() + B::virt_1(); } |
| 29 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' refers to a member overridden in subclass; did you mean 'B'? [bugprone-parent-virtual-call] |
| 30 | // CHECK-FIXES: int virt_1() override { return B::virt_1() + B::virt_1(); } |
| 31 | int virt_2() override { return A::virt_1() + B::virt_1(); } |
| 32 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'B'? {{.*}} |
| 33 | // CHECK-FIXES: int virt_2() override { return B::virt_1() + B::virt_1(); } |
| 34 | |
| 35 | // Test that non-virtual and static methods are not affected by this cherker. |
| 36 | int method_c() { return A::stat() + A::non_virt(); } |
| 37 | }; |
| 38 | |
| 39 | // Check aliased type names |
| 40 | using A1 = A; |
| 41 | typedef A A2; |
| 42 | #define A3 A |
| 43 | |
| 44 | class C2 : public B { |
| 45 | public: |
| 46 | int virt_1() override { return A1::virt_1() + A2::virt_1() + A3::virt_1(); } |
| 47 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A1::virt_1' {{.*}}; did you mean 'B'? {{.*}} |
| 48 | // CHECK-MESSAGES: :[[@LINE-2]]:49: warning: qualified name 'A2::virt_1' {{.*}}; did you mean 'B'? {{.*}} |
| 49 | // CHECK-MESSAGES: :[[@LINE-3]]:64: warning: qualified name 'A3::virt_1' {{.*}}; did you mean 'B'? {{.*}} |
| 50 | // CHECK-FIXES: int virt_1() override { return B::virt_1() + B::virt_1() + B::virt_1(); } |
| 51 | }; |
| 52 | |
| 53 | // Test that the check affects grand-grand..-parent calls too. |
| 54 | class D : public C { |
| 55 | public: |
| 56 | int virt_1() override { return A::virt_1() + B::virt_1() + D::virt_1(); } |
| 57 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'C'? {{.*}} |
| 58 | // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: qualified name 'B::virt_1' {{.*}}; did you mean 'C'? {{.*}} |
| 59 | // CHECK-FIXES: int virt_1() override { return C::virt_1() + C::virt_1() + D::virt_1(); } |
| 60 | int virt_2() override { return A::virt_1() + B::virt_1() + D::virt_1(); } |
| 61 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'C'? {{.*}} |
| 62 | // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: qualified name 'B::virt_1' {{.*}}; did you mean 'C'? {{.*}} |
| 63 | // CHECK-FIXES: int virt_2() override { return C::virt_1() + C::virt_1() + D::virt_1(); } |
| 64 | }; |
| 65 | |
| 66 | // Test classes in namespaces. |
| 67 | namespace { |
| 68 | class BN : public A { |
| 69 | public: |
| 70 | int virt_1() override { return A::virt_1() + 3; } |
| 71 | int virt_2() override { return A::virt_2() + 4; } |
| 72 | }; |
| 73 | } // namespace |
| 74 | |
| 75 | namespace N1 { |
| 76 | class A { |
| 77 | public: |
| 78 | A() = default; |
| 79 | virtual int virt_1() { return foo() + 1; } |
| 80 | virtual int virt_2() { return foo() + 2; } |
| 81 | }; |
| 82 | } // namespace N1 |
| 83 | |
| 84 | namespace N2 { |
| 85 | class BN : public N1::A { |
| 86 | public: |
| 87 | int virt_1() override { return A::virt_1() + 3; } |
| 88 | int virt_2() override { return A::virt_2() + 4; } |
| 89 | }; |
| 90 | } // namespace N2 |
| 91 | |
| 92 | class CN : public BN { |
| 93 | public: |
| 94 | int virt_1() override { return A::virt_1() + BN::virt_1(); } |
| 95 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BN'? {{.*}} |
| 96 | // CHECK-FIXES: int virt_1() override { return BN::virt_1() + BN::virt_1(); } |
| 97 | int virt_2() override { return A::virt_1() + BN::virt_1(); } |
| 98 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BN'? {{.*}} |
| 99 | // CHECK-FIXES: int virt_2() override { return BN::virt_1() + BN::virt_1(); } |
| 100 | }; |
| 101 | |
| 102 | class CNN : public N2::BN { |
| 103 | public: |
| 104 | int virt_1() override { return N1::A::virt_1() + N2::BN::virt_1(); } |
| 105 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'N1::A::virt_1' {{.*}}; did you mean 'N2::BN'? {{.*}} |
| 106 | // CHECK-FIXES: int virt_1() override { return N2::BN::virt_1() + N2::BN::virt_1(); } |
| 107 | int virt_2() override { return N1::A::virt_1() + N2::BN::virt_1(); } |
| 108 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'N1::A::virt_1' {{.*}}; did you mean 'N2::BN'? {{.*}} |
| 109 | // CHECK-FIXES: int virt_2() override { return N2::BN::virt_1() + N2::BN::virt_1(); } |
| 110 | }; |
| 111 | |
| 112 | // Test multiple inheritance fixes |
| 113 | class AA { |
| 114 | public: |
| 115 | AA() = default; |
| 116 | virtual ~AA() = default; |
| 117 | |
| 118 | virtual int virt_1() { return foo() + 1; } |
| 119 | virtual int virt_2() { return foo() + 2; } |
| 120 | |
| 121 | int non_virt() { return foo() + 3; } |
| 122 | static int stat() { return foo() + 4; } |
| 123 | }; |
| 124 | |
| 125 | class BB_1 : virtual public AA { |
| 126 | public: |
| 127 | BB_1() = default; |
| 128 | |
| 129 | // Nothing to fix: calls to parent. |
| 130 | int virt_1() override { return AA::virt_1() + 3; } |
| 131 | int virt_2() override { return AA::virt_2() + 4; } |
| 132 | }; |
| 133 | |
| 134 | class BB_2 : virtual public AA { |
| 135 | public: |
| 136 | BB_2() = default; |
| 137 | int virt_1() override { return AA::virt_1() + 3; } |
| 138 | }; |
| 139 | |
| 140 | class CC : public BB_1, public BB_2 { |
| 141 | public: |
| 142 | int virt_1() override { return AA::virt_1() + 3; } |
| 143 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'AA::virt_1' refers to a member overridden in subclasses; did you mean 'BB_1' or 'BB_2'? {{.*}} |
| 144 | // No fix available due to multiple choice of parent class. |
| 145 | }; |
| 146 | |
| 147 | // Test that virtual method is not diagnosed as not overridden in parent. |
| 148 | class BI : public A { |
| 149 | public: |
| 150 | BI() = default; |
| 151 | }; |
| 152 | |
| 153 | class CI : BI { |
| 154 | int virt_1() override { return A::virt_1(); } |
| 155 | }; |
| 156 | |
| 157 | // Test templated classes. |
| 158 | template <class F> class BF : public A { |
| 159 | public: |
| 160 | int virt_1() override { return A::virt_1() + 3; } |
| 161 | }; |
| 162 | |
| 163 | // Test templated parent class. |
| 164 | class CF : public BF<int> { |
| 165 | public: |
| 166 | int virt_1() override { return A::virt_1(); } |
| 167 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BF'? {{.*}} |
| 168 | }; |
| 169 | |
| 170 | // Test both templated class and its parent class. |
| 171 | template <class F> class DF : public BF<F> { |
| 172 | public: |
| 173 | DF() = default; |
| 174 | int virt_1() override { return A::virt_1(); } |
| 175 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BF'? {{.*}} |
| 176 | }; |
| 177 | |
| 178 | // Just to instantiate DF<F>. |
| 179 | int bar() { return (new DF<int>())->virt_1(); } |
| 180 | |