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