clang-tools  3.8.0
VirtualNearMissCheck.cpp
Go to the documentation of this file.
1 //===--- VirtualNearMissCheck.cpp - clang-tidy-----------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "VirtualNearMissCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/CXXInheritance.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace misc {
21 
22 /// Finds out if the given method overrides some method.
23 static bool isOverrideMethod(const CXXMethodDecl *MD) {
24  return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
25 }
26 
27 /// Checks whether the return types are covariant, according to
28 /// C++[class.virtual]p7.
29 ///
30 /// Similar with clang::Sema::CheckOverridingFunctionReturnType.
31 /// \returns true if the return types of BaseMD and DerivedMD are covariant.
32 static bool checkOverridingFunctionReturnType(const ASTContext *Context,
33  const CXXMethodDecl *BaseMD,
34  const CXXMethodDecl *DerivedMD) {
35  QualType BaseReturnTy =
36  BaseMD->getType()->getAs<FunctionType>()->getReturnType();
37  QualType DerivedReturnTy =
38  DerivedMD->getType()->getAs<FunctionType>()->getReturnType();
39 
40  if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
41  return false;
42 
43  // Check if return types are identical.
44  if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
45  return true;
46 
47  /// Check if the return types are covariant.
48  /// BTy is the class type in return type of BaseMD. For example,
49  /// B* Base::md()
50  /// While BRD is the declaration of B.
51  QualType BTy, DTy;
52  const CXXRecordDecl *BRD, *DRD;
53 
54  // Both types must be pointers or references to classes.
55  if (const auto *DerivedPT = DerivedReturnTy->getAs<PointerType>()) {
56  if (const auto *BasePT = BaseReturnTy->getAs<PointerType>()) {
57  DTy = DerivedPT->getPointeeType();
58  BTy = BasePT->getPointeeType();
59  }
60  } else if (const auto *DerivedRT = DerivedReturnTy->getAs<ReferenceType>()) {
61  if (const auto *BaseRT = BaseReturnTy->getAs<ReferenceType>()) {
62  DTy = DerivedRT->getPointeeType();
63  BTy = BaseRT->getPointeeType();
64  }
65  }
66 
67  // The return types aren't either both pointers or references to a class type.
68  if (DTy.isNull())
69  return false;
70 
71  DRD = DTy->getAsCXXRecordDecl();
72  BRD = BTy->getAsCXXRecordDecl();
73  if (DRD == nullptr || BRD == nullptr)
74  return false;
75 
76  if (DRD == BRD)
77  return true;
78 
79  if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
80  // Begin checking whether the conversion from D to B is valid.
81  CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
82  /*DetectVirtual=*/false);
83 
84  // Check whether D is derived from B, and fill in a CXXBasePaths object.
85  if (!DRD->isDerivedFrom(BRD, Paths))
86  return false;
87 
88  // Check ambiguity.
89  if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
90  return false;
91 
92  // Check accessibility.
93  // FIXME: We currently only support checking if B is accessible base class
94  // of D, or D is the same class which DerivedMD is in.
95  bool IsItself = DRD == DerivedMD->getParent();
96  bool HasPublicAccess = false;
97  for (const auto &Path : Paths) {
98  if (Path.Access == AS_public)
99  HasPublicAccess = true;
100  }
101  if (!HasPublicAccess && !IsItself)
102  return false;
103  // End checking conversion from D to B.
104  }
105 
106  // Both pointers or references should have the same cv-qualification.
107  if (DerivedReturnTy.getLocalCVRQualifiers() !=
108  BaseReturnTy.getLocalCVRQualifiers())
109  return false;
110 
111  // The class type D should have the same cv-qualification as or less
112  // cv-qualification than the class type B.
113  if (DTy.isMoreQualifiedThan(BTy))
114  return false;
115 
116  return true;
117 }
118 
119 /// \returns true if the param types are the same.
120 static bool checkParamTypes(const CXXMethodDecl *BaseMD,
121  const CXXMethodDecl *DerivedMD) {
122  unsigned NumParamA = BaseMD->getNumParams();
123  unsigned NumParamB = DerivedMD->getNumParams();
124  if (NumParamA != NumParamB)
125  return false;
126 
127  for (unsigned I = 0; I < NumParamA; I++) {
128  if (BaseMD->getParamDecl(I)->getType() !=
129  DerivedMD->getParamDecl(I)->getType())
130  return false;
131  }
132  return true;
133 }
134 
135 /// \returns true if derived method can override base method except for the
136 /// name.
137 static bool checkOverrideWithoutName(const ASTContext *Context,
138  const CXXMethodDecl *BaseMD,
139  const CXXMethodDecl *DerivedMD) {
140  if (BaseMD->getTypeQualifiers() != DerivedMD->getTypeQualifiers())
141  return false;
142 
143  if (BaseMD->isStatic() != DerivedMD->isStatic())
144  return false;
145 
146  if (BaseMD->getAccess() != DerivedMD->getAccess())
147  return false;
148 
149  if (BaseMD->getType() == DerivedMD->getType())
150  return true;
151 
152  // Now the function types are not identical. Then check if the return types
153  // are covariant and if the param types are the same.
154  if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
155  return false;
156  return checkParamTypes(BaseMD, DerivedMD);
157 }
158 
159 /// Check whether BaseMD overrides DerivedMD.
160 ///
161 /// Prerequisite: the class which BaseMD is in should be a base class of that
162 /// DerivedMD is in.
163 static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
164  const CXXMethodDecl *DerivedMD) {
165  if (BaseMD->getNameAsString() != DerivedMD->getNameAsString())
166  return false;
167 
168  if (!checkParamTypes(BaseMD, DerivedMD))
169  return false;
170 
171  return true;
172 }
173 
174 /// Generate unique ID for given MethodDecl.
175 ///
176 /// The Id is used as key for 'PossibleMap'.
177 /// Typical Id: "Base::func void (void)"
178 static std::string generateMethodId(const CXXMethodDecl *MD) {
179  return MD->getQualifiedNameAsString() + " " + MD->getType().getAsString();
180 }
181 
182 bool VirtualNearMissCheck::isPossibleToBeOverridden(
183  const CXXMethodDecl *BaseMD) {
184  std::string Id = generateMethodId(BaseMD);
185  auto Iter = PossibleMap.find(Id);
186  if (Iter != PossibleMap.end())
187  return Iter->second;
188 
189  bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
190  BaseMD->isVirtual();
191  PossibleMap[Id] = IsPossible;
192  return IsPossible;
193 }
194 
195 bool VirtualNearMissCheck::isOverriddenByDerivedClass(
196  const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
197  auto Key = std::make_pair(generateMethodId(BaseMD),
198  DerivedRD->getQualifiedNameAsString());
199  auto Iter = OverriddenMap.find(Key);
200  if (Iter != OverriddenMap.end())
201  return Iter->second;
202 
203  bool IsOverridden = false;
204  for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
205  if (!isOverrideMethod(DerivedMD))
206  continue;
207 
208  if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
209  IsOverridden = true;
210  break;
211  }
212  }
213  OverriddenMap[Key] = IsOverridden;
214  return IsOverridden;
215 }
216 
217 void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
218  if (!getLangOpts().CPlusPlus)
219  return;
220 
221  Finder->addMatcher(cxxMethodDecl(unless(anyOf(isOverride(), isImplicit(),
222  cxxConstructorDecl())))
223  .bind("method"),
224  this);
225 }
226 
227 void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
228  const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
229  assert(DerivedMD != nullptr);
230 
231  if (DerivedMD->isStatic())
232  return;
233 
234  const ASTContext *Context = Result.Context;
235 
236  const auto *DerivedRD = DerivedMD->getParent();
237 
238  for (const auto &BaseSpec : DerivedRD->bases()) {
239  if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
240  for (const auto *BaseMD : BaseRD->methods()) {
241  if (!isPossibleToBeOverridden(BaseMD))
242  continue;
243 
244  if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
245  continue;
246 
247  unsigned EditDistance =
248  BaseMD->getName().edit_distance(DerivedMD->getName());
249  if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
250  if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
251  // A "virtual near miss" is found.
252  diag(DerivedMD->getLocStart(),
253  "method '%0' has a similar name and the same signature as "
254  "virtual method '%1'; did you mean to override it?")
255  << DerivedMD->getQualifiedNameAsString()
256  << BaseMD->getQualifiedNameAsString();
257  }
258  }
259  }
260  }
261  }
262 }
263 
264 } // namespace misc
265 } // namespace tidy
266 } // namespace clang
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
static bool checkParamTypes(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
std::vector< HeaderHandle > Path
static bool isOverrideMethod(const CXXMethodDecl *MD)
Finds out if the given method overrides some method.
static std::string generateMethodId(const CXXMethodDecl *MD)
Generate unique ID for given MethodDecl.
static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Check whether BaseMD overrides DerivedMD.
static const char PointerType[]
static bool checkOverrideWithoutName(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
static bool checkOverridingFunctionReturnType(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Checks whether the return types are covariant, according to C++[class.virtual]p7. ...
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
const NamedDecl * Result
Definition: USRFinder.cpp:121