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
virt_1()10 virtual int virt_1() { return foo() + 1; }
virt_2()11 virtual int virt_2() { return foo() + 2; }
12
non_virt()13 int non_virt() { return foo() + 3; }
stat()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.
virt_1()22 int virt_1() override { return A::virt_1() + 3; }
virt_2()23 int virt_2() override { return A::virt_2() + 4; }
24 };
25
26 class C : public B {
27 public:
virt_1()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(); }
virt_2()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.
method_c()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:
virt_1()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:
virt_1()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(); }
virt_2()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:
virt_1()70 int virt_1() override { return A::virt_1() + 3; }
virt_2()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;
virt_1()79 virtual int virt_1() { return foo() + 1; }
virt_2()80 virtual int virt_2() { return foo() + 2; }
81 };
82 } // namespace N1
83
84 namespace N2 {
85 class BN : public N1::A {
86 public:
virt_1()87 int virt_1() override { return A::virt_1() + 3; }
virt_2()88 int virt_2() override { return A::virt_2() + 4; }
89 };
90 } // namespace N2
91
92 class CN : public BN {
93 public:
virt_1()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(); }
virt_2()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:
virt_1()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(); }
virt_2()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
virt_1()118 virtual int virt_1() { return foo() + 1; }
virt_2()119 virtual int virt_2() { return foo() + 2; }
120
non_virt()121 int non_virt() { return foo() + 3; }
stat()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.
virt_1()130 int virt_1() override { return AA::virt_1() + 3; }
virt_2()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;
virt_1()137 int virt_1() override { return AA::virt_1() + 3; }
138 };
139
140 class CC : public BB_1, public BB_2 {
141 public:
virt_1()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 {
virt_1()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:
virt_1()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:
virt_1()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;
virt_1()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>.
bar()179 int bar() { return (new DF<int>())->virt_1(); }
180