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