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 160 // FIXME: The declaration of the variable that was deleted can be 161 // rewritten. 162 const ValueDecl *Decl = DeletedVariable->getDecl(); 163 diag(Decl->getLocStart(), "variable declared here", DiagnosticIDs::Note) 164 << Decl->getSourceRange(); 165 166 return true; 167 } 168 return false; 169 } 170 171 bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) { 172 // Result of function call matchers. 173 const auto *ExpectedOwner = Nodes.getNodeAs<Expr>("expected_owner_argument"); 174 175 // Expected function argument to be owner. 176 if (ExpectedOwner) { 177 diag(ExpectedOwner->getLocStart(), 178 "expected argument of type 'gsl::owner<>'; got %0") 179 << ExpectedOwner->getType() << ExpectedOwner->getSourceRange(); 180 return true; 181 } 182 return false; 183 } 184 185 /// Assignment and initialization of owner variables. 186 bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) { 187 const auto *OwnerAssignment = 188 Nodes.getNodeAs<BinaryOperator>("owner_assignment"); 189 const auto *OwnerInitialization = 190 Nodes.getNodeAs<VarDecl>("owner_initialization"); 191 const auto *OwnerInitializer = 192 Nodes.getNodeAs<CXXCtorInitializer>("owner_member_initializer"); 193 194 // Assignments to owners. 195 if (OwnerAssignment) { 196 diag(OwnerAssignment->getLocStart(), 197 "expected assignment source to be of type 'gsl::owner<>'; got %0") 198 << OwnerAssignment->getRHS()->getType() 199 << OwnerAssignment->getSourceRange(); 200 return true; 201 } 202 203 // Initialization of owners. 204 if (OwnerInitialization) { 205 diag(OwnerInitialization->getLocStart(), 206 "expected initialization with value of type 'gsl::owner<>'; got %0") 207 << OwnerInitialization->getAnyInitializer()->getType() 208 << OwnerInitialization->getSourceRange(); 209 return true; 210 } 211 212 // Initializer of class constructors that initialize owners. 213 if (OwnerInitializer) { 214 diag(OwnerInitializer->getSourceLocation(), 215 "expected initialization of owner member variable with value of type " 216 "'gsl::owner<>'; got %0") 217 // FIXME: the expression from getInit has type 'void', but the type 218 // of the supplied argument would be of interest. 219 << OwnerInitializer->getInit()->getType() 220 << OwnerInitializer->getSourceRange(); 221 return true; 222 } 223 return false; 224 } 225 226 /// Problematic assignment and initializations, since the assigned value is a 227 /// newly created owner. 228 bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { 229 const auto *BadOwnerAssignment = 230 Nodes.getNodeAs<BinaryOperator>("bad_owner_creation_assignment"); 231 const auto *BadOwnerInitialization = 232 Nodes.getNodeAs<VarDecl>("bad_owner_creation_variable"); 233 234 const auto *BadOwnerArgument = 235 Nodes.getNodeAs<Expr>("bad_owner_creation_argument"); 236 const auto *BadOwnerParameter = 237 Nodes.getNodeAs<ParmVarDecl>("bad_owner_creation_parameter"); 238 239 // Bad assignments to non-owners, where the RHS is a newly created owner. 240 if (BadOwnerAssignment) { 241 diag(BadOwnerAssignment->getLocStart(), 242 "assigning newly created 'gsl::owner<>' to non-owner %0") 243 << BadOwnerAssignment->getLHS()->getType() 244 << BadOwnerAssignment->getSourceRange(); 245 return true; 246 } 247 248 // Bad initialization of non-owners, where the RHS is a newly created owner. 249 if (BadOwnerInitialization) { 250 diag(BadOwnerInitialization->getLocStart(), 251 "initializing non-owner %0 with a newly created 'gsl::owner<>'") 252 << BadOwnerInitialization->getType() 253 << BadOwnerInitialization->getSourceRange(); 254 255 // FIXME: FixitHint to rewrite the type of the initialized variable 256 // as 'gsl::owner<OriginalType>' 257 258 // If the type of the variable was deduced, the wrapping owner typedef is 259 // eliminated, therefore the check emits a special note for that case. 260 if (Nodes.getNodeAs<AutoType>("deduced_type")) { 261 diag(BadOwnerInitialization->getLocStart(), 262 "type deduction did not result in an owner", DiagnosticIDs::Note); 263 } 264 return true; 265 } 266 267 // Function call, where one arguments is a newly created owner, but the 268 // parameter type is not. 269 if (BadOwnerArgument) { 270 assert(BadOwnerParameter && 271 "parameter for the problematic argument not found"); 272 diag(BadOwnerArgument->getLocStart(), "initializing non-owner argument of " 273 "type %0 with a newly created " 274 "'gsl::owner<>'") 275 << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange(); 276 return true; 277 } 278 return false; 279 } 280 281 bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) { 282 // Function return statements, that are owners/resources, but the function 283 // declaration does not declare its return value as owner. 284 const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return"); 285 const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl"); 286 287 // Function return values, that should be owners but aren't. 288 if (BadReturnType) { 289 // The returned value is a resource or variable that was not annotated with 290 // owner<> and the function return type is not owner<>. 291 diag(BadReturnType->getLocStart(), 292 "returning a newly created resource of " 293 "type %0 or 'gsl::owner<>' from a " 294 "function whose return type is not 'gsl::owner<>'") 295 << Function->getReturnType() << BadReturnType->getSourceRange(); 296 297 // FIXME: Rewrite the return type as 'gsl::owner<OriginalType>' 298 return true; 299 } 300 return false; 301 } 302 303 bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) { 304 // Classes, that have owners as member, but do not declare destructors 305 // accordingly. 306 const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>("non_destructor_class"); 307 308 // Classes, that contains owners, but do not declare destructors. 309 if (BadClass) { 310 const auto *DeclaredOwnerMember = 311 Nodes.getNodeAs<FieldDecl>("undestructed_owner_member"); 312 assert(DeclaredOwnerMember && 313 "match on class with bad destructor but without a declared owner"); 314 315 diag(DeclaredOwnerMember->getLocStart(), 316 "member variable of type 'gsl::owner<>' requires the class %0 to " 317 "implement a destructor to release the owned resource") 318 << BadClass; 319 return true; 320 } 321 return false; 322 } 323 324 } // namespace cppcoreguidelines 325 } // namespace tidy 326 } // namespace clang 327