CocoaConventions.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. //===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--//
  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. // This file implements cocoa naming convention analysis.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
  13. #include "clang/AST/Decl.h"
  14. #include "clang/AST/DeclObjC.h"
  15. #include "clang/AST/Type.h"
  16. #include "clang/Basic/CharInfo.h"
  17. #include "llvm/ADT/StringExtras.h"
  18. #include "llvm/Support/ErrorHandling.h"
  19. using namespace clang;
  20. using namespace ento;
  21. bool cocoa::isRefType(QualType RetTy, StringRef Prefix,
  22. StringRef Name) {
  23. // Recursively walk the typedef stack, allowing typedefs of reference types.
  24. while (const TypedefType *TD = RetTy->getAs<TypedefType>()) {
  25. StringRef TDName = TD->getDecl()->getIdentifier()->getName();
  26. if (TDName.startswith(Prefix) && TDName.endswith("Ref"))
  27. return true;
  28. // XPC unfortunately uses CF-style function names, but aren't CF types.
  29. if (TDName.startswith("xpc_"))
  30. return false;
  31. RetTy = TD->getDecl()->getUnderlyingType();
  32. }
  33. if (Name.empty())
  34. return false;
  35. // Is the type void*?
  36. const PointerType* PT = RetTy->castAs<PointerType>();
  37. if (!PT || !PT->getPointeeType().getUnqualifiedType()->isVoidType())
  38. return false;
  39. // Does the name start with the prefix?
  40. return Name.startswith(Prefix);
  41. }
  42. /// Returns true when the passed-in type is a CF-style reference-counted
  43. /// type from the DiskArbitration framework.
  44. static bool isDiskArbitrationAPIRefType(QualType T) {
  45. return cocoa::isRefType(T, "DADisk") ||
  46. cocoa::isRefType(T, "DADissenter") ||
  47. cocoa::isRefType(T, "DASessionRef");
  48. }
  49. bool coreFoundation::isCFObjectRef(QualType T) {
  50. return cocoa::isRefType(T, "CF") || // Core Foundation.
  51. cocoa::isRefType(T, "CG") || // Core Graphics.
  52. cocoa::isRefType(T, "CM") || // Core Media.
  53. isDiskArbitrationAPIRefType(T);
  54. }
  55. bool cocoa::isCocoaObjectRef(QualType Ty) {
  56. if (!Ty->isObjCObjectPointerType())
  57. return false;
  58. const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>();
  59. // Can be true for objects with the 'NSObject' attribute.
  60. if (!PT)
  61. return true;
  62. // We assume that id<..>, id, Class, and Class<..> all represent tracked
  63. // objects.
  64. if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() ||
  65. PT->isObjCClassType() || PT->isObjCQualifiedClassType())
  66. return true;
  67. // Does the interface subclass NSObject?
  68. // FIXME: We can memoize here if this gets too expensive.
  69. const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
  70. // Assume that anything declared with a forward declaration and no
  71. // @interface subclasses NSObject.
  72. if (!ID->hasDefinition())
  73. return true;
  74. for ( ; ID ; ID = ID->getSuperClass())
  75. if (ID->getIdentifier()->getName() == "NSObject")
  76. return true;
  77. return false;
  78. }
  79. bool coreFoundation::followsCreateRule(const FunctionDecl *fn) {
  80. // For now, *just* base this on the function name, not on anything else.
  81. const IdentifierInfo *ident = fn->getIdentifier();
  82. if (!ident) return false;
  83. StringRef functionName = ident->getName();
  84. StringRef::iterator it = functionName.begin();
  85. StringRef::iterator start = it;
  86. StringRef::iterator endI = functionName.end();
  87. while (true) {
  88. // Scan for the start of 'create' or 'copy'.
  89. for ( ; it != endI ; ++it) {
  90. // Search for the first character. It can either be 'C' or 'c'.
  91. char ch = *it;
  92. if (ch == 'C' || ch == 'c') {
  93. // Make sure this isn't something like 'recreate' or 'Scopy'.
  94. if (ch == 'c' && it != start && isLetter(*(it - 1)))
  95. continue;
  96. ++it;
  97. break;
  98. }
  99. }
  100. // Did we hit the end of the string? If so, we didn't find a match.
  101. if (it == endI)
  102. return false;
  103. // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase
  104. // character.
  105. StringRef suffix = functionName.substr(it - start);
  106. if (suffix.startswith("reate")) {
  107. it += 5;
  108. }
  109. else if (suffix.startswith("opy")) {
  110. it += 3;
  111. } else {
  112. // Keep scanning.
  113. continue;
  114. }
  115. if (it == endI || !isLowercase(*it))
  116. return true;
  117. // If we matched a lowercase character, it isn't the end of the
  118. // word. Keep scanning.
  119. }
  120. }