1 //===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements cocoa naming convention analysis.
11 //===----------------------------------------------------------------------===//
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"
21 using namespace clang
;
24 bool cocoa::isRefType(QualType RetTy
, StringRef Prefix
,
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
.startswith(Prefix
) && TDName
.endswith("Ref"))
31 // XPC unfortunately uses CF-style function names, but aren't CF types.
32 if (TDName
.startswith("xpc_"))
34 RetTy
= TD
->getDecl()->getUnderlyingType();
41 const PointerType
* PT
= RetTy
->castAs
<PointerType
>();
42 if (!PT
|| !PT
->getPointeeType().getUnqualifiedType()->isVoidType())
45 // Does the name start with the prefix?
46 return Name
.startswith(Prefix
);
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");
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
);
65 bool cocoa::isCocoaObjectRef(QualType Ty
) {
66 if (!Ty
->isObjCObjectPointerType())
69 const ObjCObjectPointerType
*PT
= Ty
->getAs
<ObjCObjectPointerType
>();
71 // Can be true for objects with the 'NSObject' attribute.
75 // We assume that id<..>, id, Class, and Class<..> all represent tracked
77 if (PT
->isObjCIdType() || PT
->isObjCQualifiedIdType() ||
78 PT
->isObjCClassType() || PT
->isObjCQualifiedClassType())
81 // Does the interface subclass NSObject?
82 // FIXME: We can memoize here if this gets too expensive.
83 const ObjCInterfaceDecl
*ID
= PT
->getInterfaceDecl();
85 // Assume that anything declared with a forward declaration and no
86 // @interface subclasses NSObject.
87 if (!ID
->hasDefinition())
90 for ( ; ID
; ID
= ID
->getSuperClass())
91 if (ID
->getIdentifier()->getName() == "NSObject")
97 bool coreFoundation::followsCreateRule(const FunctionDecl
*fn
) {
98 // For now, *just* base this on the function name, not on anything else.
100 const IdentifierInfo
*ident
= fn
->getIdentifier();
101 if (!ident
) return false;
102 StringRef functionName
= ident
->getName();
104 StringRef::iterator it
= functionName
.begin();
105 StringRef::iterator start
= it
;
106 StringRef::iterator endI
= functionName
.end();
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'.
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)))
123 // Did we hit the end of the string? If so, we didn't find a match.
127 // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase
129 StringRef suffix
= functionName
.substr(it
- start
);
130 if (suffix
.startswith("reate")) {
133 else if (suffix
.startswith("opy")) {
140 if (it
== endI
|| !isLowercase(*it
))
143 // If we matched a lowercase character, it isn't the end of the
144 // word. Keep scanning.