Utils.h (15813B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef Utils_h__ 6 #define Utils_h__ 7 8 #include "CustomAttributes.h" 9 #include "ThirdPartyPaths.h" 10 #include "ThreadAllows.h" 11 #include "plugin.h" 12 13 #if CLANG_VERSION_FULL >= 1300 14 // Starting with clang-13 some functions from StringRef have been renamed 15 #define compare_lower compare_insensitive 16 #endif 17 18 inline StringRef getFilename(const SourceManager &SM, SourceLocation Loc) { 19 // We use the presumed location to handle #line directives and such, so the 20 // plugin is friendly to icecc / sccache users. 21 auto PL = SM.getPresumedLoc(Loc); 22 if (PL.isValid()) { 23 return StringRef(PL.getFilename()); 24 } 25 return SM.getFilename(Loc); 26 } 27 28 // Check if the given expression contains an assignment expression. 29 // This can either take the form of a Binary Operator or a 30 // Overloaded Operator Call. 31 inline bool hasSideEffectAssignment(const Expr *Expression) { 32 if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) { 33 auto BinOp = OpCallExpr->getOperator(); 34 if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) { 35 return true; 36 } 37 } else if (auto BinOpExpr = dyn_cast_or_null<BinaryOperator>(Expression)) { 38 if (BinOpExpr->isAssignmentOp()) { 39 return true; 40 } 41 } 42 43 // Recurse to children. 44 for (const Stmt *SubStmt : Expression->children()) { 45 auto ChildExpr = dyn_cast_or_null<Expr>(SubStmt); 46 if (ChildExpr && hasSideEffectAssignment(ChildExpr)) { 47 return true; 48 } 49 } 50 51 return false; 52 } 53 54 template <class T> 55 inline bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) { 56 auto &SourceManager = AC.getSourceManager(); 57 auto ExpansionLoc = SourceManager.getExpansionLoc(D.getBeginLoc()); 58 if (ExpansionLoc.isInvalid()) { 59 return false; 60 } 61 return SourceManager.isInSystemHeader(ExpansionLoc); 62 } 63 64 template <typename T> inline StringRef getNameChecked(const T &D) { 65 return D->getIdentifier() ? D->getName() : ""; 66 } 67 68 /// A cached data of whether classes are refcounted or not. 69 typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>> 70 RefCountedMap; 71 extern RefCountedMap RefCountedClasses; 72 73 inline bool classHasAddRefRelease(const CXXRecordDecl *D) { 74 const RefCountedMap::iterator &It = RefCountedClasses.find(D); 75 if (It != RefCountedClasses.end()) { 76 return It->second.second; 77 } 78 79 bool SeenAddRef = false; 80 bool SeenRelease = false; 81 for (CXXRecordDecl::method_iterator Method = D->method_begin(); 82 Method != D->method_end(); ++Method) { 83 const auto &Name = getNameChecked(Method); 84 if (Name == "AddRef") { 85 SeenAddRef = true; 86 } else if (Name == "Release") { 87 SeenRelease = true; 88 } 89 } 90 RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease); 91 return SeenAddRef && SeenRelease; 92 } 93 94 inline bool isClassRefCounted(QualType T); 95 96 inline bool isClassRefCounted(const CXXRecordDecl *D) { 97 // Normalize so that D points to the definition if it exists. 98 if (!D->hasDefinition()) 99 return false; 100 D = D->getDefinition(); 101 // Base class: anyone with AddRef/Release is obviously a refcounted class. 102 if (classHasAddRefRelease(D)) 103 return true; 104 105 // Look through all base cases to figure out if the parent is a refcounted 106 // class. 107 for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(); 108 Base != D->bases_end(); ++Base) { 109 bool Super = isClassRefCounted(Base->getType()); 110 if (Super) { 111 return true; 112 } 113 } 114 115 return false; 116 } 117 118 inline bool isClassRefCounted(QualType T) { 119 while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) 120 T = ArrTy->getElementType(); 121 CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); 122 return Clazz ? isClassRefCounted(Clazz) : false; 123 } 124 125 inline const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) { 126 for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end(); 127 Field != E; ++Field) { 128 if (getNameChecked(Field) == "mRefCnt") { 129 return *Field; 130 } 131 } 132 return 0; 133 } 134 135 inline bool typeHasVTable(QualType T) { 136 while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) 137 T = ArrTy->getElementType(); 138 CXXRecordDecl *Offender = T->getAsCXXRecordDecl(); 139 return Offender && Offender->hasDefinition() && Offender->isDynamicClass(); 140 } 141 142 inline StringRef getDeclarationNamespace(const Decl *Declaration) { 143 const DeclContext *DC = 144 Declaration->getDeclContext()->getEnclosingNamespaceContext(); 145 const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); 146 if (!ND) { 147 return ""; 148 } 149 150 while (const DeclContext *ParentDC = ND->getParent()) { 151 if (!isa<NamespaceDecl>(ParentDC)) { 152 break; 153 } 154 ND = cast<NamespaceDecl>(ParentDC); 155 } 156 157 const auto &Name = ND->getName(); 158 return Name; 159 } 160 161 inline bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) { 162 StringRef Name = getDeclarationNamespace(Declaration); 163 if (Name == "") { 164 return false; 165 } 166 167 return Name == "std" || // standard C++ lib 168 Name == "__gnu_cxx" || // gnu C++ lib 169 Name == "boost" || // boost 170 Name == "webrtc" || // upstream webrtc 171 Name == "rtc" || // upstream webrtc 'base' package 172 #if CLANG_VERSION_MAJOR >= 16 173 Name.starts_with("icu_") || // icu 174 #else 175 Name.startswith("icu_") || // icu 176 #endif 177 Name == "google" || // protobuf 178 Name == "google_breakpad" || // breakpad 179 Name == "soundtouch" || // libsoundtouch 180 Name == "stagefright" || // libstagefright 181 Name == "MacFileUtilities" || // MacFileUtilities 182 Name == "dwarf2reader" || // dwarf2reader 183 Name == "arm_ex_to_module" || // arm_ex_to_module 184 Name == "testing" || // gtest 185 Name == "Json" || // jsoncpp 186 Name == "rlbox" || // rlbox 187 Name == "v8"; // irregexp 188 } 189 190 inline bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) { 191 StringRef Name = getDeclarationNamespace(Declaration); 192 if (Name == "") { 193 return false; 194 } 195 196 return Name == "std" || // standard C++ lib 197 Name == "__gnu_cxx" || // gnu C++ lib 198 Name == "google_breakpad" || // breakpad 199 Name == "webrtc" || // libwebrtc 200 Name == "testing" || // gtest 201 Name == "rlbox"; // rlbox 202 } 203 204 inline bool isIgnoredPathForImplicitConversion(const Decl *Declaration) { 205 Declaration = Declaration->getCanonicalDecl(); 206 SourceLocation Loc = Declaration->getLocation(); 207 const SourceManager &SM = Declaration->getASTContext().getSourceManager(); 208 SmallString<1024> FileName = getFilename(SM, Loc); 209 llvm::sys::fs::make_absolute(FileName); 210 llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), 211 End = llvm::sys::path::rend(FileName); 212 for (; Begin != End; ++Begin) { 213 if (Begin->compare_lower(StringRef("graphite2")) == 0) { 214 return true; 215 } 216 if (Begin->compare_lower(StringRef("chromium")) == 0) { 217 // Ignore security/sandbox/chromium but not ipc/chromium. 218 ++Begin; 219 return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0; 220 } 221 } 222 return false; 223 } 224 225 inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, 226 const SourceManager &SM) { 227 SourceLocation Loc = Call->getBeginLoc(); 228 SmallString<1024> FileName = getFilename(SM, Loc); 229 llvm::sys::fs::make_absolute(FileName); 230 llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), 231 End = llvm::sys::path::rend(FileName); 232 for (; Begin != End; ++Begin) { 233 if (Begin->compare_lower(StringRef("angle")) == 0 || 234 Begin->compare_lower(StringRef("chromium")) == 0 || 235 Begin->compare_lower(StringRef("crashreporter")) == 0 || 236 Begin->compare_lower(StringRef("google-breakpad")) == 0 || 237 Begin->compare_lower(StringRef("gflags")) == 0 || 238 Begin->compare_lower(StringRef("harfbuzz")) == 0 || 239 Begin->compare_lower(StringRef("icu")) == 0 || 240 Begin->compare_lower(StringRef("jsoncpp")) == 0 || 241 Begin->compare_lower(StringRef("libstagefright")) == 0 || 242 Begin->compare_lower(StringRef("transport")) == 0 || 243 Begin->compare_lower(StringRef("protobuf")) == 0 || 244 Begin->compare_lower(StringRef("skia")) == 0 || 245 Begin->compare_lower(StringRef("sfntly")) == 0 || 246 // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof 247 Begin->compare_lower(StringRef("testing")) == 0) { 248 return true; 249 } 250 if (Begin->compare_lower(StringRef("webrtc")) == 0) { 251 // Ignore trunk/webrtc, but not media/webrtc 252 ++Begin; 253 return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0; 254 } 255 } 256 return false; 257 } 258 259 inline bool isInterestingDeclForImplicitConversion(const Decl *Declaration) { 260 return !isInIgnoredNamespaceForImplicitConversion(Declaration) && 261 !isIgnoredPathForImplicitConversion(Declaration); 262 } 263 264 inline bool isIgnoredExprForMustUse(const Expr *E) { 265 if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) { 266 switch (OpCall->getOperator()) { 267 case OO_Equal: 268 case OO_PlusEqual: 269 case OO_MinusEqual: 270 case OO_StarEqual: 271 case OO_SlashEqual: 272 case OO_PercentEqual: 273 case OO_CaretEqual: 274 case OO_AmpEqual: 275 case OO_PipeEqual: 276 case OO_LessLessEqual: 277 case OO_GreaterGreaterEqual: 278 return true; 279 default: 280 return false; 281 } 282 } 283 284 if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) { 285 return Op->isAssignmentOp(); 286 } 287 288 return false; 289 } 290 291 inline bool typeIsRefPtr(QualType Q) { 292 CXXRecordDecl *D = Q->getAsCXXRecordDecl(); 293 if (!D || !D->getIdentifier()) { 294 return false; 295 } 296 297 StringRef name = D->getName(); 298 if (name == "RefPtr" || name == "nsCOMPtr") { 299 return true; 300 } 301 return false; 302 } 303 304 // The method defined in clang for ignoring implicit nodes doesn't work with 305 // some AST trees. To get around this, we define our own implementation of 306 // IgnoreTrivials. 307 inline const Stmt *MaybeSkipOneTrivial(const Stmt *s) { 308 if (!s) { 309 return nullptr; 310 } 311 if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) { 312 return ewc->getSubExpr(); 313 } 314 if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) { 315 // With clang 10 and up `getTemporary` has been replaced with the more 316 // versatile `getSubExpr`. 317 #if CLANG_VERSION_FULL >= 1000 318 return mte->getSubExpr(); 319 #else 320 return mte->GetTemporaryExpr(); 321 #endif 322 } 323 if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) { 324 return bte->getSubExpr(); 325 } 326 if (auto *ce = dyn_cast<CastExpr>(s)) { 327 s = ce->getSubExpr(); 328 } 329 if (auto *pe = dyn_cast<ParenExpr>(s)) { 330 s = pe->getSubExpr(); 331 } 332 // Not a trivial. 333 return s; 334 } 335 336 inline const Stmt *IgnoreTrivials(const Stmt *s) { 337 while (true) { 338 const Stmt *newS = MaybeSkipOneTrivial(s); 339 if (newS == s) { 340 return newS; 341 } 342 s = newS; 343 } 344 345 // Unreachable 346 return nullptr; 347 } 348 349 inline const Expr *IgnoreTrivials(const Expr *e) { 350 return cast_or_null<Expr>(IgnoreTrivials(static_cast<const Stmt *>(e))); 351 } 352 353 // Returns the input if the input is not a trivial. 354 inline const Expr *MaybeSkipOneTrivial(const Expr *e) { 355 return cast_or_null<Expr>(MaybeSkipOneTrivial(static_cast<const Stmt *>(e))); 356 } 357 358 const FieldDecl *getBaseRefCntMember(QualType T); 359 360 inline const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) { 361 const FieldDecl *RefCntMember = getClassRefCntMember(D); 362 if (RefCntMember && isClassRefCounted(D)) { 363 return RefCntMember; 364 } 365 366 for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(), 367 E = D->bases_end(); 368 Base != E; ++Base) { 369 RefCntMember = getBaseRefCntMember(Base->getType()); 370 if (RefCntMember) { 371 return RefCntMember; 372 } 373 } 374 return 0; 375 } 376 377 inline const FieldDecl *getBaseRefCntMember(QualType T) { 378 while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) 379 T = ArrTy->getElementType(); 380 CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); 381 return Clazz ? getBaseRefCntMember(Clazz) : 0; 382 } 383 384 inline bool isPlacementNew(const CXXNewExpr *Expression) { 385 // Regular new expressions aren't placement new 386 if (Expression->getNumPlacementArgs() == 0) 387 return false; 388 const FunctionDecl *Declaration = Expression->getOperatorNew(); 389 if (Declaration && hasCustomAttribute<moz_heap_allocator>(Declaration)) { 390 return false; 391 } 392 return true; 393 } 394 395 extern DenseMap<StringRef, bool> InThirdPartyPathCache; 396 397 inline bool inThirdPartyPath(SourceLocation Loc, const SourceManager &SM) { 398 StringRef OriginalFileName = getFilename(SM, Loc); 399 auto pair = InThirdPartyPathCache.find(OriginalFileName); 400 if (pair != InThirdPartyPathCache.end()) { 401 return pair->second; 402 } 403 404 SmallString<1024> FileName = OriginalFileName; 405 llvm::sys::fs::make_absolute(FileName); 406 407 for (uint32_t i = 0; i < MOZ_THIRD_PARTY_PATHS_COUNT; ++i) { 408 auto PathB = sys::path::begin(FileName); 409 auto PathE = sys::path::end(FileName); 410 411 auto ThirdPartyB = sys::path::begin(MOZ_THIRD_PARTY_PATHS[i]); 412 auto ThirdPartyE = sys::path::end(MOZ_THIRD_PARTY_PATHS[i]); 413 414 for (; PathB != PathE; ++PathB) { 415 // Perform an inner loop to compare path segments, checking if the current 416 // segment is the start of the current third party path. 417 auto IPathB = PathB; 418 auto IThirdPartyB = ThirdPartyB; 419 for (; IPathB != PathE && IThirdPartyB != ThirdPartyE; 420 ++IPathB, ++IThirdPartyB) { 421 if (IPathB->compare_lower(*IThirdPartyB) != 0) { 422 break; 423 } 424 } 425 426 // We found a match! 427 if (IThirdPartyB == ThirdPartyE) { 428 InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, true)); 429 return true; 430 } 431 } 432 } 433 434 InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, false)); 435 return false; 436 } 437 438 inline bool inThirdPartyPath(const Decl *D, ASTContext *context) { 439 D = D->getCanonicalDecl(); 440 SourceLocation Loc = D->getLocation(); 441 const SourceManager &SM = context->getSourceManager(); 442 443 return inThirdPartyPath(Loc, SM); 444 } 445 446 inline CXXRecordDecl *getNonTemplateSpecializedCXXRecordDecl(QualType Q) { 447 auto *D = Q->getAsCXXRecordDecl(); 448 449 if (!D) { 450 auto TemplateQ = Q->getAs<TemplateSpecializationType>(); 451 if (!TemplateQ) { 452 return nullptr; 453 } 454 455 auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl(); 456 if (!TemplateDecl) { 457 return nullptr; 458 } 459 460 D = dyn_cast_or_null<CXXRecordDecl>(TemplateDecl->getTemplatedDecl()); 461 if (!D) { 462 return nullptr; 463 } 464 } 465 466 return D; 467 } 468 469 inline bool inThirdPartyPath(const Decl *D) { 470 return inThirdPartyPath(D, &D->getASTContext()); 471 } 472 473 inline bool inThirdPartyPath(const Stmt *S, ASTContext *context) { 474 SourceLocation Loc = S->getBeginLoc(); 475 const SourceManager &SM = context->getSourceManager(); 476 auto ExpansionLoc = SM.getExpansionLoc(Loc); 477 if (ExpansionLoc.isInvalid()) { 478 return inThirdPartyPath(Loc, SM); 479 } 480 return inThirdPartyPath(ExpansionLoc, SM); 481 } 482 483 /// Polyfill for CXXOperatorCallExpr::isInfixBinaryOp() 484 inline bool isInfixBinaryOp(const CXXOperatorCallExpr *OpCall) { 485 #if CLANG_VERSION_FULL >= 400 486 return OpCall->isInfixBinaryOp(); 487 #else 488 // Taken from clang source. 489 if (OpCall->getNumArgs() != 2) 490 return false; 491 492 switch (OpCall->getOperator()) { 493 case OO_Call: 494 case OO_Subscript: 495 return false; 496 default: 497 return true; 498 } 499 #endif 500 } 501 502 #undef compare_lower 503 #endif