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 /// Match common cases, where the owner semantic is relevant, like function
26 /// calls, delete expressions and others.
27 void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
28   if (!getLangOpts().CPlusPlus11)
29     return;
30 
31   const auto OwnerDecl = typeAliasTemplateDecl(hasName("::gsl::owner"));
32   const auto IsOwnerType = hasType(OwnerDecl);
33   const auto CreatesOwner =
34       anyOf(cxxNewExpr(), callExpr(callee(functionDecl(
35                               returns(qualType(hasDeclaration(OwnerDecl)))))));
36   const auto ConsideredOwner = anyOf(IsOwnerType, CreatesOwner);
37 
38   // Find delete expressions that delete non-owners.
39   Finder->addMatcher(
40       cxxDeleteExpr(
41           hasDescendant(
42               declRefExpr(unless(ConsideredOwner)).bind("deleted_variable")))
43           .bind("delete_expr"),
44       this);
45 
46   // Matching assignment to owners, with the rhs not being an owner nor creating
47   // one.
48   Finder->addMatcher(binaryOperator(allOf(matchers::isAssignmentOperator(),
49                                           hasLHS(IsOwnerType),
50                                           hasRHS(unless(ConsideredOwner))))
51                          .bind("owner_assignment"),
52                      this);
53 
54   // Matching initialization of owners with non-owners, nor creating owners.
55   Finder->addMatcher(
56       namedDecl(
57           varDecl(allOf(hasInitializer(unless(ConsideredOwner)), IsOwnerType))
58               .bind("owner_initialization")),
59       this);
60 
61   const auto HasConstructorInitializerForOwner =
62       has(cxxConstructorDecl(forEachConstructorInitializer(
63           cxxCtorInitializer(allOf(isMemberInitializer(), forField(IsOwnerType),
64                                    withInitializer(
65                                        // Avoid templatesdeclaration with
66                                        // excluding parenListExpr.
67                                        allOf(unless(ConsideredOwner),
68                                              unless(parenListExpr())))))
69               .bind("owner_member_initializer"))));
70 
71   // Match class member initialization that expects owners, but does not get
72   // them.
73   Finder->addMatcher(cxxRecordDecl(HasConstructorInitializerForOwner), this);
74 
75   // Matching on assignment operations where the RHS is a newly created owner,
76   // but the LHS is not an owner.
77   Finder->addMatcher(
78       binaryOperator(allOf(matchers::isAssignmentOperator(),
79                            hasLHS(unless(IsOwnerType)), hasRHS(CreatesOwner)))
80           .bind("bad_owner_creation_assignment"),
81       this);
82 
83   // Matching on initialization operations where the initial value is a newly
84   // created owner, but the LHS is not an owner.
85   Finder->addMatcher(
86       namedDecl(varDecl(eachOf(allOf(hasInitializer(CreatesOwner),
87                                      unless(IsOwnerType)),
88                                allOf(hasInitializer(ConsideredOwner),
89                                      hasType(autoType().bind("deduced_type")))))
90                     .bind("bad_owner_creation_variable")),
91       this);
92 
93   // Match on all function calls that expect owners as arguments, but didn't
94   // get them.
95   Finder->addMatcher(
96       callExpr(forEachArgumentWithParam(
97           expr(unless(ConsideredOwner)).bind("expected_owner_argument"),
98           parmVarDecl(IsOwnerType))),
99       this);
100 
101   // Matching for function calls where one argument is a created owner, but the
102   // parameter type is not an owner.
103   Finder->addMatcher(callExpr(forEachArgumentWithParam(
104                          expr(CreatesOwner).bind("bad_owner_creation_argument"),
105                          parmVarDecl(unless(IsOwnerType))
106                              .bind("bad_owner_creation_parameter"))),
107                      this);
108 
109   // Matching on functions, that return an owner/resource, but don't declare
110   // their return type as owner.
111   Finder->addMatcher(
112       functionDecl(
113           allOf(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner))
114                                   .bind("bad_owner_return")),
115                 unless(returns(qualType(hasDeclaration(OwnerDecl))))))
116           .bind("function_decl"),
117       this);
118 
119   // Match on classes that have an owner as member, but don't declare a
120   // destructor to properly release the owner.
121   Finder->addMatcher(
122       cxxRecordDecl(
123           allOf(
124               has(fieldDecl(IsOwnerType).bind("undestructed_owner_member")),
125               anyOf(unless(has(cxxDestructorDecl())),
126                     has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted()))))))
127           .bind("non_destructor_class"),
128       this);
129 }
130 
131 void OwningMemoryCheck::check(const MatchFinder::MatchResult &Result) {
132   const auto &Nodes = Result.Nodes;
133 
134   bool CheckExecuted = false;
135   CheckExecuted |= handleDeletion(Nodes);
136   CheckExecuted |= handleExpectedOwner(Nodes);
137   CheckExecuted |= handleAssignmentAndInit(Nodes);
138   CheckExecuted |= handleAssignmentFromNewOwner(Nodes);
139   CheckExecuted |= handleReturnValues(Nodes);
140   CheckExecuted |= handleOwnerMembers(Nodes);
141 
142   assert(CheckExecuted &&
143          "None of the subroutines executed, logic error in matcher!");
144 }
145 
146 bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) {
147   // Result of delete matchers.
148   const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>("delete_expr");
149   const auto *DeletedVariable =
150       Nodes.getNodeAs<DeclRefExpr>("deleted_variable");
151 
152   // Deletion of non-owners, with `delete variable;`
153   if (DeleteStmt) {
154     diag(DeleteStmt->getLocStart(),
155          "deleting a pointer through a type that is "
156          "not marked 'gsl::owner<>'; consider using a "
157          "smart pointer instead")
158         << DeletedVariable->getSourceRange();
159 
160     // FIXME: The declaration of the variable that was deleted can be
161     // rewritten.
162     const ValueDecl *Decl = DeletedVariable->getDecl();
163     diag(Decl->getLocStart(), "variable declared here", DiagnosticIDs::Note)
164         << Decl->getSourceRange();
165 
166     return true;
167   }
168   return false;
169 }
170 
171 bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) {
172   // Result of function call matchers.
173   const auto *ExpectedOwner = Nodes.getNodeAs<Expr>("expected_owner_argument");
174 
175   // Expected function argument to be owner.
176   if (ExpectedOwner) {
177     diag(ExpectedOwner->getLocStart(),
178          "expected argument of type 'gsl::owner<>'; got %0")
179         << ExpectedOwner->getType() << ExpectedOwner->getSourceRange();
180     return true;
181   }
182   return false;
183 }
184 
185 /// Assignment and initialization of owner variables.
186 bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) {
187   const auto *OwnerAssignment =
188       Nodes.getNodeAs<BinaryOperator>("owner_assignment");
189   const auto *OwnerInitialization =
190       Nodes.getNodeAs<VarDecl>("owner_initialization");
191   const auto *OwnerInitializer =
192       Nodes.getNodeAs<CXXCtorInitializer>("owner_member_initializer");
193 
194   // Assignments to owners.
195   if (OwnerAssignment) {
196     diag(OwnerAssignment->getLocStart(),
197          "expected assignment source to be of type 'gsl::owner<>'; got %0")
198         << OwnerAssignment->getRHS()->getType()
199         << OwnerAssignment->getSourceRange();
200     return true;
201   }
202 
203   // Initialization of owners.
204   if (OwnerInitialization) {
205     diag(OwnerInitialization->getLocStart(),
206          "expected initialization with value of type 'gsl::owner<>'; got %0")
207         << OwnerInitialization->getAnyInitializer()->getType()
208         << OwnerInitialization->getSourceRange();
209     return true;
210   }
211 
212   // Initializer of class constructors that initialize owners.
213   if (OwnerInitializer) {
214     diag(OwnerInitializer->getSourceLocation(),
215          "expected initialization of owner member variable with value of type "
216          "'gsl::owner<>'; got %0")
217         // FIXME: the expression from getInit has type 'void', but the type
218         // of the supplied argument would be of interest.
219         << OwnerInitializer->getInit()->getType()
220         << OwnerInitializer->getSourceRange();
221     return true;
222   }
223   return false;
224 }
225 
226 /// Problematic assignment and initializations, since the assigned value is a
227 /// newly created owner.
228 bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) {
229   const auto *BadOwnerAssignment =
230       Nodes.getNodeAs<BinaryOperator>("bad_owner_creation_assignment");
231   const auto *BadOwnerInitialization =
232       Nodes.getNodeAs<VarDecl>("bad_owner_creation_variable");
233 
234   const auto *BadOwnerArgument =
235       Nodes.getNodeAs<Expr>("bad_owner_creation_argument");
236   const auto *BadOwnerParameter =
237       Nodes.getNodeAs<ParmVarDecl>("bad_owner_creation_parameter");
238 
239   // Bad assignments to non-owners, where the RHS is a newly created owner.
240   if (BadOwnerAssignment) {
241     diag(BadOwnerAssignment->getLocStart(),
242          "assigning newly created 'gsl::owner<>' to non-owner %0")
243         << BadOwnerAssignment->getLHS()->getType()
244         << BadOwnerAssignment->getSourceRange();
245     return true;
246   }
247 
248   // Bad initialization of non-owners, where the RHS is a newly created owner.
249   if (BadOwnerInitialization) {
250     diag(BadOwnerInitialization->getLocStart(),
251          "initializing non-owner %0 with a newly created 'gsl::owner<>'")
252         << BadOwnerInitialization->getType()
253         << BadOwnerInitialization->getSourceRange();
254 
255     // FIXME: FixitHint to rewrite the type of the initialized variable
256     // as 'gsl::owner<OriginalType>'
257 
258     // If the type of the variable was deduced, the wrapping owner typedef is
259     // eliminated, therefore the check emits a special note for that case.
260     if (Nodes.getNodeAs<AutoType>("deduced_type")) {
261       diag(BadOwnerInitialization->getLocStart(),
262            "type deduction did not result in an owner", DiagnosticIDs::Note);
263     }
264     return true;
265   }
266 
267   // Function call, where one arguments is a newly created owner, but the
268   // parameter type is not.
269   if (BadOwnerArgument) {
270     assert(BadOwnerParameter &&
271            "parameter for the problematic argument not found");
272     diag(BadOwnerArgument->getLocStart(), "initializing non-owner argument of "
273                                           "type %0 with a newly created "
274                                           "'gsl::owner<>'")
275         << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange();
276     return true;
277   }
278   return false;
279 }
280 
281 bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) {
282   // Function return statements, that are owners/resources, but the function
283   // declaration does not declare its return value as owner.
284   const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return");
285   const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl");
286 
287   // Function return values, that should be owners but aren't.
288   if (BadReturnType) {
289     // The returned value is a resource or variable that was not annotated with
290     // owner<> and the function return type is not owner<>.
291     diag(BadReturnType->getLocStart(),
292          "returning a newly created resource of "
293          "type %0 or 'gsl::owner<>' from a "
294          "function whose return type is not 'gsl::owner<>'")
295         << Function->getReturnType() << BadReturnType->getSourceRange();
296 
297     // FIXME: Rewrite the return type as 'gsl::owner<OriginalType>'
298     return true;
299   }
300   return false;
301 }
302 
303 bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) {
304   // Classes, that have owners as member, but do not declare destructors
305   // accordingly.
306   const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>("non_destructor_class");
307 
308   // Classes, that contains owners, but do not declare destructors.
309   if (BadClass) {
310     const auto *DeclaredOwnerMember =
311         Nodes.getNodeAs<FieldDecl>("undestructed_owner_member");
312     assert(DeclaredOwnerMember &&
313            "match on class with bad destructor but without a declared owner");
314 
315     diag(DeclaredOwnerMember->getLocStart(),
316          "member variable of type 'gsl::owner<>' requires the class %0 to "
317          "implement a destructor to release the owned resource")
318         << BadClass;
319     return true;
320   }
321   return false;
322 }
323 
324 } // namespace cppcoreguidelines
325 } // namespace tidy
326 } // namespace clang
327