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