SingleLevelPointer,
// Multi-level pointer (of any pointer kind).
MultiLevelPointer,
+ // CFFooRef*
+ MaybePointerToCFRef,
// CFErrorRef*
CFErrorRefPointer,
// NSError**
case 1:
return PointerDeclaratorKind::SingleLevelPointer;
+ case 2:
+ return PointerDeclaratorKind::MaybePointerToCFRef;
+
default:
return PointerDeclaratorKind::MultiLevelPointer;
}
// Determine whether we should infer __nonnull on pointer types.
Optional<NullabilityKind> inferNullability;
bool inferNullabilityCS = false;
+ bool inferNullabilityInnerOnly = false;
+ bool inferNullabilityInnerOnlyComplete = false;
// Are we in an assume-nonnull region?
bool inAssumeNonNullRegion = false;
if (isFunctionOrMethod && inAssumeNonNullRegion)
inferNullability = NullabilityKind::Nullable;
break;
+
+ case PointerDeclaratorKind::MaybePointerToCFRef:
+ if (isFunctionOrMethod) {
+ // On pointer-to-pointer parameters marked cf_returns_retained or
+ // cf_returns_not_retained, if the outer pointer is explicit then
+ // infer the inner pointer as __nullable.
+ auto hasCFReturnsAttr = [](const AttributeList *NextAttr) -> bool {
+ while (NextAttr) {
+ if (NextAttr->getKind() == AttributeList::AT_CFReturnsRetained ||
+ NextAttr->getKind() == AttributeList::AT_CFReturnsNotRetained)
+ return true;
+ NextAttr = NextAttr->getNext();
+ }
+ return false;
+ };
+ if (const auto *InnermostChunk = D.getInnermostNonParenChunk()) {
+ if (hasCFReturnsAttr(D.getAttributes()) ||
+ hasCFReturnsAttr(InnermostChunk->getAttrs()) ||
+ hasCFReturnsAttr(D.getDeclSpec().getAttributes().getList())) {
+ inferNullability = NullabilityKind::Nullable;
+ inferNullabilityInnerOnly = true;
+ }
+ }
+ }
+ break;
}
break;
return nullptr;
// If we're supposed to infer nullability, do so now.
- if (inferNullability) {
- auto syntax = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword
- : AttributeList::AS_Keyword;
+ if (inferNullability && !inferNullabilityInnerOnlyComplete) {
+ AttributeList::Syntax syntax
+ = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword
+ : AttributeList::AS_Keyword;
AttributeList *nullabilityAttr = state.getDeclarator().getAttributePool()
.create(
S.getNullabilityKeyword(
nullptr, 0, syntax);
spliceAttrIntoList(*nullabilityAttr, attrs);
+
+ if (inferNullabilityInnerOnly)
+ inferNullabilityInnerOnlyComplete = true;
return nullabilityAttr;
}
void func3(int *); // expected-warning{{pointer is missing a nullability type specifier}}
+#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
+typedef void *CFTypeRef;
+void cf1(CFTypeRef * p CF_RETURNS_NOT_RETAINED); // expected-warning {{pointer is missing a nullability type specifier}}
+
+void cf2(CFTypeRef * __nullable p CF_RETURNS_NOT_RETAINED);
+void cf3(CFTypeRef * __nonnull p CF_RETURNS_NOT_RETAINED);
+
+void cf4(CFTypeRef __nullable * __nullable p CF_RETURNS_NOT_RETAINED);
+void cf5(CFTypeRef __nonnull * __nullable p CF_RETURNS_NOT_RETAINED);
+
+void cf6(CFTypeRef * __nullable CF_RETURNS_NOT_RETAINED p);
+void cf7(CF_RETURNS_NOT_RETAINED CFTypeRef * __nonnull p);
+
+typedef CFTypeRef __nullable *CFTypeRefPtr;
+void cfp1(CFTypeRefPtr p CF_RETURNS_NOT_RETAINED); // expected-warning {{pointer is missing a nullability type specifier}}
+void cfp2(CFTypeRefPtr __nonnull p CF_RETURNS_NOT_RETAINED);