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(ast_type_traits::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(ast_type_traits::TK_AsIs, 87 callExpr(callee(LegacyOwnerConsumers), 88 hasAnyArgument( 89 expr(unless(ignoringImpCasts(ConsideredOwner)), 90 hasType(pointerType())))) 91 .bind("legacy_consumer")), 92 this); 93 94 // Matching assignment to owners, with the rhs not being an owner nor creating 95 // one. 96 Finder->addMatcher( 97 traverse(ast_type_traits::TK_AsIs, 98 binaryOperator(isAssignmentOperator(), hasLHS(IsOwnerType), 99 hasRHS(unless(ConsideredOwner))) 100 .bind("owner_assignment")), 101 this); 102 103 // Matching initialization of owners with non-owners, nor creating owners. 104 Finder->addMatcher( 105 traverse(ast_type_traits::TK_AsIs, 106 namedDecl( 107 varDecl(hasInitializer(unless(ConsideredOwner)), IsOwnerType) 108 .bind("owner_initialization"))), 109 this); 110 111 const auto HasConstructorInitializerForOwner = 112 has(cxxConstructorDecl(forEachConstructorInitializer( 113 cxxCtorInitializer( 114 isMemberInitializer(), forField(IsOwnerType), 115 withInitializer( 116 // Avoid templatesdeclaration with 117 // excluding parenListExpr. 118 allOf(unless(ConsideredOwner), unless(parenListExpr())))) 119 .bind("owner_member_initializer")))); 120 121 // Match class member initialization that expects owners, but does not get 122 // them. 123 Finder->addMatcher(traverse(ast_type_traits::TK_AsIs, 124 cxxRecordDecl(HasConstructorInitializerForOwner)), 125 this); 126 127 // Matching on assignment operations where the RHS is a newly created owner, 128 // but the LHS is not an owner. 129 Finder->addMatcher(binaryOperator(isAssignmentOperator(), 130 hasLHS(unless(IsOwnerType)), 131 hasRHS(CreatesOwner)) 132 .bind("bad_owner_creation_assignment"), 133 this); 134 135 // Matching on initialization operations where the initial value is a newly 136 // created owner, but the LHS is not an owner. 137 Finder->addMatcher( 138 traverse( 139 ast_type_traits::TK_AsIs, 140 namedDecl( 141 varDecl(eachOf(allOf(hasInitializer(CreatesOwner), 142 unless(IsOwnerType)), 143 allOf(hasInitializer(ConsideredOwner), 144 hasType(autoType().bind("deduced_type"))))) 145 .bind("bad_owner_creation_variable"))), 146 this); 147 148 // Match on all function calls that expect owners as arguments, but didn't 149 // get them. 150 Finder->addMatcher( 151 callExpr(forEachArgumentWithParam( 152 expr(unless(ConsideredOwner)).bind("expected_owner_argument"), 153 parmVarDecl(IsOwnerType))), 154 this); 155 156 // Matching for function calls where one argument is a created owner, but the 157 // parameter type is not an owner. 158 Finder->addMatcher(callExpr(forEachArgumentWithParam( 159 expr(CreatesOwner).bind("bad_owner_creation_argument"), 160 parmVarDecl(unless(IsOwnerType)) 161 .bind("bad_owner_creation_parameter"))), 162 this); 163 164 // Matching on functions, that return an owner/resource, but don't declare 165 // their return type as owner. 166 Finder->addMatcher( 167 functionDecl(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner)) 168 .bind("bad_owner_return")), 169 unless(returns(qualType(hasDeclaration(OwnerDecl))))) 170 .bind("function_decl"), 171 this); 172 173 // Match on classes that have an owner as member, but don't declare a 174 // destructor to properly release the owner. 175 Finder->addMatcher( 176 cxxRecordDecl( 177 has(fieldDecl(IsOwnerType).bind("undestructed_owner_member")), 178 anyOf(unless(has(cxxDestructorDecl())), 179 has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted()))))) 180 .bind("non_destructor_class"), 181 this); 182 } 183 184 void OwningMemoryCheck::check(const MatchFinder::MatchResult &Result) { 185 const auto &Nodes = Result.Nodes; 186 187 bool CheckExecuted = false; 188 CheckExecuted |= handleDeletion(Nodes); 189 CheckExecuted |= handleLegacyConsumers(Nodes); 190 CheckExecuted |= handleExpectedOwner(Nodes); 191 CheckExecuted |= handleAssignmentAndInit(Nodes); 192 CheckExecuted |= handleAssignmentFromNewOwner(Nodes); 193 CheckExecuted |= handleReturnValues(Nodes); 194 CheckExecuted |= handleOwnerMembers(Nodes); 195 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