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