clang-tools  8.0.0
OwningMemoryCheck.cpp
Go to the documentation of this file.
1 //===--- OwningMemoryCheck.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 "OwningMemoryCheck.h"
11 #include "../utils/Matchers.h"
12 #include "../utils/OptionsUtils.h"
13 #include "clang/AST/ASTContext.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include <string>
16 #include <vector>
17 
18 using namespace clang::ast_matchers;
19 using namespace clang::ast_matchers::internal;
20 
21 namespace clang {
22 namespace tidy {
23 namespace cppcoreguidelines {
24 
25 // FIXME: Copied from 'NoMallocCheck.cpp'. Has to be refactored into 'util' or
26 // something like that.
27 namespace {
28 Matcher<FunctionDecl> hasAnyListedName(const std::string &FunctionNames) {
29  const std::vector<std::string> NameList =
30  utils::options::parseStringList(FunctionNames);
31  return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
32 }
33 } // namespace
34 
35 void OwningMemoryCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
36  Options.store(Opts, "LegacyResourceProducers", LegacyResourceProducers);
37  Options.store(Opts, "LegacyResourceConsumers", LegacyResourceConsumers);
38 }
39 
40 /// Match common cases, where the owner semantic is relevant, like function
41 /// calls, delete expressions and others.
42 void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
43  if (!getLangOpts().CPlusPlus11)
44  return;
45 
46  const auto OwnerDecl = typeAliasTemplateDecl(hasName("::gsl::owner"));
47  const auto IsOwnerType = hasType(OwnerDecl);
48 
49  const auto LegacyCreatorFunctions = hasAnyListedName(LegacyResourceProducers);
50  const auto LegacyConsumerFunctions =
51  hasAnyListedName(LegacyResourceConsumers);
52 
53  // Legacy functions that are use for resource management but cannot be
54  // updated to use `gsl::owner<>`, like standard C memory management.
55  const auto CreatesLegacyOwner =
56  callExpr(callee(functionDecl(LegacyCreatorFunctions)));
57  // C-style functions like `::malloc()` sometimes create owners as void*
58  // which is expected to be cast to the correct type in C++. This case
59  // must be catched explicitly.
60  const auto LegacyOwnerCast =
61  castExpr(hasSourceExpression(CreatesLegacyOwner));
62  // Functions that do manual resource management but cannot be updated to use
63  // owner. Best example is `::free()`.
64  const auto LegacyOwnerConsumers = functionDecl(LegacyConsumerFunctions);
65 
66  const auto CreatesOwner =
67  anyOf(cxxNewExpr(),
68  callExpr(callee(
69  functionDecl(returns(qualType(hasDeclaration(OwnerDecl)))))),
70  CreatesLegacyOwner, LegacyOwnerCast);
71 
72  const auto ConsideredOwner = eachOf(IsOwnerType, CreatesOwner);
73 
74  // Find delete expressions that delete non-owners.
75  Finder->addMatcher(
76  cxxDeleteExpr(
77  hasDescendant(
78  declRefExpr(unless(ConsideredOwner)).bind("deleted_variable")))
79  .bind("delete_expr"),
80  this);
81 
82  // Ignoring the implicit casts is vital because the legacy owners do not work
83  // with the 'owner<>' annotation and therefore always implicitly cast to the
84  // legacy type (even 'void *').
85  //
86  // Furthermore, legacy owner functions are assumed to use raw pointers for
87  // resources. This check assumes that all pointer arguments of a legacy
88  // functions shall be 'gsl::owner<>'.
89  Finder->addMatcher(
90  callExpr(callee(LegacyOwnerConsumers),
91  hasAnyArgument(expr(unless(ignoringImpCasts(ConsideredOwner)),
92  hasType(pointerType()))))
93  .bind("legacy_consumer"),
94  this);
95 
96  // Matching assignment to owners, with the rhs not being an owner nor creating
97  // one.
98  Finder->addMatcher(binaryOperator(matchers::isAssignmentOperator(),
99  hasLHS(IsOwnerType),
100  hasRHS(unless(ConsideredOwner)))
101  .bind("owner_assignment"),
102  this);
103 
104  // Matching initialization of owners with non-owners, nor creating owners.
105  Finder->addMatcher(
106  namedDecl(varDecl(hasInitializer(unless(ConsideredOwner)), IsOwnerType)
107  .bind("owner_initialization")),
108  this);
109 
110  const auto HasConstructorInitializerForOwner =
111  has(cxxConstructorDecl(forEachConstructorInitializer(
112  cxxCtorInitializer(
113  isMemberInitializer(), forField(IsOwnerType),
114  withInitializer(
115  // Avoid templatesdeclaration with
116  // excluding parenListExpr.
117  allOf(unless(ConsideredOwner), unless(parenListExpr()))))
118  .bind("owner_member_initializer"))));
119 
120  // Match class member initialization that expects owners, but does not get
121  // them.
122  Finder->addMatcher(cxxRecordDecl(HasConstructorInitializerForOwner), this);
123 
124  // Matching on assignment operations where the RHS is a newly created owner,
125  // but the LHS is not an owner.
126  Finder->addMatcher(binaryOperator(matchers::isAssignmentOperator(),
127  hasLHS(unless(IsOwnerType)),
128  hasRHS(CreatesOwner))
129  .bind("bad_owner_creation_assignment"),
130  this);
131 
132  // Matching on initialization operations where the initial value is a newly
133  // created owner, but the LHS is not an owner.
134  Finder->addMatcher(
135  namedDecl(varDecl(eachOf(allOf(hasInitializer(CreatesOwner),
136  unless(IsOwnerType)),
137  allOf(hasInitializer(ConsideredOwner),
138  hasType(autoType().bind("deduced_type")))))
139  .bind("bad_owner_creation_variable")),
140  this);
141 
142  // Match on all function calls that expect owners as arguments, but didn't
143  // get them.
144  Finder->addMatcher(
145  callExpr(forEachArgumentWithParam(
146  expr(unless(ConsideredOwner)).bind("expected_owner_argument"),
147  parmVarDecl(IsOwnerType))),
148  this);
149 
150  // Matching for function calls where one argument is a created owner, but the
151  // parameter type is not an owner.
152  Finder->addMatcher(callExpr(forEachArgumentWithParam(
153  expr(CreatesOwner).bind("bad_owner_creation_argument"),
154  parmVarDecl(unless(IsOwnerType))
155  .bind("bad_owner_creation_parameter"))),
156  this);
157 
158  // Matching on functions, that return an owner/resource, but don't declare
159  // their return type as owner.
160  Finder->addMatcher(
161  functionDecl(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner))
162  .bind("bad_owner_return")),
163  unless(returns(qualType(hasDeclaration(OwnerDecl)))))
164  .bind("function_decl"),
165  this);
166 
167  // Match on classes that have an owner as member, but don't declare a
168  // destructor to properly release the owner.
169  Finder->addMatcher(
170  cxxRecordDecl(
171  has(fieldDecl(IsOwnerType).bind("undestructed_owner_member")),
172  anyOf(unless(has(cxxDestructorDecl())),
173  has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted())))))
174  .bind("non_destructor_class"),
175  this);
176 }
177 
178 void OwningMemoryCheck::check(const MatchFinder::MatchResult &Result) {
179  const auto &Nodes = Result.Nodes;
180 
181  bool CheckExecuted = false;
182  CheckExecuted |= handleDeletion(Nodes);
183  CheckExecuted |= handleLegacyConsumers(Nodes);
184  CheckExecuted |= handleExpectedOwner(Nodes);
185  CheckExecuted |= handleAssignmentAndInit(Nodes);
186  CheckExecuted |= handleAssignmentFromNewOwner(Nodes);
187  CheckExecuted |= handleReturnValues(Nodes);
188  CheckExecuted |= handleOwnerMembers(Nodes);
189 
190  assert(CheckExecuted &&
191  "None of the subroutines executed, logic error in matcher!");
192 }
193 
194 bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) {
195  // Result of delete matchers.
196  const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>("delete_expr");
197  const auto *DeletedVariable =
198  Nodes.getNodeAs<DeclRefExpr>("deleted_variable");
199 
200  // Deletion of non-owners, with `delete variable;`
201  if (DeleteStmt) {
202  diag(DeleteStmt->getBeginLoc(),
203  "deleting a pointer through a type that is "
204  "not marked 'gsl::owner<>'; consider using a "
205  "smart pointer instead")
206  << DeletedVariable->getSourceRange();
207 
208  // FIXME: The declaration of the variable that was deleted can be
209  // rewritten.
210  const ValueDecl *Decl = DeletedVariable->getDecl();
211  diag(Decl->getBeginLoc(), "variable declared here", DiagnosticIDs::Note)
212  << Decl->getSourceRange();
213 
214  return true;
215  }
216  return false;
217 }
218 
219 bool OwningMemoryCheck::handleLegacyConsumers(const BoundNodes &Nodes) {
220  // Result of matching for legacy consumer-functions like `::free()`.
221  const auto *LegacyConsumer = Nodes.getNodeAs<CallExpr>("legacy_consumer");
222 
223  // FIXME: `freopen` should be handled seperately because it takes the filename
224  // as a pointer, which should not be an owner. The argument that is an owner
225  // is known and the false positive coming from the filename can be avoided.
226  if (LegacyConsumer) {
227  diag(LegacyConsumer->getBeginLoc(),
228  "calling legacy resource function without passing a 'gsl::owner<>'")
229  << LegacyConsumer->getSourceRange();
230  return true;
231  }
232  return false;
233 }
234 
235 bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) {
236  // Result of function call matchers.
237  const auto *ExpectedOwner = Nodes.getNodeAs<Expr>("expected_owner_argument");
238 
239  // Expected function argument to be owner.
240  if (ExpectedOwner) {
241  diag(ExpectedOwner->getBeginLoc(),
242  "expected argument of type 'gsl::owner<>'; got %0")
243  << ExpectedOwner->getType() << ExpectedOwner->getSourceRange();
244  return true;
245  }
246  return false;
247 }
248 
249 /// Assignment and initialization of owner variables.
250 bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) {
251  const auto *OwnerAssignment =
252  Nodes.getNodeAs<BinaryOperator>("owner_assignment");
253  const auto *OwnerInitialization =
254  Nodes.getNodeAs<VarDecl>("owner_initialization");
255  const auto *OwnerInitializer =
256  Nodes.getNodeAs<CXXCtorInitializer>("owner_member_initializer");
257 
258  // Assignments to owners.
259  if (OwnerAssignment) {
260  diag(OwnerAssignment->getBeginLoc(),
261  "expected assignment source to be of type 'gsl::owner<>'; got %0")
262  << OwnerAssignment->getRHS()->getType()
263  << OwnerAssignment->getSourceRange();
264  return true;
265  }
266 
267  // Initialization of owners.
268  if (OwnerInitialization) {
269  diag(OwnerInitialization->getBeginLoc(),
270  "expected initialization with value of type 'gsl::owner<>'; got %0")
271  << OwnerInitialization->getAnyInitializer()->getType()
272  << OwnerInitialization->getSourceRange();
273  return true;
274  }
275 
276  // Initializer of class constructors that initialize owners.
277  if (OwnerInitializer) {
278  diag(OwnerInitializer->getSourceLocation(),
279  "expected initialization of owner member variable with value of type "
280  "'gsl::owner<>'; got %0")
281  // FIXME: the expression from getInit has type 'void', but the type
282  // of the supplied argument would be of interest.
283  << OwnerInitializer->getInit()->getType()
284  << OwnerInitializer->getSourceRange();
285  return true;
286  }
287  return false;
288 }
289 
290 /// Problematic assignment and initializations, since the assigned value is a
291 /// newly created owner.
292 bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) {
293  const auto *BadOwnerAssignment =
294  Nodes.getNodeAs<BinaryOperator>("bad_owner_creation_assignment");
295  const auto *BadOwnerInitialization =
296  Nodes.getNodeAs<VarDecl>("bad_owner_creation_variable");
297 
298  const auto *BadOwnerArgument =
299  Nodes.getNodeAs<Expr>("bad_owner_creation_argument");
300  const auto *BadOwnerParameter =
301  Nodes.getNodeAs<ParmVarDecl>("bad_owner_creation_parameter");
302 
303  // Bad assignments to non-owners, where the RHS is a newly created owner.
304  if (BadOwnerAssignment) {
305  diag(BadOwnerAssignment->getBeginLoc(),
306  "assigning newly created 'gsl::owner<>' to non-owner %0")
307  << BadOwnerAssignment->getLHS()->getType()
308  << BadOwnerAssignment->getSourceRange();
309  return true;
310  }
311 
312  // Bad initialization of non-owners, where the RHS is a newly created owner.
313  if (BadOwnerInitialization) {
314  diag(BadOwnerInitialization->getBeginLoc(),
315  "initializing non-owner %0 with a newly created 'gsl::owner<>'")
316  << BadOwnerInitialization->getType()
317  << BadOwnerInitialization->getSourceRange();
318 
319  // FIXME: FixitHint to rewrite the type of the initialized variable
320  // as 'gsl::owner<OriginalType>'
321 
322  // If the type of the variable was deduced, the wrapping owner typedef is
323  // eliminated, therefore the check emits a special note for that case.
324  if (Nodes.getNodeAs<AutoType>("deduced_type")) {
325  diag(BadOwnerInitialization->getBeginLoc(),
326  "type deduction did not result in an owner", DiagnosticIDs::Note);
327  }
328  return true;
329  }
330 
331  // Function call, where one arguments is a newly created owner, but the
332  // parameter type is not.
333  if (BadOwnerArgument) {
334  assert(BadOwnerParameter &&
335  "parameter for the problematic argument not found");
336  diag(BadOwnerArgument->getBeginLoc(), "initializing non-owner argument of "
337  "type %0 with a newly created "
338  "'gsl::owner<>'")
339  << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange();
340  return true;
341  }
342  return false;
343 }
344 
345 bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) {
346  // Function return statements, that are owners/resources, but the function
347  // declaration does not declare its return value as owner.
348  const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return");
349  const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl");
350 
351  // Function return values, that should be owners but aren't.
352  if (BadReturnType) {
353  // The returned value is a resource or variable that was not annotated with
354  // owner<> and the function return type is not owner<>.
355  diag(BadReturnType->getBeginLoc(),
356  "returning a newly created resource of "
357  "type %0 or 'gsl::owner<>' from a "
358  "function whose return type is not 'gsl::owner<>'")
359  << Function->getReturnType() << BadReturnType->getSourceRange();
360 
361  // FIXME: Rewrite the return type as 'gsl::owner<OriginalType>'
362  return true;
363  }
364  return false;
365 }
366 
367 bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) {
368  // Classes, that have owners as member, but do not declare destructors
369  // accordingly.
370  const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>("non_destructor_class");
371 
372  // Classes, that contains owners, but do not declare destructors.
373  if (BadClass) {
374  const auto *DeclaredOwnerMember =
375  Nodes.getNodeAs<FieldDecl>("undestructed_owner_member");
376  assert(DeclaredOwnerMember &&
377  "match on class with bad destructor but without a declared owner");
378 
379  diag(DeclaredOwnerMember->getBeginLoc(),
380  "member variable of type 'gsl::owner<>' requires the class %0 to "
381  "implement a destructor to release the owned resource")
382  << BadClass;
383  return true;
384  }
385  return false;
386 }
387 
388 } // namespace cppcoreguidelines
389 } // namespace tidy
390 } // namespace clang
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//