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
391