annotate clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp @ 173:0572611fdcc8 llvm10 llvm12

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