forked from OSchip/llvm-project
Use ARC parsing rules for ns_returns_retained in MRC so that code can
be shared without warnings. Build AttributedTypes to leave breadcrumbs for tools like the static analyzer. Warn about attempting to use the attribute with incompatible return types. llvm-svn: 308092
This commit is contained in:
parent
8561c2e22c
commit
12251887bd
|
@ -3878,6 +3878,7 @@ public:
|
||||||
attr_sptr,
|
attr_sptr,
|
||||||
attr_uptr,
|
attr_uptr,
|
||||||
attr_nonnull,
|
attr_nonnull,
|
||||||
|
attr_ns_returns_retained,
|
||||||
attr_nullable,
|
attr_nullable,
|
||||||
attr_null_unspecified,
|
attr_null_unspecified,
|
||||||
attr_objc_kindof,
|
attr_objc_kindof,
|
||||||
|
|
|
@ -8380,6 +8380,8 @@ public:
|
||||||
unsigned SpellingListIndex, bool isNSConsumed,
|
unsigned SpellingListIndex, bool isNSConsumed,
|
||||||
bool isTemplateInstantiation);
|
bool isTemplateInstantiation);
|
||||||
|
|
||||||
|
bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type);
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// C++ Coroutines TS
|
// C++ Coroutines TS
|
||||||
//
|
//
|
||||||
|
|
|
@ -3023,6 +3023,7 @@ bool AttributedType::isQualifier() const {
|
||||||
case AttributedType::attr_sptr:
|
case AttributedType::attr_sptr:
|
||||||
case AttributedType::attr_uptr:
|
case AttributedType::attr_uptr:
|
||||||
case AttributedType::attr_objc_kindof:
|
case AttributedType::attr_objc_kindof:
|
||||||
|
case AttributedType::attr_ns_returns_retained:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
llvm_unreachable("bad attributed type kind");
|
llvm_unreachable("bad attributed type kind");
|
||||||
|
@ -3056,6 +3057,7 @@ bool AttributedType::isCallingConv() const {
|
||||||
case attr_objc_inert_unsafe_unretained:
|
case attr_objc_inert_unsafe_unretained:
|
||||||
case attr_noreturn:
|
case attr_noreturn:
|
||||||
case attr_nonnull:
|
case attr_nonnull:
|
||||||
|
case attr_ns_returns_retained:
|
||||||
case attr_nullable:
|
case attr_nullable:
|
||||||
case attr_null_unspecified:
|
case attr_null_unspecified:
|
||||||
case attr_objc_kindof:
|
case attr_objc_kindof:
|
||||||
|
|
|
@ -104,6 +104,7 @@ namespace {
|
||||||
void printAfter(QualType T, raw_ostream &OS);
|
void printAfter(QualType T, raw_ostream &OS);
|
||||||
void AppendScope(DeclContext *DC, raw_ostream &OS);
|
void AppendScope(DeclContext *DC, raw_ostream &OS);
|
||||||
void printTag(TagDecl *T, raw_ostream &OS);
|
void printTag(TagDecl *T, raw_ostream &OS);
|
||||||
|
void printFunctionAfter(const FunctionType::ExtInfo &Info, raw_ostream &OS);
|
||||||
#define ABSTRACT_TYPE(CLASS, PARENT)
|
#define ABSTRACT_TYPE(CLASS, PARENT)
|
||||||
#define TYPE(CLASS, PARENT) \
|
#define TYPE(CLASS, PARENT) \
|
||||||
void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \
|
void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \
|
||||||
|
@ -685,6 +686,36 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
|
||||||
|
|
||||||
FunctionType::ExtInfo Info = T->getExtInfo();
|
FunctionType::ExtInfo Info = T->getExtInfo();
|
||||||
|
|
||||||
|
printFunctionAfter(Info, OS);
|
||||||
|
|
||||||
|
if (unsigned quals = T->getTypeQuals()) {
|
||||||
|
OS << ' ';
|
||||||
|
AppendTypeQualList(OS, quals, Policy.Restrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (T->getRefQualifier()) {
|
||||||
|
case RQ_None:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RQ_LValue:
|
||||||
|
OS << " &";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RQ_RValue:
|
||||||
|
OS << " &&";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
T->printExceptionSpecification(OS, Policy);
|
||||||
|
|
||||||
|
if (T->hasTrailingReturn()) {
|
||||||
|
OS << " -> ";
|
||||||
|
print(T->getReturnType(), OS, StringRef());
|
||||||
|
} else
|
||||||
|
printAfter(T->getReturnType(), OS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
|
||||||
|
raw_ostream &OS) {
|
||||||
if (!InsideCCAttribute) {
|
if (!InsideCCAttribute) {
|
||||||
switch (Info.getCC()) {
|
switch (Info.getCC()) {
|
||||||
case CC_C:
|
case CC_C:
|
||||||
|
@ -747,36 +778,13 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
|
||||||
|
|
||||||
if (Info.getNoReturn())
|
if (Info.getNoReturn())
|
||||||
OS << " __attribute__((noreturn))";
|
OS << " __attribute__((noreturn))";
|
||||||
|
if (Info.getProducesResult())
|
||||||
|
OS << " __attribute__((ns_returns_retained))";
|
||||||
if (Info.getRegParm())
|
if (Info.getRegParm())
|
||||||
OS << " __attribute__((regparm ("
|
OS << " __attribute__((regparm ("
|
||||||
<< Info.getRegParm() << ")))";
|
<< Info.getRegParm() << ")))";
|
||||||
if (Info.getNoCallerSavedRegs())
|
if (Info.getNoCallerSavedRegs())
|
||||||
OS << "__attribute__((no_caller_saved_registers))";
|
OS << " __attribute__((no_caller_saved_registers))";
|
||||||
|
|
||||||
if (unsigned quals = T->getTypeQuals()) {
|
|
||||||
OS << ' ';
|
|
||||||
AppendTypeQualList(OS, quals, Policy.Restrict);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (T->getRefQualifier()) {
|
|
||||||
case RQ_None:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RQ_LValue:
|
|
||||||
OS << " &";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RQ_RValue:
|
|
||||||
OS << " &&";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
T->printExceptionSpecification(OS, Policy);
|
|
||||||
|
|
||||||
if (T->hasTrailingReturn()) {
|
|
||||||
OS << " -> ";
|
|
||||||
print(T->getReturnType(), OS, StringRef());
|
|
||||||
} else
|
|
||||||
printAfter(T->getReturnType(), OS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T,
|
void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T,
|
||||||
|
@ -795,8 +803,7 @@ void TypePrinter::printFunctionNoProtoAfter(const FunctionNoProtoType *T,
|
||||||
SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
|
SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
|
||||||
|
|
||||||
OS << "()";
|
OS << "()";
|
||||||
if (T->getNoReturnAttr())
|
printFunctionAfter(T->getExtInfo(), OS);
|
||||||
OS << " __attribute__((noreturn))";
|
|
||||||
printAfter(T->getReturnType(), OS);
|
printAfter(T->getReturnType(), OS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1270,6 +1277,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
|
||||||
if (T->getAttrKind() == AttributedType::attr_objc_inert_unsafe_unretained)
|
if (T->getAttrKind() == AttributedType::attr_objc_inert_unsafe_unretained)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Don't print ns_returns_retained unless it had an effect.
|
||||||
|
if (T->getAttrKind() == AttributedType::attr_ns_returns_retained &&
|
||||||
|
!T->getEquivalentType()->castAs<FunctionType>()
|
||||||
|
->getExtInfo().getProducesResult())
|
||||||
|
return;
|
||||||
|
|
||||||
// Print nullability type specifiers that occur after
|
// Print nullability type specifiers that occur after
|
||||||
if (T->getAttrKind() == AttributedType::attr_nonnull ||
|
if (T->getAttrKind() == AttributedType::attr_nonnull ||
|
||||||
T->getAttrKind() == AttributedType::attr_nullable ||
|
T->getAttrKind() == AttributedType::attr_nullable ||
|
||||||
|
@ -1361,6 +1374,10 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
|
||||||
OS << ')';
|
OS << ')';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AttributedType::attr_ns_returns_retained:
|
||||||
|
OS << "ns_returns_retained";
|
||||||
|
break;
|
||||||
|
|
||||||
// FIXME: When Sema learns to form this AttributedType, avoid printing the
|
// FIXME: When Sema learns to form this AttributedType, avoid printing the
|
||||||
// attribute again in printFunctionProtoAfter.
|
// attribute again in printFunctionProtoAfter.
|
||||||
case AttributedType::attr_noreturn: OS << "noreturn"; break;
|
case AttributedType::attr_noreturn: OS << "noreturn"; break;
|
||||||
|
|
|
@ -4679,6 +4679,16 @@ void Sema::AddNSConsumedAttr(SourceRange attrRange, Decl *D,
|
||||||
CFConsumedAttr(attrRange, Context, spellingIndex));
|
CFConsumedAttr(attrRange, Context, spellingIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Sema::checkNSReturnsRetainedReturnType(SourceLocation loc,
|
||||||
|
QualType type) {
|
||||||
|
if (isValidSubjectOfNSReturnsRetainedAttribute(type))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Diag(loc, diag::warn_ns_attribute_wrong_return_type)
|
||||||
|
<< "'ns_returns_retained'" << 0 << 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
|
static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
|
||||||
const AttributeList &Attr) {
|
const AttributeList &Attr) {
|
||||||
QualType returnType;
|
QualType returnType;
|
||||||
|
@ -4700,6 +4710,8 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
|
||||||
<< Attr.getRange();
|
<< Attr.getRange();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (Attr.isUsedAsTypeAttr()) {
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
AttributeDeclKind ExpectedDeclKind;
|
AttributeDeclKind ExpectedDeclKind;
|
||||||
switch (Attr.getKind()) {
|
switch (Attr.getKind()) {
|
||||||
|
@ -4743,6 +4755,9 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!typeOK) {
|
if (!typeOK) {
|
||||||
|
if (Attr.isUsedAsTypeAttr())
|
||||||
|
return;
|
||||||
|
|
||||||
if (isa<ParmVarDecl>(D)) {
|
if (isa<ParmVarDecl>(D)) {
|
||||||
S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type)
|
S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type)
|
||||||
<< Attr.getName() << /*pointer-to-CF*/2
|
<< Attr.getName() << /*pointer-to-CF*/2
|
||||||
|
|
|
@ -120,6 +120,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr,
|
||||||
|
|
||||||
// Function type attributes.
|
// Function type attributes.
|
||||||
#define FUNCTION_TYPE_ATTRS_CASELIST \
|
#define FUNCTION_TYPE_ATTRS_CASELIST \
|
||||||
|
case AttributeList::AT_NSReturnsRetained: \
|
||||||
case AttributeList::AT_NoReturn: \
|
case AttributeList::AT_NoReturn: \
|
||||||
case AttributeList::AT_Regparm: \
|
case AttributeList::AT_Regparm: \
|
||||||
case AttributeList::AT_AnyX86NoCallerSavedRegisters: \
|
case AttributeList::AT_AnyX86NoCallerSavedRegisters: \
|
||||||
|
@ -640,12 +641,6 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state,
|
||||||
distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType);
|
distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AttributeList::AT_NSReturnsRetained:
|
|
||||||
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
|
|
||||||
break;
|
|
||||||
// fallthrough
|
|
||||||
LLVM_FALLTHROUGH;
|
|
||||||
|
|
||||||
FUNCTION_TYPE_ATTRS_CASELIST:
|
FUNCTION_TYPE_ATTRS_CASELIST:
|
||||||
distributeFunctionTypeAttrFromDeclarator(state, *attr, declSpecType);
|
distributeFunctionTypeAttrFromDeclarator(state, *attr, declSpecType);
|
||||||
break;
|
break;
|
||||||
|
@ -2385,6 +2380,11 @@ QualType Sema::BuildFunctionType(QualType T,
|
||||||
[=](unsigned i) { return Loc; });
|
[=](unsigned i) { return Loc; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (EPI.ExtInfo.getProducesResult()) {
|
||||||
|
// This is just a warning, so we can't fail to build if we see it.
|
||||||
|
checkNSReturnsRetainedReturnType(Loc, T);
|
||||||
|
}
|
||||||
|
|
||||||
if (Invalid)
|
if (Invalid)
|
||||||
return QualType();
|
return QualType();
|
||||||
|
|
||||||
|
@ -5017,6 +5017,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
|
||||||
return AttributeList::AT_TypeNullUnspecified;
|
return AttributeList::AT_TypeNullUnspecified;
|
||||||
case AttributedType::attr_objc_kindof:
|
case AttributedType::attr_objc_kindof:
|
||||||
return AttributeList::AT_ObjCKindOf;
|
return AttributeList::AT_ObjCKindOf;
|
||||||
|
case AttributedType::attr_ns_returns_retained:
|
||||||
|
return AttributeList::AT_NSReturnsRetained;
|
||||||
}
|
}
|
||||||
llvm_unreachable("unexpected attribute kind!");
|
llvm_unreachable("unexpected attribute kind!");
|
||||||
}
|
}
|
||||||
|
@ -6373,17 +6375,26 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state,
|
||||||
// ns_returns_retained is not always a type attribute, but if we got
|
// ns_returns_retained is not always a type attribute, but if we got
|
||||||
// here, we're treating it as one right now.
|
// here, we're treating it as one right now.
|
||||||
if (attr.getKind() == AttributeList::AT_NSReturnsRetained) {
|
if (attr.getKind() == AttributeList::AT_NSReturnsRetained) {
|
||||||
assert(S.getLangOpts().ObjCAutoRefCount &&
|
|
||||||
"ns_returns_retained treated as type attribute in non-ARC");
|
|
||||||
if (attr.getNumArgs()) return true;
|
if (attr.getNumArgs()) return true;
|
||||||
|
|
||||||
// Delay if this is not a function type.
|
// Delay if this is not a function type.
|
||||||
if (!unwrapped.isFunctionType())
|
if (!unwrapped.isFunctionType())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
FunctionType::ExtInfo EI
|
// Check whether the return type is reasonable.
|
||||||
= unwrapped.get()->getExtInfo().withProducesResult(true);
|
if (S.checkNSReturnsRetainedReturnType(attr.getLoc(),
|
||||||
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
|
unwrapped.get()->getReturnType()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Only actually change the underlying type in ARC builds.
|
||||||
|
QualType origType = type;
|
||||||
|
if (state.getSema().getLangOpts().ObjCAutoRefCount) {
|
||||||
|
FunctionType::ExtInfo EI
|
||||||
|
= unwrapped.get()->getExtInfo().withProducesResult(true);
|
||||||
|
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
|
||||||
|
}
|
||||||
|
type = S.Context.getAttributedType(AttributedType::attr_ns_returns_retained,
|
||||||
|
origType, type);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6945,12 +6956,6 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
|
||||||
attr.setUsedAsTypeAttr();
|
attr.setUsedAsTypeAttr();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AttributeList::AT_NSReturnsRetained:
|
|
||||||
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
|
|
||||||
break;
|
|
||||||
// fallthrough into the function attrs
|
|
||||||
LLVM_FALLTHROUGH;
|
|
||||||
|
|
||||||
FUNCTION_TYPE_ATTRS_CASELIST:
|
FUNCTION_TYPE_ATTRS_CASELIST:
|
||||||
attr.setUsedAsTypeAttr();
|
attr.setUsedAsTypeAttr();
|
||||||
|
|
||||||
|
|
|
@ -1447,7 +1447,7 @@ typedef NSString* MyStringTy;
|
||||||
+ (void) consume2:(id) CF_CONSUMED x;
|
+ (void) consume2:(id) CF_CONSUMED x;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to functions and methods}}
|
static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' only applies to function types; type here is 'int'}}
|
||||||
|
|
||||||
void test_attr_1(TestOwnershipAttr *X) {
|
void test_attr_1(TestOwnershipAttr *X) {
|
||||||
NSString *str = [X returnsAnOwnedString]; // expected-warning{{leak}}
|
NSString *str = [X returnsAnOwnedString]; // expected-warning{{leak}}
|
||||||
|
|
|
@ -24,7 +24,7 @@ void foo6(){} // expected-note {{previous declaration is here}}
|
||||||
void __attribute__((no_caller_saved_registers)) foo6(); // expected-error {{function declared with 'no_caller_saved_registers' attribute was previously declared without the 'no_caller_saved_registers' attribute}}
|
void __attribute__((no_caller_saved_registers)) foo6(); // expected-error {{function declared with 'no_caller_saved_registers' attribute was previously declared without the 'no_caller_saved_registers' attribute}}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
void (*fp)(int *) = foo; // expected-error {{cannot initialize a variable of type 'void (*)(int *)' with an lvalue of type 'void (int *)__attribute__((no_caller_saved_registers))'}}
|
void (*fp)(int *) = foo; // expected-error {{cannot initialize a variable of type 'void (*)(int *)' with an lvalue of type 'void (int *) __attribute__((no_caller_saved_registers))'}}
|
||||||
a::foo(&argc);
|
a::foo(&argc);
|
||||||
foo3 func = foo2;
|
foo3 func = foo2;
|
||||||
func(&argc);
|
func(&argc);
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s
|
||||||
|
// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fblocks -verify %s
|
||||||
|
|
||||||
|
// rdar://20130079
|
||||||
|
|
||||||
|
#if __has_feature(objc_arc)
|
||||||
|
__attribute__((ns_returns_retained)) id (^invalidBlockRedecl)(); // expected-note {{previous definition is here}}
|
||||||
|
id (^invalidBlockRedecl)(); //expected-error {{redefinition of 'invalidBlockRedecl' with a different type: 'id (^__strong)()' vs 'id ((^__strong))() __attribute__((ns_returns_retained))'}}
|
||||||
|
#else
|
||||||
|
__attribute__((ns_returns_retained)) id (^invalidBlockRedecl)();
|
||||||
|
id (^invalidBlockRedecl)();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef __attribute__((ns_returns_retained)) id (^blockType)();
|
||||||
|
|
||||||
|
typedef __attribute__((ns_returns_retained)) int (^invalidBlockType)(); // expected-warning {{'ns_returns_retained' attribute only applies to functions that return an Objective-C object}}
|
||||||
|
|
||||||
|
__attribute__((ns_returns_retained)) int functionDecl(); // expected-warning {{'ns_returns_retained' attribute only applies to functions that return an Objective-C object}}
|
Loading…
Reference in New Issue