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