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 assert(CheckExecuted && 196 "None of the subroutines executed, logic error in matcher!"); 197 } 198 199 bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) { 200 // Result of delete matchers. 201 const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>("delete_expr"); 202 const auto *DeletedVariable = 203 Nodes.getNodeAs<DeclRefExpr>("deleted_variable"); 204 205 // Deletion of non-owners, with `delete variable;` 206 if (DeleteStmt) { 207 diag(DeleteStmt->getBeginLoc(), 208 "deleting a pointer through a type that is " 209 "not marked 'gsl::owner<>'; consider using a " 210 "smart pointer instead") 211 << DeletedVariable->getSourceRange(); 212 213 // FIXME: The declaration of the variable that was deleted can be 214 // rewritten. 215 const ValueDecl *Decl = DeletedVariable->getDecl(); 216 diag(Decl->getBeginLoc(), "variable declared here", DiagnosticIDs::Note) 217 << Decl->getSourceRange(); 218 219 return true; 220 } 221 return false; 222 } 223 224 bool OwningMemoryCheck::handleLegacyConsumers(const BoundNodes &Nodes) { 225 // Result of matching for legacy consumer-functions like `::free()`. 226 const auto *LegacyConsumer = Nodes.getNodeAs<CallExpr>("legacy_consumer"); 227 228 // FIXME: `freopen` should be handled separately because it takes the filename 229 // as a pointer, which should not be an owner. The argument that is an owner 230 // is known and the false positive coming from the filename can be avoided. 231 if (LegacyConsumer) { 232 diag(LegacyConsumer->getBeginLoc(), 233 "calling legacy resource function without passing a 'gsl::owner<>'") 234 << LegacyConsumer->getSourceRange(); 235 return true; 236 } 237 return false; 238 } 239 240 bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) { 241 // Result of function call matchers. 242 const auto *ExpectedOwner = Nodes.getNodeAs<Expr>("expected_owner_argument"); 243 244 // Expected function argument to be owner. 245 if (ExpectedOwner) { 246 diag(ExpectedOwner->getBeginLoc(), 247 "expected argument of type 'gsl::owner<>'; got %0") 248 << ExpectedOwner->getType() << ExpectedOwner->getSourceRange(); 249 return true; 250 } 251 return false; 252 } 253 254 /// Assignment and initialization of owner variables. 255 bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) { 256 const auto *OwnerAssignment = 257 Nodes.getNodeAs<BinaryOperator>("owner_assignment"); 258 const auto *OwnerInitialization = 259 Nodes.getNodeAs<VarDecl>("owner_initialization"); 260 const auto *OwnerInitializer = 261 Nodes.getNodeAs<CXXCtorInitializer>("owner_member_initializer"); 262 263 // Assignments to owners. 264 if (OwnerAssignment) { 265 diag(OwnerAssignment->getBeginLoc(), 266 "expected assignment source to be of type 'gsl::owner<>'; got %0") 267 << OwnerAssignment->getRHS()->getType() 268 << OwnerAssignment->getSourceRange(); 269 return true; 270 } 271 272 // Initialization of owners. 273 if (OwnerInitialization) { 274 diag(OwnerInitialization->getBeginLoc(), 275 "expected initialization with value of type 'gsl::owner<>'; got %0") 276 << OwnerInitialization->getAnyInitializer()->getType() 277 << OwnerInitialization->getSourceRange(); 278 return true; 279 } 280 281 // Initializer of class constructors that initialize owners. 282 if (OwnerInitializer) { 283 diag(OwnerInitializer->getSourceLocation(), 284 "expected initialization of owner member variable with value of type " 285 "'gsl::owner<>'; got %0") 286 // FIXME: the expression from getInit has type 'void', but the type 287 // of the supplied argument would be of interest. 288 << OwnerInitializer->getInit()->getType() 289 << OwnerInitializer->getSourceRange(); 290 return true; 291 } 292 return false; 293 } 294 295 /// Problematic assignment and initializations, since the assigned value is a 296 /// newly created owner. 297 bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { 298 const auto *BadOwnerAssignment = 299 Nodes.getNodeAs<BinaryOperator>("bad_owner_creation_assignment"); 300 const auto *BadOwnerInitialization = 301 Nodes.getNodeAs<VarDecl>("bad_owner_creation_variable"); 302 303 const auto *BadOwnerArgument = 304 Nodes.getNodeAs<Expr>("bad_owner_creation_argument"); 305 const auto *BadOwnerParameter = 306 Nodes.getNodeAs<ParmVarDecl>("bad_owner_creation_parameter"); 307 308 // Bad assignments to non-owners, where the RHS is a newly created owner. 309 if (BadOwnerAssignment) { 310 diag(BadOwnerAssignment->getBeginLoc(), 311 "assigning newly created 'gsl::owner<>' to non-owner %0") 312 << BadOwnerAssignment->getLHS()->getType() 313 << BadOwnerAssignment->getSourceRange(); 314 return true; 315 } 316 317 // Bad initialization of non-owners, where the RHS is a newly created owner. 318 if (BadOwnerInitialization) { 319 diag(BadOwnerInitialization->getBeginLoc(), 320 "initializing non-owner %0 with a newly created 'gsl::owner<>'") 321 << BadOwnerInitialization->getType() 322 << BadOwnerInitialization->getSourceRange(); 323 324 // FIXME: FixitHint to rewrite the type of the initialized variable 325 // as 'gsl::owner<OriginalType>' 326 327 // If the type of the variable was deduced, the wrapping owner typedef is 328 // eliminated, therefore the check emits a special note for that case. 329 if (Nodes.getNodeAs<AutoType>("deduced_type")) { 330 diag(BadOwnerInitialization->getBeginLoc(), 331 "type deduction did not result in an owner", DiagnosticIDs::Note); 332 } 333 return true; 334 } 335 336 // Function call, where one arguments is a newly created owner, but the 337 // parameter type is not. 338 if (BadOwnerArgument) { 339 assert(BadOwnerParameter && 340 "parameter for the problematic argument not found"); 341 diag(BadOwnerArgument->getBeginLoc(), "initializing non-owner argument of " 342 "type %0 with a newly created " 343 "'gsl::owner<>'") 344 << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange(); 345 return true; 346 } 347 return false; 348 } 349 350 bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) { 351 // Function return statements, that are owners/resources, but the function 352 // declaration does not declare its return value as owner. 353 const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return"); 354 const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl"); 355 356 // Function return values, that should be owners but aren't. 357 if (BadReturnType) { 358 // The returned value is a resource or variable that was not annotated with 359 // owner<> and the function return type is not owner<>. 360 diag(BadReturnType->getBeginLoc(), 361 "returning a newly created resource of " 362 "type %0 or 'gsl::owner<>' from a " 363 "function whose return type is not 'gsl::owner<>'") 364 << Function->getReturnType() << BadReturnType->getSourceRange(); 365 366 // FIXME: Rewrite the return type as 'gsl::owner<OriginalType>' 367 return true; 368 } 369 return false; 370 } 371 372 bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) { 373 // Classes, that have owners as member, but do not declare destructors 374 // accordingly. 375 const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>("non_destructor_class"); 376 377 // Classes, that contains owners, but do not declare destructors. 378 if (BadClass) { 379 const auto *DeclaredOwnerMember = 380 Nodes.getNodeAs<FieldDecl>("undestructed_owner_member"); 381 assert(DeclaredOwnerMember && 382 "match on class with bad destructor but without a declared owner"); 383 384 diag(DeclaredOwnerMember->getBeginLoc(), 385 "member variable of type 'gsl::owner<>' requires the class %0 to " 386 "implement a destructor to release the owned resource") 387 << BadClass; 388 return true; 389 } 390 return false; 391 } 392 393 } // namespace cppcoreguidelines 394 } // namespace tidy 395 } // namespace clang 396