clang-tools  8.0.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 bugprone {
21 
22 namespace {
23 AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
24 
25 AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
26  return Node.isOverloadedOperator();
27 }
28 } // namespace
29 
30 /// Finds out if the given method overrides some method.
31 static bool isOverrideMethod(const CXXMethodDecl *MD) {
32  return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
33 }
34 
35 /// Checks whether the return types are covariant, according to
36 /// C++[class.virtual]p7.
37 ///
38 /// Similar with clang::Sema::CheckOverridingFunctionReturnType.
39 /// \returns true if the return types of BaseMD and DerivedMD are covariant.
40 static bool checkOverridingFunctionReturnType(const ASTContext *Context,
41  const CXXMethodDecl *BaseMD,
42  const CXXMethodDecl *DerivedMD) {
43  QualType BaseReturnTy = BaseMD->getType()
44  ->getAs<FunctionType>()
45  ->getReturnType()
46  .getCanonicalType();
47  QualType DerivedReturnTy = DerivedMD->getType()
48  ->getAs<FunctionType>()
49  ->getReturnType()
50  .getCanonicalType();
51 
52  if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
53  return false;
54 
55  // Check if return types are identical.
56  if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
57  return true;
58 
59  /// Check if the return types are covariant.
60 
61  // Both types must be pointers or references to classes.
62  if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
63  !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
64  return false;
65 
66  /// BTy is the class type in return type of BaseMD. For example,
67  /// B* Base::md()
68  /// While BRD is the declaration of B.
69  QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
70  QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
71 
72  const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
73  const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
74  if (DRD == nullptr || BRD == nullptr)
75  return false;
76 
77  if (!DRD->hasDefinition() || !BRD->hasDefinition())
78  return false;
79 
80  if (DRD == BRD)
81  return true;
82 
83  if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
84  // Begin checking whether the conversion from D to B is valid.
85  CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
86  /*DetectVirtual=*/false);
87 
88  // Check whether D is derived from B, and fill in a CXXBasePaths object.
89  if (!DRD->isDerivedFrom(BRD, Paths))
90  return false;
91 
92  // Check ambiguity.
93  if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
94  return false;
95 
96  // Check accessibility.
97  // FIXME: We currently only support checking if B is accessible base class
98  // of D, or D is the same class which DerivedMD is in.
99  bool IsItself =
100  DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
101  bool HasPublicAccess = false;
102  for (const auto &Path : Paths) {
103  if (Path.Access == AS_public)
104  HasPublicAccess = true;
105  }
106  if (!HasPublicAccess && !IsItself)
107  return false;
108  // End checking conversion from D to B.
109  }
110 
111  // Both pointers or references should have the same cv-qualification.
112  if (DerivedReturnTy.getLocalCVRQualifiers() !=
113  BaseReturnTy.getLocalCVRQualifiers())
114  return false;
115 
116  // The class type D should have the same cv-qualification as or less
117  // cv-qualification than the class type B.
118  if (DTy.isMoreQualifiedThan(BTy))
119  return false;
120 
121  return true;
122 }
123 
124 /// \returns decayed type for arrays and functions.
125 static QualType getDecayedType(QualType Type) {
126  if (const auto *Decayed = Type->getAs<DecayedType>())
127  return Decayed->getDecayedType();
128  return Type;
129 }
130 
131 /// \returns true if the param types are the same.
132 static bool checkParamTypes(const CXXMethodDecl *BaseMD,
133  const CXXMethodDecl *DerivedMD) {
134  unsigned NumParamA = BaseMD->getNumParams();
135  unsigned NumParamB = DerivedMD->getNumParams();
136  if (NumParamA != NumParamB)
137  return false;
138 
139  for (unsigned I = 0; I < NumParamA; I++) {
140  if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
142  DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
143  return false;
144  }
145  return true;
146 }
147 
148 /// \returns true if derived method can override base method except for the
149 /// name.
150 static bool checkOverrideWithoutName(const ASTContext *Context,
151  const CXXMethodDecl *BaseMD,
152  const CXXMethodDecl *DerivedMD) {
153  if (BaseMD->isStatic() != DerivedMD->isStatic())
154  return false;
155 
156  if (BaseMD->getType() == DerivedMD->getType())
157  return true;
158 
159  // Now the function types are not identical. Then check if the return types
160  // are covariant and if the param types are the same.
161  if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
162  return false;
163  return checkParamTypes(BaseMD, DerivedMD);
164 }
165 
166 /// Check whether BaseMD overrides DerivedMD.
167 ///
168 /// Prerequisite: the class which BaseMD is in should be a base class of that
169 /// DerivedMD is in.
170 static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
171  const CXXMethodDecl *DerivedMD) {
172  for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
173  E = DerivedMD->end_overridden_methods();
174  I != E; ++I) {
175  const CXXMethodDecl *OverriddenMD = *I;
176  if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
177  return true;
178  }
179 
180  return false;
181 }
182 
183 bool VirtualNearMissCheck::isPossibleToBeOverridden(
184  const CXXMethodDecl *BaseMD) {
185  auto Iter = PossibleMap.find(BaseMD);
186  if (Iter != PossibleMap.end())
187  return Iter->second;
188 
189  bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
190  !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
191  !BaseMD->isOverloadedOperator() &&
192  !isa<CXXConversionDecl>(BaseMD);
193  PossibleMap[BaseMD] = IsPossible;
194  return IsPossible;
195 }
196 
197 bool VirtualNearMissCheck::isOverriddenByDerivedClass(
198  const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
199  auto Key = std::make_pair(BaseMD, DerivedRD);
200  auto Iter = OverriddenMap.find(Key);
201  if (Iter != OverriddenMap.end())
202  return Iter->second;
203 
204  bool IsOverridden = false;
205  for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
206  if (!isOverrideMethod(DerivedMD))
207  continue;
208 
209  if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
210  IsOverridden = true;
211  break;
212  }
213  }
214  OverriddenMap[Key] = IsOverridden;
215  return IsOverridden;
216 }
217 
218 void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
219  if (!getLangOpts().CPlusPlus)
220  return;
221 
222  Finder->addMatcher(
223  cxxMethodDecl(
224  unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
225  cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
226  isOverloadedOperator())))
227  .bind("method"),
228  this);
229 }
230 
231 void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
232  const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
233  assert(DerivedMD);
234 
235  const ASTContext *Context = Result.Context;
236 
237  const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
238  assert(DerivedRD);
239 
240  for (const auto &BaseSpec : DerivedRD->bases()) {
241  if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
242  for (const auto *BaseMD : BaseRD->methods()) {
243  if (!isPossibleToBeOverridden(BaseMD))
244  continue;
245 
246  if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
247  continue;
248 
249  unsigned EditDistance = BaseMD->getName().edit_distance(
250  DerivedMD->getName(), EditDistanceThreshold);
251  if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
252  if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
253  // A "virtual near miss" is found.
254  auto Range = CharSourceRange::getTokenRange(
255  SourceRange(DerivedMD->getLocation()));
256 
257  bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
258  !DerivedMD->isTemplateInstantiation();
259  auto Diag =
260  diag(DerivedMD->getBeginLoc(),
261  "method '%0' has a similar name and the same signature as "
262  "virtual method '%1'; did you mean to override it?")
263  << DerivedMD->getQualifiedNameAsString()
264  << BaseMD->getQualifiedNameAsString();
265  if (ApplyFix)
266  Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
267  }
268  }
269  }
270  }
271  }
272 }
273 
274 } // namespace bugprone
275 } // namespace tidy
276 } // namespace clang
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
static bool isOverrideMethod(const CXXMethodDecl *MD)
Finds out if the given method overrides some method.
static bool checkOverrideWithoutName(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
static QualType getDecayedType(QualType Type)
std::vector< HeaderHandle > Path
std::string getReturnType(const CodeCompletionString &CCS)
Gets detail to be used as the detail field in an LSP completion item.
static bool checkParamTypes(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. ...
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
CharSourceRange Range
SourceRange for the file name.
static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Check whether BaseMD overrides DerivedMD.