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 | |