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