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 /// Match common cases, where the owner semantic is relevant, like function 26 /// calls, delete expressions and others. 27 void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) { 28 if (!getLangOpts().CPlusPlus11) 29 return; 30 31 const auto OwnerDecl = typeAliasTemplateDecl(hasName("::gsl::owner")); 32 const auto IsOwnerType = hasType(OwnerDecl); 33 const auto CreatesOwner = 34 anyOf(cxxNewExpr(), callExpr(callee(functionDecl( 35 returns(qualType(hasDeclaration(OwnerDecl))))))); 36 const auto ConsideredOwner = anyOf(IsOwnerType, CreatesOwner); 37 38 // Find delete expressions that delete non-owners. 39 Finder->addMatcher( 40 cxxDeleteExpr( 41 hasDescendant( 42 declRefExpr(unless(ConsideredOwner)).bind("deleted_variable"))) 43 .bind("delete_expr"), 44 this); 45 46 // Matching assignment to owners, with the rhs not being an owner nor creating 47 // one. 48 Finder->addMatcher(binaryOperator(allOf(matchers::isAssignmentOperator(), 49 hasLHS(IsOwnerType), 50 hasRHS(unless(ConsideredOwner)))) 51 .bind("owner_assignment"), 52 this); 53 54 // Matching initialization of owners with non-owners, nor creating owners. 55 Finder->addMatcher( 56 namedDecl( 57 varDecl(allOf(hasInitializer(unless(ConsideredOwner)), IsOwnerType)) 58 .bind("owner_initialization")), 59 this); 60 61 const auto HasConstructorInitializerForOwner = 62 has(cxxConstructorDecl(forEachConstructorInitializer( 63 cxxCtorInitializer(allOf(isMemberInitializer(), forField(IsOwnerType), 64 withInitializer( 65 // Avoid templatesdeclaration with 66 // excluding parenListExpr. 67 allOf(unless(ConsideredOwner), 68 unless(parenListExpr()))))) 69 .bind("owner_member_initializer")))); 70 71 // Match class member initialization that expects owners, but does not get 72 // them. 73 Finder->addMatcher(cxxRecordDecl(HasConstructorInitializerForOwner), this); 74 75 // Matching on assignment operations where the RHS is a newly created owner, 76 // but the LHS is not an owner. 77 Finder->addMatcher( 78 binaryOperator(allOf(matchers::isAssignmentOperator(), 79 hasLHS(unless(IsOwnerType)), hasRHS(CreatesOwner))) 80 .bind("bad_owner_creation_assignment"), 81 this); 82 83 // Matching on initialization operations where the initial value is a newly 84 // created owner, but the LHS is not an owner. 85 Finder->addMatcher( 86 namedDecl(varDecl(eachOf(allOf(hasInitializer(CreatesOwner), 87 unless(IsOwnerType)), 88 allOf(hasInitializer(ConsideredOwner), 89 hasType(autoType().bind("deduced_type"))))) 90 .bind("bad_owner_creation_variable")), 91 this); 92 93 // Match on all function calls that expect owners as arguments, but didn't 94 // get them. 95 Finder->addMatcher( 96 callExpr(forEachArgumentWithParam( 97 expr(unless(ConsideredOwner)).bind("expected_owner_argument"), 98 parmVarDecl(IsOwnerType))), 99 this); 100 101 // Matching for function calls where one argument is a created owner, but the 102 // parameter type is not an owner. 103 Finder->addMatcher(callExpr(forEachArgumentWithParam( 104 expr(CreatesOwner).bind("bad_owner_creation_argument"), 105 parmVarDecl(unless(IsOwnerType)) 106 .bind("bad_owner_creation_parameter"))), 107 this); 108 109 // Matching on functions, that return an owner/resource, but don't declare 110 // their return type as owner. 111 Finder->addMatcher( 112 functionDecl( 113 allOf(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner)) 114 .bind("bad_owner_return")), 115 unless(returns(qualType(hasDeclaration(OwnerDecl)))))) 116 .bind("function_decl"), 117 this); 118 119 // Match on classes that have an owner as member, but don't declare a 120 // destructor to properly release the owner. 121 Finder->addMatcher( 122 cxxRecordDecl( 123 allOf( 124 has(fieldDecl(IsOwnerType).bind("undestructed_owner_member")), 125 anyOf(unless(has(cxxDestructorDecl())), 126 has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted())))))) 127 .bind("non_destructor_class"), 128 this); 129 } 130 131 void OwningMemoryCheck::check(const MatchFinder::MatchResult &Result) { 132 const auto &Nodes = Result.Nodes; 133 134 bool CheckExecuted = false; 135 CheckExecuted |= handleDeletion(Nodes); 136 CheckExecuted |= handleExpectedOwner(Nodes); 137 CheckExecuted |= handleAssignmentAndInit(Nodes); 138 CheckExecuted |= handleAssignmentFromNewOwner(Nodes); 139 CheckExecuted |= handleReturnValues(Nodes); 140 CheckExecuted |= handleOwnerMembers(Nodes); 141 142 assert(CheckExecuted && 143 "None of the subroutines executed, logic error in matcher!"); 144 } 145 146 bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) { 147 // Result of delete matchers. 148 const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>("delete_expr"); 149 const auto *DeletedVariable = 150 Nodes.getNodeAs<DeclRefExpr>("deleted_variable"); 151 152 // Deletion of non-owners, with `delete variable;` 153 if (DeleteStmt) { 154 diag(DeleteStmt->getLocStart(), 155 "deleting a pointer through a type that is " 156 "not marked 'gsl::owner<>'; consider using a " 157 "smart pointer instead") 158 << DeletedVariable->getSourceRange(); 159 return true; 160 } 161 return false; 162 } 163 164 bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) { 165 // Result of function call matchers. 166 const auto *ExpectedOwner = Nodes.getNodeAs<Expr>("expected_owner_argument"); 167 168 // Expected function argument to be owner. 169 if (ExpectedOwner) { 170 diag(ExpectedOwner->getLocStart(), 171 "expected argument of type 'gsl::owner<>'; got %0") 172 << ExpectedOwner->getType() << ExpectedOwner->getSourceRange(); 173 return true; 174 } 175 return false; 176 } 177 178 /// Assignment and initialization of owner variables. 179 bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) { 180 const auto *OwnerAssignment = 181 Nodes.getNodeAs<BinaryOperator>("owner_assignment"); 182 const auto *OwnerInitialization = 183 Nodes.getNodeAs<VarDecl>("owner_initialization"); 184 const auto *OwnerInitializer = 185 Nodes.getNodeAs<CXXCtorInitializer>("owner_member_initializer"); 186 187 // Assignments to owners. 188 if (OwnerAssignment) { 189 diag(OwnerAssignment->getLocStart(), 190 "expected assignment source to be of type 'gsl::owner<>'; got %0") 191 << OwnerAssignment->getRHS()->getType() 192 << OwnerAssignment->getSourceRange(); 193 return true; 194 } 195 196 // Initialization of owners. 197 if (OwnerInitialization) { 198 diag(OwnerInitialization->getLocStart(), 199 "expected initialization with value of type 'gsl::owner<>'; got %0") 200 << OwnerInitialization->getAnyInitializer()->getType() 201 << OwnerInitialization->getSourceRange(); 202 return true; 203 } 204 205 // Initializer of class constructors that initialize owners. 206 if (OwnerInitializer) { 207 diag(OwnerInitializer->getSourceLocation(), 208 "expected initialization of owner member variable with value of type " 209 "'gsl::owner<>'; got %0") 210 // FIXME: the expression from getInit has type 'void', but the type 211 // of the supplied argument would be of interest. 212 << OwnerInitializer->getInit()->getType() 213 << OwnerInitializer->getSourceRange(); 214 return true; 215 } 216 return false; 217 } 218 219 /// Problematic assignment and initializations, since the assigned value is a 220 /// newly created owner. 221 bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { 222 const auto *BadOwnerAssignment = 223 Nodes.getNodeAs<BinaryOperator>("bad_owner_creation_assignment"); 224 const auto *BadOwnerInitialization = 225 Nodes.getNodeAs<VarDecl>("bad_owner_creation_variable"); 226 227 const auto *BadOwnerArgument = 228 Nodes.getNodeAs<Expr>("bad_owner_creation_argument"); 229 const auto *BadOwnerParameter = 230 Nodes.getNodeAs<ParmVarDecl>("bad_owner_creation_parameter"); 231 232 // Bad assignments to non-owners, where the RHS is a newly created owner. 233 if (BadOwnerAssignment) { 234 diag(BadOwnerAssignment->getLocStart(), 235 "assigning newly created 'gsl::owner<>' to non-owner %0") 236 << BadOwnerAssignment->getLHS()->getType() 237 << BadOwnerAssignment->getSourceRange(); 238 return true; 239 } 240 241 // Bad initialization of non-owners, where the RHS is a newly created owner. 242 if (BadOwnerInitialization) { 243 diag(BadOwnerInitialization->getLocStart(), 244 "initializing non-owner %0 with a newly created 'gsl::owner<>'") 245 << BadOwnerInitialization->getType() 246 << BadOwnerInitialization->getSourceRange(); 247 // FIXME: FixitHint to rewrite the type if possible. 248 249 // If the type of the variable was deduced, the wrapping owner typedef is 250 // eliminated, therefore the check emits a special note for that case. 251 if (Nodes.getNodeAs<AutoType>("deduced_type")) { 252 diag(BadOwnerInitialization->getLocStart(), 253 "type deduction did not result in an owner", DiagnosticIDs::Note); 254 } 255 return true; 256 } 257 258 // Function call, where one arguments is a newly created owner, but the 259 // parameter type is not. 260 if (BadOwnerArgument) { 261 assert(BadOwnerParameter && 262 "parameter for the problematic argument not found"); 263 diag(BadOwnerArgument->getLocStart(), "initializing non-owner argument of " 264 "type %0 with a newly created " 265 "'gsl::owner<>'") 266 << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange(); 267 return true; 268 } 269 return false; 270 } 271 272 bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) { 273 // Function return statements, that are owners/resources, but the function 274 // declaration does not declare its return value as owner. 275 const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return"); 276 const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl"); 277 278 // Function return values, that should be owners but aren't. 279 if (BadReturnType) { 280 // The returned value is of type owner, but not the declared return type. 281 diag(BadReturnType->getLocStart(), 282 "returning a newly created resource of " 283 "type %0 or 'gsl::owner<>' from a " 284 "function whose return type is not 'gsl::owner<>'") 285 << Function->getReturnType() << BadReturnType->getSourceRange(); 286 // The returned value is a resource that was not annotated with owner<> and 287 // the function return type is not owner<>. 288 return true; 289 } 290 return false; 291 } 292 293 bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) { 294 // Classes, that have owners as member, but do not declare destructors 295 // accordingly. 296 const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>("non_destructor_class"); 297 298 // Classes, that contains owners, but do not declare destructors. 299 if (BadClass) { 300 const auto *DeclaredOwnerMember = 301 Nodes.getNodeAs<FieldDecl>("undestructed_owner_member"); 302 assert(DeclaredOwnerMember && 303 "match on class with bad destructor but without a declared owner"); 304 305 diag(DeclaredOwnerMember->getLocStart(), 306 "member variable of type 'gsl::owner<>' requires the class %0 to " 307 "implement a destructor to release the owned resource") 308 << BadClass; 309 return true; 310 } 311 return false; 312 } 313 314 } // namespace cppcoreguidelines 315 } // namespace tidy 316 } // namespace clang 317