mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 09:16:05 +00:00
Extend type nullability qualifiers for Objective-C.
Introduce context-sensitive, non-underscored nullability specifiers (nonnull, nullable, null_unspecified) for Objective-C method return types, method parameter types, and properties. Introduce Objective-C-specific semantics, including computation of the nullability of the result of a message send, merging of nullability information from the @interface of a class into its @implementation, etc . This is the Objective-C part of rdar://problem/18868820. llvm-svn: 240154
This commit is contained in:
parent
b4866e85e5
commit
813a066f16
@ -1854,6 +1854,36 @@ public:
|
||||
getCanonicalType(T2).getTypePtr();
|
||||
}
|
||||
|
||||
bool hasSameNullabilityTypeQualifier(QualType SubT, QualType SuperT,
|
||||
bool IsParam) const {
|
||||
auto SubTnullability = SubT->getNullability(*this);
|
||||
auto SuperTnullability = SuperT->getNullability(*this);
|
||||
if (SubTnullability.hasValue() == SuperTnullability.hasValue()) {
|
||||
// Neither has nullability; return true
|
||||
if (!SubTnullability)
|
||||
return true;
|
||||
// Both have nullability qualifier.
|
||||
if (*SubTnullability == *SuperTnullability ||
|
||||
*SubTnullability == NullabilityKind::Unspecified ||
|
||||
*SuperTnullability == NullabilityKind::Unspecified)
|
||||
return true;
|
||||
|
||||
if (IsParam) {
|
||||
// Ok for the superclass method parameter to be “nonnull” and the subclass
|
||||
// method parameter to be “nullable”
|
||||
return (*SuperTnullability == NullabilityKind::NonNull &&
|
||||
*SubTnullability == NullabilityKind::Nullable);
|
||||
}
|
||||
else {
|
||||
// For the return type, it’s okay for the superclass method to specify
|
||||
// “nullable” and the subclass method specify “nonnull”
|
||||
return (*SuperTnullability == NullabilityKind::Nullable &&
|
||||
*SubTnullability == NullabilityKind::NonNull);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjCMethodsAreEqual(const ObjCMethodDecl *MethodDecl,
|
||||
const ObjCMethodDecl *MethodImp);
|
||||
|
||||
|
@ -178,7 +178,12 @@ public:
|
||||
OBJC_TQ_Out = 0x4,
|
||||
OBJC_TQ_Bycopy = 0x8,
|
||||
OBJC_TQ_Byref = 0x10,
|
||||
OBJC_TQ_Oneway = 0x20
|
||||
OBJC_TQ_Oneway = 0x20,
|
||||
|
||||
/// The nullability qualifier is set when the nullability of the
|
||||
/// result or parameter was expressed via a context-sensitive
|
||||
/// keyword.
|
||||
OBJC_TQ_CSNullability = 0x40
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -141,7 +141,7 @@ private:
|
||||
|
||||
// NOTE: VC++ treats enums as signed, avoid using the ObjCDeclQualifier enum
|
||||
/// in, inout, etc.
|
||||
unsigned objcDeclQualifier : 6;
|
||||
unsigned objcDeclQualifier : 7;
|
||||
|
||||
/// \brief Indicates whether this method has a related result type.
|
||||
unsigned RelatedResultType : 1;
|
||||
@ -2203,13 +2203,16 @@ public:
|
||||
OBJC_PR_atomic = 0x100,
|
||||
OBJC_PR_weak = 0x200,
|
||||
OBJC_PR_strong = 0x400,
|
||||
OBJC_PR_unsafe_unretained = 0x800
|
||||
OBJC_PR_unsafe_unretained = 0x800,
|
||||
/// Indicates that the nullability of the type was spelled with a
|
||||
/// property attribute rather than a type qualifier.
|
||||
OBJC_PR_nullability = 0x1000
|
||||
// Adding a property should change NumPropertyAttrsBits
|
||||
};
|
||||
|
||||
enum {
|
||||
/// \brief Number of bits fitting all the property attributes.
|
||||
NumPropertyAttrsBits = 12
|
||||
NumPropertyAttrsBits = 13
|
||||
};
|
||||
|
||||
enum SetterKind { Assign, Retain, Copy, Weak };
|
||||
@ -2217,7 +2220,8 @@ public:
|
||||
private:
|
||||
SourceLocation AtLoc; // location of \@property
|
||||
SourceLocation LParenLoc; // location of '(' starting attribute list or null.
|
||||
TypeSourceInfo *DeclType;
|
||||
QualType DeclType;
|
||||
TypeSourceInfo *DeclTypeSourceInfo;
|
||||
unsigned PropertyAttributes : NumPropertyAttrsBits;
|
||||
unsigned PropertyAttributesAsWritten : NumPropertyAttrsBits;
|
||||
// \@required/\@optional
|
||||
@ -2232,12 +2236,13 @@ private:
|
||||
|
||||
ObjCPropertyDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
|
||||
SourceLocation AtLocation, SourceLocation LParenLocation,
|
||||
TypeSourceInfo *T)
|
||||
QualType T, TypeSourceInfo *TSI,
|
||||
PropertyControl propControl)
|
||||
: NamedDecl(ObjCProperty, DC, L, Id), AtLoc(AtLocation),
|
||||
LParenLoc(LParenLocation), DeclType(T),
|
||||
LParenLoc(LParenLocation), DeclType(T), DeclTypeSourceInfo(TSI),
|
||||
PropertyAttributes(OBJC_PR_noattr),
|
||||
PropertyAttributesAsWritten(OBJC_PR_noattr),
|
||||
PropertyImplementation(None),
|
||||
PropertyImplementation(propControl),
|
||||
GetterName(Selector()),
|
||||
SetterName(Selector()),
|
||||
GetterMethodDecl(nullptr), SetterMethodDecl(nullptr),
|
||||
@ -2248,7 +2253,8 @@ public:
|
||||
SourceLocation L,
|
||||
IdentifierInfo *Id, SourceLocation AtLocation,
|
||||
SourceLocation LParenLocation,
|
||||
TypeSourceInfo *T,
|
||||
QualType T,
|
||||
TypeSourceInfo *TSI,
|
||||
PropertyControl propControl = None);
|
||||
|
||||
static ObjCPropertyDecl *CreateDeserialized(ASTContext &C, unsigned ID);
|
||||
@ -2259,9 +2265,14 @@ public:
|
||||
SourceLocation getLParenLoc() const { return LParenLoc; }
|
||||
void setLParenLoc(SourceLocation L) { LParenLoc = L; }
|
||||
|
||||
TypeSourceInfo *getTypeSourceInfo() const { return DeclType; }
|
||||
QualType getType() const { return DeclType->getType(); }
|
||||
void setType(TypeSourceInfo *T) { DeclType = T; }
|
||||
TypeSourceInfo *getTypeSourceInfo() const { return DeclTypeSourceInfo; }
|
||||
|
||||
QualType getType() const { return DeclType; }
|
||||
|
||||
void setType(QualType T, TypeSourceInfo *TSI) {
|
||||
DeclType = T;
|
||||
DeclTypeSourceInfo = TSI;
|
||||
}
|
||||
|
||||
PropertyAttributeKind getPropertyAttributes() const {
|
||||
return PropertyAttributeKind(PropertyAttributes);
|
||||
|
@ -101,15 +101,32 @@ def err_enum_template : Error<"enumeration cannot be a template">;
|
||||
|
||||
let CategoryName = "Nullability Issue" in {
|
||||
|
||||
def warn_mismatched_nullability_attr : Warning<
|
||||
"nullability specifier "
|
||||
"'__%select{nonnull|nullable|null_unspecified}0' "
|
||||
"conflicts with existing specifier "
|
||||
"'__%select{nonnull|nullable|null_unspecified}1'">,
|
||||
def warn_nullability_duplicate : Warning<
|
||||
"duplicate nullability specifier "
|
||||
"'%select{__|}1%select{nonnull|nullable|null_unspecified}0'">,
|
||||
InGroup<Nullability>;
|
||||
|
||||
def warn_conflicting_nullability_attr_overriding_ret_types : Warning<
|
||||
"conflicting nullability specifier on return types, "
|
||||
"'%select{%select{__|}1nonnull|"
|
||||
"%select{__|}1nullable|%select{__|}1null_unspecified}0' "
|
||||
"conflicts with existing specifier '%select{%select{__|}3nonnull|"
|
||||
"%select{__|}3nullable|%select{__|}3null_unspecified}2'">,
|
||||
InGroup<Nullability>;
|
||||
|
||||
def note_nullability_here : Note<
|
||||
"'%select{__nonnull|__nullable|__null_unspecified}0' specified here">;
|
||||
def warn_conflicting_nullability_attr_overriding_param_types : Warning<
|
||||
"conflicting nullability specifier on parameter types, "
|
||||
"'%select{%select{__|}1nonnull|"
|
||||
"%select{__|}1nullable|%select{__|}1null_unspecified}0' "
|
||||
"conflicts with existing specifier '%select{%select{__|}3nonnull|"
|
||||
"%select{__|}3nullable|%select{__|}3null_unspecified}2'">,
|
||||
InGroup<Nullability>;
|
||||
|
||||
def err_nullability_conflicting : Error<
|
||||
"nullability specifier "
|
||||
"'%select{__|}1%select{nonnull|nullable|null_unspecified}0' conflicts with "
|
||||
"existing specifier '%select{__|}3%select{nonnull|nullable|"
|
||||
"null_unspecified}2'">;
|
||||
|
||||
}
|
||||
|
||||
|
@ -7676,9 +7676,11 @@ def warn_profile_data_unprofiled : Warning<
|
||||
|
||||
let CategoryName = "Nullability Issue" in {
|
||||
|
||||
def warn_duplicate_nullability : Warning<
|
||||
"duplicate nullability specifier "
|
||||
"'%select{__nonnull|__nullable|__null_unspecified}0'">,
|
||||
def warn_mismatched_nullability_attr : Warning<
|
||||
"nullability specifier "
|
||||
"'%select{__|}1%select{nonnull|nullable|null_unspecified}0' "
|
||||
"conflicts with existing specifier "
|
||||
"'%select{__|}3%select{nonnull|nullable|null_unspecified}2'">,
|
||||
InGroup<Nullability>;
|
||||
|
||||
def warn_nullability_declspec : Warning<
|
||||
@ -7690,21 +7692,28 @@ def warn_nullability_declspec : Warning<
|
||||
InGroup<NullabilityDeclSpec>,
|
||||
DefaultError;
|
||||
|
||||
def note_nullability_here : Note<
|
||||
"'%select{__nonnull|__nullable|__null_unspecified}0' specified here">;
|
||||
|
||||
def err_nullability_nonpointer : Error<
|
||||
"nullability specifier "
|
||||
"'%select{__nonnull|__nullable|__null_unspecified}0' cannot be applied to "
|
||||
"non-pointer type %1">;
|
||||
|
||||
def err_nullability_conflicting : Error<
|
||||
"nullability specifier "
|
||||
"'%select{__nonnull|__nullable|__null_unspecified}0' conflicts with existing "
|
||||
"specifier '%select{__nonnull|__nullable|__null_unspecified}1'">;
|
||||
"'%select{__|}1%select{nonnull|nullable|null_unspecified}0' cannot be applied "
|
||||
"to non-pointer type %2">;
|
||||
|
||||
def warn_nullability_lost : Warning<
|
||||
"implicit conversion from nullable pointer %0 to non-nullable pointer "
|
||||
"type %1">,
|
||||
InGroup<NullableToNonNullConversion>, DefaultIgnore;
|
||||
|
||||
def err_nullability_cs_multilevel : Error<
|
||||
"nullability keyword "
|
||||
"'%select{nonnull|nullable|null_unspecified}0' cannot be applied to "
|
||||
"multi-level pointer type %1">;
|
||||
def note_nullability_type_specifier : Note<
|
||||
"use nullability type specifier "
|
||||
"'%select{__nonnull|__nullable|__null_unspecified}0' to affect the innermost "
|
||||
"pointer type of %1">;
|
||||
|
||||
}
|
||||
|
||||
} // end of sema component.
|
||||
|
@ -16,6 +16,7 @@
|
||||
#ifndef LLVM_CLANG_BASIC_SPECIFIERS_H
|
||||
#define LLVM_CLANG_BASIC_SPECIFIERS_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/DataTypes.h"
|
||||
|
||||
namespace clang {
|
||||
@ -254,6 +255,9 @@ namespace clang {
|
||||
/// though it has been considered.
|
||||
Unspecified
|
||||
};
|
||||
|
||||
/// Retrieve the spelling of the given nullability kind.
|
||||
llvm::StringRef getNullabilitySpelling(NullabilityKind kind);
|
||||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_BASIC_SPECIFIERS_H
|
||||
|
@ -139,6 +139,11 @@ class Parser : public CodeCompletionHandler {
|
||||
// used as type traits.
|
||||
llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind> RevertibleTypeTraits;
|
||||
|
||||
/// Nullability type specifiers.
|
||||
IdentifierInfo *Ident___nonnull = nullptr;
|
||||
IdentifierInfo *Ident___nullable = nullptr;
|
||||
IdentifierInfo *Ident___null_unspecified = nullptr;
|
||||
|
||||
std::unique_ptr<PragmaHandler> AlignHandler;
|
||||
std::unique_ptr<PragmaHandler> GCCVisibilityHandler;
|
||||
std::unique_ptr<PragmaHandler> OptionsHandler;
|
||||
@ -303,6 +308,10 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Retrieve the underscored keyword (__nonnull, __nullable,
|
||||
/// __null_unspecified) that corresponds to the given nullability kind.
|
||||
IdentifierInfo *getNullabilityKeyword(NullabilityKind nullability);
|
||||
|
||||
private:
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Low-Level token peeking and consumption methods.
|
||||
@ -1282,6 +1291,7 @@ private:
|
||||
// Definitions for Objective-c context sensitive keywords recognition.
|
||||
enum ObjCTypeQual {
|
||||
objc_in=0, objc_out, objc_inout, objc_oneway, objc_bycopy, objc_byref,
|
||||
objc_nonnull, objc_nullable, objc_null_unspecified,
|
||||
objc_NumQuals
|
||||
};
|
||||
IdentifierInfo *ObjCTypeQuals[objc_NumQuals];
|
||||
|
@ -120,6 +120,9 @@ private:
|
||||
/// True if this has a ParsedType
|
||||
unsigned HasParsedType : 1;
|
||||
|
||||
/// True when this keyword attribute is a context-sensitive keyword.
|
||||
unsigned IsContextSensitiveKeyword : 1;
|
||||
|
||||
unsigned AttrKind : 8;
|
||||
|
||||
/// \brief The location of the 'unavailable' keyword in an
|
||||
@ -220,7 +223,8 @@ private:
|
||||
ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs),
|
||||
SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false),
|
||||
IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
|
||||
HasParsedType(false), NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
HasParsedType(false), IsContextSensitiveKeyword(false),
|
||||
NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion));
|
||||
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
|
||||
}
|
||||
@ -238,8 +242,8 @@ private:
|
||||
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed),
|
||||
Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
|
||||
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
|
||||
UnavailableLoc(unavailable), MessageExpr(messageExpr),
|
||||
NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
IsContextSensitiveKeyword(false), UnavailableLoc(unavailable),
|
||||
MessageExpr(messageExpr), NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
ArgsUnion PVal(Parm);
|
||||
memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
|
||||
new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
|
||||
@ -259,7 +263,8 @@ private:
|
||||
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(3), SyntaxUsed(syntaxUsed),
|
||||
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
|
||||
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
|
||||
NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
|
||||
NextInPool(nullptr) {
|
||||
ArgsVector Args;
|
||||
Args.push_back(Parm1);
|
||||
Args.push_back(Parm2);
|
||||
@ -277,7 +282,8 @@ private:
|
||||
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed),
|
||||
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
|
||||
IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false),
|
||||
NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
|
||||
NextInPool(nullptr) {
|
||||
ArgsUnion PVal(ArgKind);
|
||||
memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
|
||||
TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot();
|
||||
@ -295,7 +301,8 @@ private:
|
||||
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed),
|
||||
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
|
||||
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true),
|
||||
NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
|
||||
NextInPool(nullptr) {
|
||||
new (&getTypeBuffer()) ParsedType(typeArg);
|
||||
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
|
||||
}
|
||||
@ -309,7 +316,8 @@ private:
|
||||
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed),
|
||||
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
|
||||
IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false),
|
||||
NextInPosition(nullptr), NextInPool(nullptr) {
|
||||
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
|
||||
NextInPool(nullptr) {
|
||||
new (&getPropertyDataBuffer()) PropertyData(getterId, setterId);
|
||||
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
|
||||
}
|
||||
@ -352,6 +360,15 @@ public:
|
||||
}
|
||||
bool isKeywordAttribute() const { return SyntaxUsed == AS_Keyword; }
|
||||
|
||||
bool isContextSensitiveKeywordAttribute() const {
|
||||
return IsContextSensitiveKeyword;
|
||||
}
|
||||
|
||||
void setContextSensitiveKeywordAttribute() {
|
||||
assert(SyntaxUsed == AS_Keyword);
|
||||
IsContextSensitiveKeyword = true;
|
||||
}
|
||||
|
||||
bool isInvalid() const { return Invalid; }
|
||||
void setInvalid(bool b = true) const { Invalid = b; }
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "clang/Lex/Token.h"
|
||||
#include "clang/Sema/AttributeList.h"
|
||||
#include "clang/Sema/Ownership.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
@ -785,7 +786,8 @@ public:
|
||||
DQ_Out = 0x4,
|
||||
DQ_Bycopy = 0x8,
|
||||
DQ_Byref = 0x10,
|
||||
DQ_Oneway = 0x20
|
||||
DQ_Oneway = 0x20,
|
||||
DQ_CSNullability = 0x40
|
||||
};
|
||||
|
||||
/// PropertyAttributeKind - list of property attributes.
|
||||
@ -802,17 +804,21 @@ public:
|
||||
DQ_PR_atomic = 0x100,
|
||||
DQ_PR_weak = 0x200,
|
||||
DQ_PR_strong = 0x400,
|
||||
DQ_PR_unsafe_unretained = 0x800
|
||||
DQ_PR_unsafe_unretained = 0x800,
|
||||
DQ_PR_nullability = 0x1000
|
||||
};
|
||||
|
||||
|
||||
ObjCDeclSpec()
|
||||
: objcDeclQualifier(DQ_None), PropertyAttributes(DQ_PR_noattr),
|
||||
GetterName(nullptr), SetterName(nullptr) { }
|
||||
Nullability(0), GetterName(nullptr), SetterName(nullptr) { }
|
||||
|
||||
ObjCDeclQualifier getObjCDeclQualifier() const { return objcDeclQualifier; }
|
||||
void setObjCDeclQualifier(ObjCDeclQualifier DQVal) {
|
||||
objcDeclQualifier = (ObjCDeclQualifier) (objcDeclQualifier | DQVal);
|
||||
}
|
||||
void clearObjCDeclQualifier(ObjCDeclQualifier DQVal) {
|
||||
objcDeclQualifier = (ObjCDeclQualifier) (objcDeclQualifier & ~DQVal);
|
||||
}
|
||||
|
||||
ObjCPropertyAttributeKind getPropertyAttributes() const {
|
||||
return ObjCPropertyAttributeKind(PropertyAttributes);
|
||||
@ -822,6 +828,28 @@ public:
|
||||
(ObjCPropertyAttributeKind)(PropertyAttributes | PRVal);
|
||||
}
|
||||
|
||||
NullabilityKind getNullability() const {
|
||||
assert(((getObjCDeclQualifier() & DQ_CSNullability) ||
|
||||
(getPropertyAttributes() & DQ_PR_nullability)) &&
|
||||
"Objective-C declspec doesn't have nullability");
|
||||
return static_cast<NullabilityKind>(Nullability);
|
||||
}
|
||||
|
||||
SourceLocation getNullabilityLoc() const {
|
||||
assert(((getObjCDeclQualifier() & DQ_CSNullability) ||
|
||||
(getPropertyAttributes() & DQ_PR_nullability)) &&
|
||||
"Objective-C declspec doesn't have nullability");
|
||||
return NullabilityLoc;
|
||||
}
|
||||
|
||||
void setNullability(SourceLocation loc, NullabilityKind kind) {
|
||||
assert(((getObjCDeclQualifier() & DQ_CSNullability) ||
|
||||
(getPropertyAttributes() & DQ_PR_nullability)) &&
|
||||
"Set the nullability declspec or property attribute first");
|
||||
Nullability = static_cast<unsigned>(kind);
|
||||
NullabilityLoc = loc;
|
||||
}
|
||||
|
||||
const IdentifierInfo *getGetterName() const { return GetterName; }
|
||||
IdentifierInfo *getGetterName() { return GetterName; }
|
||||
void setGetterName(IdentifierInfo *name) { GetterName = name; }
|
||||
@ -834,10 +862,15 @@ private:
|
||||
// FIXME: These two are unrelated and mutually exclusive. So perhaps
|
||||
// we can put them in a union to reflect their mutual exclusivity
|
||||
// (space saving is negligible).
|
||||
ObjCDeclQualifier objcDeclQualifier : 6;
|
||||
ObjCDeclQualifier objcDeclQualifier : 7;
|
||||
|
||||
// NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind
|
||||
unsigned PropertyAttributes : 12;
|
||||
unsigned PropertyAttributes : 13;
|
||||
|
||||
unsigned Nullability : 2;
|
||||
|
||||
SourceLocation NullabilityLoc;
|
||||
|
||||
IdentifierInfo *GetterName; // getter name or NULL if no getter
|
||||
IdentifierInfo *SetterName; // setter name or NULL if no setter
|
||||
};
|
||||
|
@ -2859,6 +2859,26 @@ public:
|
||||
/// Valid types should not have multiple attributes with different CCs.
|
||||
const AttributedType *getCallingConvAttributedType(QualType T) const;
|
||||
|
||||
/// Check whether a nullability type specifier can be added to the given
|
||||
/// type.
|
||||
///
|
||||
/// \param type The type to which the nullability specifier will be
|
||||
/// added. On success, this type will be updated appropriately.
|
||||
///
|
||||
/// \param nullability The nullability specifier to add.
|
||||
///
|
||||
/// \param nullabilityLoc The location of the nullability specifier.
|
||||
///
|
||||
/// \param isContextSensitive Whether this nullability specifier was
|
||||
/// written as a context-sensitive keyword (in an Objective-C
|
||||
/// method) or an Objective-C property attribute, rather than as an
|
||||
/// underscored type specifier.
|
||||
///
|
||||
/// \returns true if nullability cannot be applied, false otherwise.
|
||||
bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
|
||||
SourceLocation nullabilityLoc,
|
||||
bool isContextSensitive);
|
||||
|
||||
/// \brief Stmt attributes - this routine is the top level dispatcher.
|
||||
StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs,
|
||||
SourceRange Range);
|
||||
@ -2934,7 +2954,8 @@ public:
|
||||
const unsigned Attributes,
|
||||
const unsigned AttributesAsWritten,
|
||||
bool *isOverridingProperty,
|
||||
TypeSourceInfo *T,
|
||||
QualType T,
|
||||
TypeSourceInfo *TSI,
|
||||
tok::ObjCKeywordKind MethodImplKind);
|
||||
|
||||
/// Called by ActOnProperty and HandlePropertyInClassExtension to
|
||||
@ -2950,7 +2971,8 @@ public:
|
||||
const bool isReadWrite,
|
||||
const unsigned Attributes,
|
||||
const unsigned AttributesAsWritten,
|
||||
TypeSourceInfo *T,
|
||||
QualType T,
|
||||
TypeSourceInfo *TSI,
|
||||
tok::ObjCKeywordKind MethodImplKind,
|
||||
DeclContext *lexicalDC = nullptr);
|
||||
|
||||
|
@ -3922,8 +3922,8 @@ Decl *ASTNodeImporter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
|
||||
}
|
||||
|
||||
// Import the type.
|
||||
TypeSourceInfo *T = Importer.Import(D->getTypeSourceInfo());
|
||||
if (!T)
|
||||
TypeSourceInfo *TSI = Importer.Import(D->getTypeSourceInfo());
|
||||
if (!TSI)
|
||||
return nullptr;
|
||||
|
||||
// Create the new property.
|
||||
@ -3932,7 +3932,8 @@ Decl *ASTNodeImporter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
|
||||
Name.getAsIdentifierInfo(),
|
||||
Importer.Import(D->getAtLoc()),
|
||||
Importer.Import(D->getLParenLoc()),
|
||||
T,
|
||||
Importer.Import(D->getType()),
|
||||
TSI,
|
||||
D->getPropertyImplementation());
|
||||
Importer.Imported(D, ToProperty);
|
||||
ToProperty->setLexicalDeclContext(LexicalDC);
|
||||
|
@ -1862,16 +1862,18 @@ ObjCPropertyDecl *ObjCPropertyDecl::Create(ASTContext &C, DeclContext *DC,
|
||||
IdentifierInfo *Id,
|
||||
SourceLocation AtLoc,
|
||||
SourceLocation LParenLoc,
|
||||
TypeSourceInfo *T,
|
||||
QualType T,
|
||||
TypeSourceInfo *TSI,
|
||||
PropertyControl propControl) {
|
||||
return new (C, DC) ObjCPropertyDecl(DC, L, Id, AtLoc, LParenLoc, T);
|
||||
return new (C, DC) ObjCPropertyDecl(DC, L, Id, AtLoc, LParenLoc, T, TSI,
|
||||
propControl);
|
||||
}
|
||||
|
||||
ObjCPropertyDecl *ObjCPropertyDecl::CreateDeserialized(ASTContext &C,
|
||||
unsigned ID) {
|
||||
return new (C, ID) ObjCPropertyDecl(nullptr, SourceLocation(), nullptr,
|
||||
SourceLocation(), SourceLocation(),
|
||||
nullptr);
|
||||
QualType(), nullptr, None);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -37,6 +37,13 @@ namespace {
|
||||
|
||||
void Print(AccessSpecifier AS);
|
||||
|
||||
/// Print an Objective-C method type in parentheses.
|
||||
///
|
||||
/// \param Quals The Objective-C declaration qualifiers.
|
||||
/// \param T The type to print.
|
||||
void PrintObjCMethodType(ASTContext &Ctx, Decl::ObjCDeclQualifier Quals,
|
||||
QualType T);
|
||||
|
||||
public:
|
||||
DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy,
|
||||
unsigned Indentation = 0, bool PrintInstantiation = false)
|
||||
@ -930,24 +937,64 @@ void DeclPrinter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
|
||||
// Objective-C declarations
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// Strip off the top-level nullability annotation, if it's there.
|
||||
static Optional<NullabilityKind> stripOuterNullability(QualType &T) {
|
||||
if (auto attributed = dyn_cast<AttributedType>(T.getTypePtr())) {
|
||||
if (auto nullability = attributed->getImmediateNullability()) {
|
||||
T = attributed->getModifiedType();
|
||||
return nullability;
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
void DeclPrinter::PrintObjCMethodType(ASTContext &Ctx,
|
||||
Decl::ObjCDeclQualifier Quals,
|
||||
QualType T) {
|
||||
Out << '(';
|
||||
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_In)
|
||||
Out << "in ";
|
||||
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Inout)
|
||||
Out << "inout ";
|
||||
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Out)
|
||||
Out << "out ";
|
||||
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Bycopy)
|
||||
Out << "bycopy ";
|
||||
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Byref)
|
||||
Out << "byref ";
|
||||
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Oneway)
|
||||
Out << "oneway ";
|
||||
if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_CSNullability) {
|
||||
if (auto nullability = stripOuterNullability(T)) {
|
||||
Out << getNullabilitySpelling(*nullability).substr(2) << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
Out << Ctx.getUnqualifiedObjCPointerType(T).getAsString(Policy);
|
||||
Out << ')';
|
||||
}
|
||||
|
||||
void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
|
||||
if (OMD->isInstanceMethod())
|
||||
Out << "- ";
|
||||
else
|
||||
Out << "+ ";
|
||||
if (!OMD->getReturnType().isNull())
|
||||
Out << '(' << OMD->getASTContext()
|
||||
.getUnqualifiedObjCPointerType(OMD->getReturnType())
|
||||
.getAsString(Policy) << ")";
|
||||
if (!OMD->getReturnType().isNull()) {
|
||||
PrintObjCMethodType(OMD->getASTContext(), OMD->getObjCDeclQualifier(),
|
||||
OMD->getReturnType());
|
||||
}
|
||||
|
||||
std::string name = OMD->getSelector().getAsString();
|
||||
std::string::size_type pos, lastPos = 0;
|
||||
for (const auto *PI : OMD->params()) {
|
||||
// FIXME: selector is missing here!
|
||||
pos = name.find_first_of(':', lastPos);
|
||||
Out << " " << name.substr(lastPos, pos - lastPos);
|
||||
Out << ":(" << PI->getASTContext().getUnqualifiedObjCPointerType(PI->getType()).
|
||||
getAsString(Policy) << ')' << *PI;
|
||||
Out << " " << name.substr(lastPos, pos - lastPos) << ':';
|
||||
PrintObjCMethodType(OMD->getASTContext(),
|
||||
PI->getObjCDeclQualifier(),
|
||||
PI->getType());
|
||||
Out << *PI;
|
||||
lastPos = pos + 1;
|
||||
}
|
||||
|
||||
@ -1103,6 +1150,8 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
|
||||
else if (PDecl->getPropertyImplementation() == ObjCPropertyDecl::Optional)
|
||||
Out << "@optional\n";
|
||||
|
||||
QualType T = PDecl->getType();
|
||||
|
||||
Out << "@property";
|
||||
if (PDecl->getPropertyAttributes() != ObjCPropertyDecl::OBJC_PR_noattr) {
|
||||
bool first = true;
|
||||
@ -1161,10 +1210,19 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (PDecl->getPropertyAttributes() &
|
||||
ObjCPropertyDecl::OBJC_PR_nullability) {
|
||||
if (auto nullability = stripOuterNullability(T)) {
|
||||
Out << (first ? ' ' : ',')
|
||||
<< getNullabilitySpelling(*nullability).substr(2);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
(void) first; // Silence dead store warning due to idiomatic code.
|
||||
Out << " )";
|
||||
}
|
||||
Out << ' ' << PDecl->getASTContext().getUnqualifiedObjCPointerType(PDecl->getType()).
|
||||
Out << ' ' << PDecl->getASTContext().getUnqualifiedObjCPointerType(T).
|
||||
getAsString(Policy) << ' ' << *PDecl;
|
||||
if (Policy.PolishForDeclaration)
|
||||
Out << ';';
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
@ -645,3 +646,16 @@ const char *clang::getOperatorSpelling(OverloadedOperatorKind Operator) {
|
||||
|
||||
llvm_unreachable("Invalid OverloadedOperatorKind!");
|
||||
}
|
||||
|
||||
StringRef clang::getNullabilitySpelling(NullabilityKind kind) {
|
||||
switch (kind) {
|
||||
case NullabilityKind::NonNull:
|
||||
return "__nonnull";
|
||||
|
||||
case NullabilityKind::Nullable:
|
||||
return "__nullable";
|
||||
|
||||
case NullabilityKind::Unspecified:
|
||||
return "__null_unspecified";
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "clang/Parse/Parser.h"
|
||||
#include "RAIIObjectsForParser.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Basic/CharInfo.h"
|
||||
#include "clang/Parse/ParseDiagnostic.h"
|
||||
#include "clang/Sema/DeclSpec.h"
|
||||
@ -307,6 +308,58 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
|
||||
return ClsType;
|
||||
}
|
||||
|
||||
IdentifierInfo *Parser::getNullabilityKeyword(NullabilityKind nullability) {
|
||||
switch (nullability) {
|
||||
case NullabilityKind::NonNull:
|
||||
if (!Ident___nonnull)
|
||||
Ident___nonnull = PP.getIdentifierInfo("__nonnull");
|
||||
return Ident___nonnull;
|
||||
|
||||
case NullabilityKind::Nullable:
|
||||
if (!Ident___nullable)
|
||||
Ident___nullable = PP.getIdentifierInfo("__nullable");
|
||||
return Ident___nullable;
|
||||
|
||||
case NullabilityKind::Unspecified:
|
||||
if (!Ident___null_unspecified)
|
||||
Ident___null_unspecified = PP.getIdentifierInfo("__null_unspecified");
|
||||
return Ident___null_unspecified;
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an attribute for a context-sensitive type nullability to the given
|
||||
/// declarator.
|
||||
static void addContextSensitiveTypeNullability(Parser &P,
|
||||
Declarator &D,
|
||||
NullabilityKind nullability,
|
||||
SourceLocation nullabilityLoc,
|
||||
bool &addedToDeclSpec) {
|
||||
// Create the attribute.
|
||||
auto getNullabilityAttr = [&]() -> AttributeList * {
|
||||
auto attr = D.getAttributePool().create(
|
||||
P.getNullabilityKeyword(nullability),
|
||||
SourceRange(nullabilityLoc),
|
||||
nullptr, SourceLocation(),
|
||||
nullptr, 0,
|
||||
AttributeList::AS_Keyword);
|
||||
attr->setContextSensitiveKeywordAttribute();
|
||||
return attr;
|
||||
};
|
||||
|
||||
if (D.getNumTypeObjects() > 0) {
|
||||
// Add the attribute to the declarator chunk nearest the declarator.
|
||||
auto nullabilityAttr = getNullabilityAttr();
|
||||
DeclaratorChunk &chunk = D.getTypeObject(0);
|
||||
nullabilityAttr->setNext(chunk.getAttrListRef());
|
||||
chunk.getAttrListRef() = nullabilityAttr;
|
||||
} else if (!addedToDeclSpec) {
|
||||
// Otherwise, just put it on the declaration specifiers (if one
|
||||
// isn't there already).
|
||||
D.getMutableDeclSpec().addAttributes(getNullabilityAttr());
|
||||
addedToDeclSpec = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// objc-interface-decl-list:
|
||||
/// empty
|
||||
/// objc-interface-decl-list objc-property-decl [OBJC2]
|
||||
@ -445,6 +498,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
|
||||
ParseObjCPropertyAttribute(OCDS);
|
||||
}
|
||||
|
||||
bool addedToDeclSpec = false;
|
||||
auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) {
|
||||
if (FD.D.getIdentifier() == nullptr) {
|
||||
Diag(AtLoc, diag::err_objc_property_requires_field_name)
|
||||
@ -457,6 +511,13 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
|
||||
return;
|
||||
}
|
||||
|
||||
// Map a nullability property attribute to a context-sensitive keyword
|
||||
// attribute.
|
||||
if (OCDS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
|
||||
addContextSensitiveTypeNullability(*this, FD.D, OCDS.getNullability(),
|
||||
OCDS.getNullabilityLoc(),
|
||||
addedToDeclSpec);
|
||||
|
||||
// Install the property declarator into interfaceDecl.
|
||||
IdentifierInfo *SelName =
|
||||
OCDS.getGetterName() ? OCDS.getGetterName() : FD.D.getIdentifier();
|
||||
@ -510,6 +571,24 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
|
||||
Actions.ActOnAtEnd(getCurScope(), AtEnd, allMethods, allTUVariables);
|
||||
}
|
||||
|
||||
/// Diagnose redundant or conflicting nullability information.
|
||||
static void diagnoseRedundantPropertyNullability(Parser &P,
|
||||
ObjCDeclSpec &DS,
|
||||
NullabilityKind nullability,
|
||||
SourceLocation nullabilityLoc){
|
||||
if (DS.getNullability() == nullability) {
|
||||
P.Diag(nullabilityLoc, diag::warn_nullability_duplicate)
|
||||
<< static_cast<unsigned>(nullability) << true
|
||||
<< SourceRange(DS.getNullabilityLoc());
|
||||
return;
|
||||
}
|
||||
|
||||
P.Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< static_cast<unsigned>(nullability) << true
|
||||
<< static_cast<unsigned>(DS.getNullability()) << true
|
||||
<< SourceRange(DS.getNullabilityLoc());
|
||||
}
|
||||
|
||||
/// Parse property attribute declarations.
|
||||
///
|
||||
/// property-attr-decl: '(' property-attrlist ')'
|
||||
@ -529,6 +608,9 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
|
||||
/// strong
|
||||
/// weak
|
||||
/// unsafe_unretained
|
||||
/// nonnull
|
||||
/// nullable
|
||||
/// null_unspecified
|
||||
///
|
||||
void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
|
||||
assert(Tok.getKind() == tok::l_paren);
|
||||
@ -614,6 +696,27 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
|
||||
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter);
|
||||
DS.setGetterName(SelIdent);
|
||||
}
|
||||
} else if (II->isStr("nonnull")) {
|
||||
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
|
||||
diagnoseRedundantPropertyNullability(*this, DS,
|
||||
NullabilityKind::NonNull,
|
||||
Tok.getLocation());
|
||||
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
|
||||
DS.setNullability(Tok.getLocation(), NullabilityKind::NonNull);
|
||||
} else if (II->isStr("nullable")) {
|
||||
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
|
||||
diagnoseRedundantPropertyNullability(*this, DS,
|
||||
NullabilityKind::Nullable,
|
||||
Tok.getLocation());
|
||||
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
|
||||
DS.setNullability(Tok.getLocation(), NullabilityKind::Nullable);
|
||||
} else if (II->isStr("null_unspecified")) {
|
||||
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
|
||||
diagnoseRedundantPropertyNullability(*this, DS,
|
||||
NullabilityKind::Unspecified,
|
||||
Tok.getLocation());
|
||||
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
|
||||
DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
|
||||
} else {
|
||||
Diag(AttrName, diag::err_objc_expected_property_attr) << II;
|
||||
SkipUntil(tok::r_paren, StopAtSemi);
|
||||
@ -779,6 +882,17 @@ bool Parser::isTokIdentifier_in() const {
|
||||
/// objc-type-qualifier
|
||||
/// objc-type-qualifiers objc-type-qualifier
|
||||
///
|
||||
/// objc-type-qualifier:
|
||||
/// 'in'
|
||||
/// 'out'
|
||||
/// 'inout'
|
||||
/// 'oneway'
|
||||
/// 'bycopy'
|
||||
/// 'byref'
|
||||
/// 'nonnull'
|
||||
/// 'nullable'
|
||||
/// 'null_unspecified'
|
||||
///
|
||||
void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
|
||||
Declarator::TheContext Context) {
|
||||
assert(Context == Declarator::ObjCParameterContext ||
|
||||
@ -796,10 +910,13 @@ void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
|
||||
|
||||
const IdentifierInfo *II = Tok.getIdentifierInfo();
|
||||
for (unsigned i = 0; i != objc_NumQuals; ++i) {
|
||||
if (II != ObjCTypeQuals[i])
|
||||
if (II != ObjCTypeQuals[i] ||
|
||||
NextToken().is(tok::less) ||
|
||||
NextToken().is(tok::coloncolon))
|
||||
continue;
|
||||
|
||||
ObjCDeclSpec::ObjCDeclQualifier Qual;
|
||||
NullabilityKind Nullability;
|
||||
switch (i) {
|
||||
default: llvm_unreachable("Unknown decl qualifier");
|
||||
case objc_in: Qual = ObjCDeclSpec::DQ_In; break;
|
||||
@ -808,8 +925,28 @@ void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
|
||||
case objc_oneway: Qual = ObjCDeclSpec::DQ_Oneway; break;
|
||||
case objc_bycopy: Qual = ObjCDeclSpec::DQ_Bycopy; break;
|
||||
case objc_byref: Qual = ObjCDeclSpec::DQ_Byref; break;
|
||||
|
||||
case objc_nonnull:
|
||||
Qual = ObjCDeclSpec::DQ_CSNullability;
|
||||
Nullability = NullabilityKind::NonNull;
|
||||
break;
|
||||
|
||||
case objc_nullable:
|
||||
Qual = ObjCDeclSpec::DQ_CSNullability;
|
||||
Nullability = NullabilityKind::Nullable;
|
||||
break;
|
||||
|
||||
case objc_null_unspecified:
|
||||
Qual = ObjCDeclSpec::DQ_CSNullability;
|
||||
Nullability = NullabilityKind::Unspecified;
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: Diagnose redundant specifiers.
|
||||
DS.setObjCDeclQualifier(Qual);
|
||||
if (Qual == ObjCDeclSpec::DQ_CSNullability)
|
||||
DS.setNullability(Tok.getLocation(), Nullability);
|
||||
|
||||
ConsumeToken();
|
||||
II = nullptr;
|
||||
break;
|
||||
@ -889,6 +1026,14 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
|
||||
|
||||
// If that's not invalid, extract a type.
|
||||
if (!declarator.isInvalidType()) {
|
||||
// Map a nullability specifier to a context-sensitive keyword attribute.
|
||||
bool addedToDeclSpec = false;
|
||||
if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability)
|
||||
addContextSensitiveTypeNullability(*this, declarator,
|
||||
DS.getNullability(),
|
||||
DS.getNullabilityLoc(),
|
||||
addedToDeclSpec);
|
||||
|
||||
TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
|
||||
if (!type.isInvalid())
|
||||
Ty = type.get();
|
||||
@ -904,8 +1049,34 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
|
||||
Ident_instancetype = PP.getIdentifierInfo("instancetype");
|
||||
|
||||
if (Tok.getIdentifierInfo() == Ident_instancetype) {
|
||||
Ty = Actions.ActOnObjCInstanceType(Tok.getLocation());
|
||||
ConsumeToken();
|
||||
SourceLocation loc = ConsumeToken();
|
||||
Ty = Actions.ActOnObjCInstanceType(loc);
|
||||
|
||||
// Map a nullability specifier to a context-sensitive keyword attribute.
|
||||
if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability) {
|
||||
// Synthesize an abstract declarator so we can use Sema::ActOnTypeName.
|
||||
bool addedToDeclSpec = false;
|
||||
const char *prevSpec;
|
||||
unsigned diagID;
|
||||
DeclSpec declSpec(AttrFactory);
|
||||
declSpec.setObjCQualifiers(&DS);
|
||||
declSpec.SetTypeSpecType(DeclSpec::TST_typename, loc, prevSpec, diagID,
|
||||
Ty,
|
||||
Actions.getASTContext().getPrintingPolicy());
|
||||
declSpec.SetRangeEnd(loc);
|
||||
Declarator declarator(declSpec, context);
|
||||
|
||||
// Add the context-sensitive keyword attribute.
|
||||
addContextSensitiveTypeNullability(*this, declarator,
|
||||
DS.getNullability(),
|
||||
DS.getNullabilityLoc(),
|
||||
addedToDeclSpec);
|
||||
|
||||
|
||||
TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
|
||||
if (!type.isInvalid())
|
||||
Ty = type.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -463,6 +463,10 @@ void Parser::Initialize() {
|
||||
ObjCTypeQuals[objc_oneway] = &PP.getIdentifierTable().get("oneway");
|
||||
ObjCTypeQuals[objc_bycopy] = &PP.getIdentifierTable().get("bycopy");
|
||||
ObjCTypeQuals[objc_byref] = &PP.getIdentifierTable().get("byref");
|
||||
ObjCTypeQuals[objc_nonnull] = &PP.getIdentifierTable().get("nonnull");
|
||||
ObjCTypeQuals[objc_nullable] = &PP.getIdentifierTable().get("nullable");
|
||||
ObjCTypeQuals[objc_null_unspecified]
|
||||
= &PP.getIdentifierTable().get("null_unspecified");
|
||||
}
|
||||
|
||||
Ident_instancetype = nullptr;
|
||||
|
@ -2471,9 +2471,13 @@ static void mergeParamDeclTypes(ParmVarDecl *NewParam,
|
||||
if (auto Oldnullability = OldParam->getType()->getNullability(S.Context)) {
|
||||
if (auto Newnullability = NewParam->getType()->getNullability(S.Context)) {
|
||||
if (*Oldnullability != *Newnullability) {
|
||||
unsigned unsNewnullability = static_cast<unsigned>(*Newnullability);
|
||||
unsigned unsOldnullability = static_cast<unsigned>(*Oldnullability);
|
||||
S.Diag(NewParam->getLocation(), diag::warn_mismatched_nullability_attr)
|
||||
<< static_cast<unsigned>(*Newnullability)
|
||||
<< static_cast<unsigned>(*Oldnullability);
|
||||
<< unsNewnullability
|
||||
<< ((NewParam->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)
|
||||
<< unsOldnullability
|
||||
<< ((OldParam->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0);
|
||||
S.Diag(OldParam->getLocation(), diag::note_previous_declaration);
|
||||
}
|
||||
}
|
||||
|
@ -1366,6 +1366,13 @@ static SourceRange getTypeRange(TypeSourceInfo *TSI) {
|
||||
return (TSI ? TSI->getTypeLoc().getSourceRange() : SourceRange());
|
||||
}
|
||||
|
||||
/// Determine whether two set of Objective-C declaration qualifiers conflict.
|
||||
static bool objcModifiersConflict(Decl::ObjCDeclQualifier x,
|
||||
Decl::ObjCDeclQualifier y) {
|
||||
return (x & ~Decl::OBJC_TQ_CSNullability) !=
|
||||
(y & ~Decl::OBJC_TQ_CSNullability);
|
||||
}
|
||||
|
||||
static bool CheckMethodOverrideReturn(Sema &S,
|
||||
ObjCMethodDecl *MethodImpl,
|
||||
ObjCMethodDecl *MethodDecl,
|
||||
@ -1373,8 +1380,8 @@ static bool CheckMethodOverrideReturn(Sema &S,
|
||||
bool IsOverridingMode,
|
||||
bool Warn) {
|
||||
if (IsProtocolMethodDecl &&
|
||||
(MethodDecl->getObjCDeclQualifier() !=
|
||||
MethodImpl->getObjCDeclQualifier())) {
|
||||
objcModifiersConflict(MethodDecl->getObjCDeclQualifier(),
|
||||
MethodImpl->getObjCDeclQualifier())) {
|
||||
if (Warn) {
|
||||
S.Diag(MethodImpl->getLocation(),
|
||||
(IsOverridingMode
|
||||
@ -1388,7 +1395,24 @@ static bool CheckMethodOverrideReturn(Sema &S,
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Warn && IsOverridingMode &&
|
||||
!isa<ObjCImplementationDecl>(MethodImpl->getDeclContext()) &&
|
||||
!S.Context.hasSameNullabilityTypeQualifier(MethodImpl->getReturnType(),
|
||||
MethodDecl->getReturnType(),
|
||||
false)) {
|
||||
unsigned unsNullabilityMethodImpl =
|
||||
static_cast<unsigned>(*MethodImpl->getReturnType()->getNullability(S.Context));
|
||||
unsigned unsNullabilityMethodDecl =
|
||||
static_cast<unsigned>(*MethodDecl->getReturnType()->getNullability(S.Context));
|
||||
S.Diag(MethodImpl->getLocation(),
|
||||
diag::warn_conflicting_nullability_attr_overriding_ret_types)
|
||||
<< unsNullabilityMethodImpl
|
||||
<< ((MethodImpl->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)
|
||||
<< unsNullabilityMethodDecl
|
||||
<< ((MethodDecl->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0);
|
||||
S.Diag(MethodDecl->getLocation(), diag::note_previous_declaration);
|
||||
}
|
||||
|
||||
if (S.Context.hasSameUnqualifiedType(MethodImpl->getReturnType(),
|
||||
MethodDecl->getReturnType()))
|
||||
return true;
|
||||
@ -1438,8 +1462,8 @@ static bool CheckMethodOverrideParam(Sema &S,
|
||||
bool IsOverridingMode,
|
||||
bool Warn) {
|
||||
if (IsProtocolMethodDecl &&
|
||||
(ImplVar->getObjCDeclQualifier() !=
|
||||
IfaceVar->getObjCDeclQualifier())) {
|
||||
objcModifiersConflict(ImplVar->getObjCDeclQualifier(),
|
||||
IfaceVar->getObjCDeclQualifier())) {
|
||||
if (Warn) {
|
||||
if (IsOverridingMode)
|
||||
S.Diag(ImplVar->getLocation(),
|
||||
@ -1459,7 +1483,19 @@ static bool CheckMethodOverrideParam(Sema &S,
|
||||
|
||||
QualType ImplTy = ImplVar->getType();
|
||||
QualType IfaceTy = IfaceVar->getType();
|
||||
|
||||
if (Warn && IsOverridingMode &&
|
||||
!isa<ObjCImplementationDecl>(MethodImpl->getDeclContext()) &&
|
||||
!S.Context.hasSameNullabilityTypeQualifier(ImplTy, IfaceTy, true)) {
|
||||
unsigned unsImplTy = static_cast<unsigned>(*ImplTy->getNullability(S.Context));
|
||||
unsigned unsIfaceTy = static_cast<unsigned>(*IfaceTy->getNullability(S.Context));
|
||||
S.Diag(ImplVar->getLocation(),
|
||||
diag::warn_conflicting_nullability_attr_overriding_param_types)
|
||||
<< unsImplTy
|
||||
<< ((ImplVar->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)
|
||||
<< unsIfaceTy
|
||||
<< ((IfaceVar->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0);
|
||||
S.Diag(IfaceVar->getLocation(), diag::note_previous_declaration);
|
||||
}
|
||||
if (S.Context.hasSameUnqualifiedType(ImplTy, IfaceTy))
|
||||
return true;
|
||||
|
||||
@ -3121,6 +3157,89 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
|
||||
ObjCMethod->setOverriding(hasOverriddenMethodsInBaseOrProtocol);
|
||||
}
|
||||
|
||||
/// Merge type nullability from for a redeclaration of the same entity,
|
||||
/// producing the updated type of the redeclared entity.
|
||||
static QualType mergeTypeNullabilityForRedecl(Sema &S, SourceLocation loc,
|
||||
QualType type,
|
||||
bool usesCSKeyword,
|
||||
SourceLocation prevLoc,
|
||||
QualType prevType,
|
||||
bool prevUsesCSKeyword) {
|
||||
// Determine the nullability of both types.
|
||||
auto nullability = type->getNullability(S.Context);
|
||||
auto prevNullability = prevType->getNullability(S.Context);
|
||||
|
||||
// Easy case: both have nullability.
|
||||
if (nullability.hasValue() == prevNullability.hasValue()) {
|
||||
// Neither has nullability; continue.
|
||||
if (!nullability)
|
||||
return type;
|
||||
|
||||
// The nullabilities are equivalent; do nothing.
|
||||
if (*nullability == *prevNullability)
|
||||
return type;
|
||||
|
||||
// Complain about mismatched nullability.
|
||||
S.Diag(loc, diag::err_nullability_conflicting)
|
||||
<< static_cast<unsigned>(*nullability) << usesCSKeyword
|
||||
<< static_cast<unsigned>(*prevNullability) << prevUsesCSKeyword;
|
||||
return type;
|
||||
}
|
||||
|
||||
// If it's the redeclaration that has nullability, don't change anything.
|
||||
if (nullability)
|
||||
return type;
|
||||
|
||||
// Otherwise, provide the result with the same nullability.
|
||||
return S.Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(*prevNullability),
|
||||
type, type);
|
||||
}
|
||||
|
||||
/// Merge information from the declaration of a method in the @interface
|
||||
/// (or a category/extension) into the corresponding method in the
|
||||
/// @implementation (for a class or category).
|
||||
static void mergeInterfaceMethodToImpl(Sema &S,
|
||||
ObjCMethodDecl *method,
|
||||
ObjCMethodDecl *prevMethod) {
|
||||
// Merge the objc_requires_super attribute.
|
||||
if (prevMethod->hasAttr<ObjCRequiresSuperAttr>() &&
|
||||
!method->hasAttr<ObjCRequiresSuperAttr>()) {
|
||||
// merge the attribute into implementation.
|
||||
method->addAttr(
|
||||
ObjCRequiresSuperAttr::CreateImplicit(S.Context,
|
||||
method->getLocation()));
|
||||
}
|
||||
|
||||
// Merge nullability of the result type.
|
||||
QualType newReturnType
|
||||
= mergeTypeNullabilityForRedecl(
|
||||
S, method->getReturnTypeSourceRange().getBegin(),
|
||||
method->getReturnType(),
|
||||
method->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability,
|
||||
prevMethod->getReturnTypeSourceRange().getBegin(),
|
||||
prevMethod->getReturnType(),
|
||||
prevMethod->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability);
|
||||
method->setReturnType(newReturnType);
|
||||
|
||||
// Handle each of the parameters.
|
||||
unsigned numParams = method->param_size();
|
||||
unsigned numPrevParams = prevMethod->param_size();
|
||||
for (unsigned i = 0, n = std::min(numParams, numPrevParams); i != n; ++i) {
|
||||
ParmVarDecl *param = method->param_begin()[i];
|
||||
ParmVarDecl *prevParam = prevMethod->param_begin()[i];
|
||||
|
||||
// Merge nullability.
|
||||
QualType newParamType
|
||||
= mergeTypeNullabilityForRedecl(
|
||||
S, param->getLocation(), param->getType(),
|
||||
param->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability,
|
||||
prevParam->getLocation(), prevParam->getType(),
|
||||
prevParam->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability);
|
||||
param->setType(newParamType);
|
||||
}
|
||||
}
|
||||
|
||||
Decl *Sema::ActOnMethodDeclaration(
|
||||
Scope *S,
|
||||
SourceLocation MethodLoc, SourceLocation EndLoc,
|
||||
@ -3151,7 +3270,9 @@ Decl *Sema::ActOnMethodDeclaration(
|
||||
if (CheckFunctionReturnType(resultDeclType, MethodLoc))
|
||||
return nullptr;
|
||||
|
||||
HasRelatedResultType = (resultDeclType == Context.getObjCInstanceType());
|
||||
QualType bareResultType = resultDeclType;
|
||||
(void)AttributedType::stripOuterNullability(bareResultType);
|
||||
HasRelatedResultType = (bareResultType == Context.getObjCInstanceType());
|
||||
} else { // get the type for "id".
|
||||
resultDeclType = Context.getObjCIdType();
|
||||
Diag(MethodLoc, diag::warn_missing_method_return_type)
|
||||
@ -3252,22 +3373,20 @@ Decl *Sema::ActOnMethodDeclaration(
|
||||
ImpDecl->addClassMethod(ObjCMethod);
|
||||
}
|
||||
|
||||
ObjCMethodDecl *IMD = nullptr;
|
||||
if (ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface())
|
||||
IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
|
||||
ObjCMethod->isInstanceMethod());
|
||||
if (IMD && IMD->hasAttr<ObjCRequiresSuperAttr>() &&
|
||||
!ObjCMethod->hasAttr<ObjCRequiresSuperAttr>()) {
|
||||
// merge the attribute into implementation.
|
||||
ObjCMethod->addAttr(ObjCRequiresSuperAttr::CreateImplicit(Context,
|
||||
ObjCMethod->getLocation()));
|
||||
}
|
||||
if (isa<ObjCCategoryImplDecl>(ImpDecl)) {
|
||||
ObjCMethodFamily family =
|
||||
ObjCMethod->getSelector().getMethodFamily();
|
||||
if (family == OMF_dealloc && IMD && IMD->isOverriding())
|
||||
Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
|
||||
<< ObjCMethod->getDeclName();
|
||||
// Merge information from the @interface declaration into the
|
||||
// @implementation.
|
||||
if (ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface()) {
|
||||
if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
|
||||
ObjCMethod->isInstanceMethod())) {
|
||||
mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD);
|
||||
|
||||
// Warn about defining -dealloc in a category.
|
||||
if (isa<ObjCCategoryImplDecl>(ImpDecl) && IMD->isOverriding() &&
|
||||
ObjCMethod->getSelector().getMethodFamily() == OMF_dealloc) {
|
||||
Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
|
||||
<< ObjCMethod->getDeclName();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod);
|
||||
|
@ -1135,49 +1135,150 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf(SourceLocation Loc) {
|
||||
}
|
||||
|
||||
static QualType stripObjCInstanceType(ASTContext &Context, QualType T) {
|
||||
QualType origType = T;
|
||||
if (auto nullability = AttributedType::stripOuterNullability(T)) {
|
||||
if (T == Context.getObjCInstanceType()) {
|
||||
return Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(*nullability),
|
||||
Context.getObjCIdType(),
|
||||
Context.getObjCIdType());
|
||||
}
|
||||
|
||||
return origType;
|
||||
}
|
||||
|
||||
if (T == Context.getObjCInstanceType())
|
||||
return Context.getObjCIdType();
|
||||
|
||||
return T;
|
||||
return origType;
|
||||
}
|
||||
|
||||
QualType Sema::getMessageSendResultType(QualType ReceiverType,
|
||||
ObjCMethodDecl *Method,
|
||||
bool isClassMessage, bool isSuperMessage) {
|
||||
/// Determine the result type of a message send based on the receiver type,
|
||||
/// method, and the kind of message send.
|
||||
///
|
||||
/// This is the "base" result type, which will still need to be adjusted
|
||||
/// to account for nullability.
|
||||
static QualType getBaseMessageSendResultType(Sema &S,
|
||||
QualType ReceiverType,
|
||||
ObjCMethodDecl *Method,
|
||||
bool isClassMessage,
|
||||
bool isSuperMessage) {
|
||||
assert(Method && "Must have a method");
|
||||
if (!Method->hasRelatedResultType())
|
||||
return Method->getSendResultType();
|
||||
|
||||
|
||||
ASTContext &Context = S.Context;
|
||||
|
||||
// Local function that transfers the nullability of the method's
|
||||
// result type to the returned result.
|
||||
auto transferNullability = [&](QualType type) -> QualType {
|
||||
// If the method's result type has nullability, extract it.
|
||||
if (auto nullability = Method->getSendResultType()->getNullability(Context)){
|
||||
// Strip off any outer nullability sugar from the provided type.
|
||||
(void)AttributedType::stripOuterNullability(type);
|
||||
|
||||
// Form a new attributed type using the method result type's nullability.
|
||||
return Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(*nullability),
|
||||
type,
|
||||
type);
|
||||
}
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
// If a method has a related return type:
|
||||
// - if the method found is an instance method, but the message send
|
||||
// was a class message send, T is the declared return type of the method
|
||||
// found
|
||||
if (Method->isInstanceMethod() && isClassMessage)
|
||||
return stripObjCInstanceType(Context, Method->getSendResultType());
|
||||
|
||||
// - if the receiver is super, T is a pointer to the class of the
|
||||
|
||||
// - if the receiver is super, T is a pointer to the class of the
|
||||
// enclosing method definition
|
||||
if (isSuperMessage) {
|
||||
if (ObjCMethodDecl *CurMethod = getCurMethodDecl())
|
||||
if (ObjCInterfaceDecl *Class = CurMethod->getClassInterface())
|
||||
return Context.getObjCObjectPointerType(
|
||||
Context.getObjCInterfaceType(Class));
|
||||
if (ObjCMethodDecl *CurMethod = S.getCurMethodDecl())
|
||||
if (ObjCInterfaceDecl *Class = CurMethod->getClassInterface()) {
|
||||
return transferNullability(
|
||||
Context.getObjCObjectPointerType(
|
||||
Context.getObjCInterfaceType(Class)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// - if the receiver is the name of a class U, T is a pointer to U
|
||||
if (ReceiverType->getAs<ObjCInterfaceType>() ||
|
||||
ReceiverType->isObjCQualifiedInterfaceType())
|
||||
return Context.getObjCObjectPointerType(ReceiverType);
|
||||
// - if the receiver is of type Class or qualified Class type,
|
||||
return transferNullability(Context.getObjCObjectPointerType(ReceiverType));
|
||||
// - if the receiver is of type Class or qualified Class type,
|
||||
// T is the declared return type of the method.
|
||||
if (ReceiverType->isObjCClassType() ||
|
||||
ReceiverType->isObjCQualifiedClassType())
|
||||
return stripObjCInstanceType(Context, Method->getSendResultType());
|
||||
|
||||
|
||||
// - if the receiver is id, qualified id, Class, or qualified Class, T
|
||||
// is the receiver type, otherwise
|
||||
// - T is the type of the receiver expression.
|
||||
return ReceiverType;
|
||||
return transferNullability(ReceiverType);
|
||||
}
|
||||
|
||||
QualType Sema::getMessageSendResultType(QualType ReceiverType,
|
||||
ObjCMethodDecl *Method,
|
||||
bool isClassMessage,
|
||||
bool isSuperMessage) {
|
||||
// Produce the result type.
|
||||
QualType resultType = getBaseMessageSendResultType(*this, ReceiverType,
|
||||
Method,
|
||||
isClassMessage,
|
||||
isSuperMessage);
|
||||
|
||||
// Map the nullability of the result into a table index.
|
||||
unsigned receiverNullabilityIdx = 0;
|
||||
if (auto nullability = ReceiverType->getNullability(Context))
|
||||
receiverNullabilityIdx = 1 + static_cast<unsigned>(*nullability);
|
||||
|
||||
unsigned resultNullabilityIdx = 0;
|
||||
if (auto nullability = resultType->getNullability(Context))
|
||||
resultNullabilityIdx = 1 + static_cast<unsigned>(*nullability);
|
||||
|
||||
// The table of nullability mappings, indexed by the receiver's nullability
|
||||
// and then the result type's nullability.
|
||||
static const uint8_t None = 0;
|
||||
static const uint8_t NonNull = 1;
|
||||
static const uint8_t Nullable = 2;
|
||||
static const uint8_t Unspecified = 3;
|
||||
static const uint8_t nullabilityMap[4][4] = {
|
||||
// None NonNull Nullable Unspecified
|
||||
/* None */ { None, None, Nullable, None },
|
||||
/* NonNull */ { None, NonNull, Nullable, Unspecified },
|
||||
/* Nullable */ { Nullable, Nullable, Nullable, Nullable },
|
||||
/* Unspecified */ { None, Unspecified, Nullable, Unspecified }
|
||||
};
|
||||
|
||||
unsigned newResultNullabilityIdx
|
||||
= nullabilityMap[receiverNullabilityIdx][resultNullabilityIdx];
|
||||
if (newResultNullabilityIdx == resultNullabilityIdx)
|
||||
return resultType;
|
||||
|
||||
// Strip off the existing nullability. This removes as little type sugar as
|
||||
// possible.
|
||||
do {
|
||||
if (auto attributed = dyn_cast<AttributedType>(resultType.getTypePtr())) {
|
||||
resultType = attributed->getModifiedType();
|
||||
} else {
|
||||
resultType = resultType.getDesugaredType(Context);
|
||||
}
|
||||
} while (resultType->getNullability(Context));
|
||||
|
||||
// Add nullability back if needed.
|
||||
if (newResultNullabilityIdx > 0) {
|
||||
auto newNullability
|
||||
= static_cast<NullabilityKind>(newResultNullabilityIdx-1);
|
||||
return Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(newNullability),
|
||||
resultType, resultType);
|
||||
}
|
||||
|
||||
return resultType;
|
||||
}
|
||||
|
||||
/// Look for an ObjC method whose result type exactly matches the given type.
|
||||
|
@ -149,7 +149,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
|
||||
TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S);
|
||||
QualType T = TSI->getType();
|
||||
Attributes |= deduceWeakPropertyFromType(*this, T);
|
||||
|
||||
bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) ||
|
||||
// default is readwrite!
|
||||
!(Attributes & ObjCDeclSpec::DQ_PR_readonly));
|
||||
@ -173,7 +172,7 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
|
||||
isAssign, isReadWrite,
|
||||
Attributes,
|
||||
ODS.getPropertyAttributes(),
|
||||
isOverridingProperty, TSI,
|
||||
isOverridingProperty, T, TSI,
|
||||
MethodImplKind);
|
||||
if (!Res)
|
||||
return nullptr;
|
||||
@ -184,7 +183,7 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
|
||||
Res = CreatePropertyDecl(S, ClassDecl, AtLoc, LParenLoc, FD,
|
||||
GetterSel, SetterSel, isAssign, isReadWrite,
|
||||
Attributes, ODS.getPropertyAttributes(),
|
||||
TSI, MethodImplKind);
|
||||
T, TSI, MethodImplKind);
|
||||
if (lexicalDC)
|
||||
Res->setLexicalDeclContext(lexicalDC);
|
||||
}
|
||||
@ -322,7 +321,8 @@ Sema::HandlePropertyInClassExtension(Scope *S,
|
||||
const unsigned Attributes,
|
||||
const unsigned AttributesAsWritten,
|
||||
bool *isOverridingProperty,
|
||||
TypeSourceInfo *T,
|
||||
QualType T,
|
||||
TypeSourceInfo *TSI,
|
||||
tok::ObjCKeywordKind MethodImplKind) {
|
||||
ObjCCategoryDecl *CDecl = cast<ObjCCategoryDecl>(CurContext);
|
||||
// Diagnose if this property is already in continuation class.
|
||||
@ -348,7 +348,7 @@ Sema::HandlePropertyInClassExtension(Scope *S,
|
||||
// FIXME. We should really be using CreatePropertyDecl for this.
|
||||
ObjCPropertyDecl *PDecl =
|
||||
ObjCPropertyDecl::Create(Context, DC, FD.D.getIdentifierLoc(),
|
||||
PropertyId, AtLoc, LParenLoc, T);
|
||||
PropertyId, AtLoc, LParenLoc, T, TSI);
|
||||
PDecl->setPropertyAttributesAsWritten(
|
||||
makePropertyAttributesAsWritten(AttributesAsWritten));
|
||||
if (Attributes & ObjCDeclSpec::DQ_PR_readonly)
|
||||
@ -359,6 +359,8 @@ Sema::HandlePropertyInClassExtension(Scope *S,
|
||||
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nonatomic);
|
||||
if (Attributes & ObjCDeclSpec::DQ_PR_atomic)
|
||||
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_atomic);
|
||||
if (Attributes & ObjCDeclSpec::DQ_PR_nullability)
|
||||
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nullability);
|
||||
// Set setter/getter selector name. Needed later.
|
||||
PDecl->setGetterName(GetterSel);
|
||||
PDecl->setSetterName(SetterSel);
|
||||
@ -383,7 +385,8 @@ Sema::HandlePropertyInClassExtension(Scope *S,
|
||||
ObjCPropertyDecl *PrimaryPDecl =
|
||||
CreatePropertyDecl(S, CCPrimary, AtLoc, LParenLoc,
|
||||
FD, GetterSel, SetterSel, isAssign, isReadWrite,
|
||||
Attributes,AttributesAsWritten, T, MethodImplKind, DC);
|
||||
Attributes,AttributesAsWritten, T, TSI, MethodImplKind,
|
||||
DC);
|
||||
|
||||
// A case of continuation class adding a new property in the class. This
|
||||
// is not what it was meant for. However, gcc supports it and so should we.
|
||||
@ -531,11 +534,11 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
|
||||
const bool isReadWrite,
|
||||
const unsigned Attributes,
|
||||
const unsigned AttributesAsWritten,
|
||||
QualType T,
|
||||
TypeSourceInfo *TInfo,
|
||||
tok::ObjCKeywordKind MethodImplKind,
|
||||
DeclContext *lexicalDC){
|
||||
IdentifierInfo *PropertyId = FD.D.getIdentifier();
|
||||
QualType T = TInfo->getType();
|
||||
|
||||
// Issue a warning if property is 'assign' as default and its object, which is
|
||||
// gc'able conforms to NSCopying protocol
|
||||
@ -564,7 +567,8 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
|
||||
DeclContext *DC = cast<DeclContext>(CDecl);
|
||||
ObjCPropertyDecl *PDecl = ObjCPropertyDecl::Create(Context, DC,
|
||||
FD.D.getIdentifierLoc(),
|
||||
PropertyId, AtLoc, LParenLoc, TInfo);
|
||||
PropertyId, AtLoc,
|
||||
LParenLoc, T, TInfo);
|
||||
|
||||
if (ObjCPropertyDecl *prevDecl =
|
||||
ObjCPropertyDecl::findPropertyDecl(DC, PropertyId)) {
|
||||
@ -639,6 +643,9 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
|
||||
else if (MethodImplKind == tok::objc_optional)
|
||||
PDecl->setPropertyImplementation(ObjCPropertyDecl::Optional);
|
||||
|
||||
if (Attributes & ObjCDeclSpec::DQ_PR_nullability)
|
||||
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nullability);
|
||||
|
||||
return PDecl;
|
||||
}
|
||||
|
||||
@ -2228,6 +2235,23 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl,
|
||||
Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
|
||||
}
|
||||
|
||||
if ((Attributes & ObjCDeclSpec::DQ_PR_weak) &&
|
||||
!(Attributes & ObjCDeclSpec::DQ_PR_readonly)) {
|
||||
// 'weak' and 'nonnull' are mutually exclusive.
|
||||
if (auto nullability = PropertyTy->getNullability(Context)) {
|
||||
if (*nullability == NullabilityKind::NonNull)
|
||||
Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
|
||||
<< "nonnull" << "weak";
|
||||
} else {
|
||||
PropertyTy =
|
||||
Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(NullabilityKind::Nullable),
|
||||
PropertyTy, PropertyTy);
|
||||
TypeSourceInfo *TSInfo = PropertyDecl->getTypeSourceInfo();
|
||||
PropertyDecl->setType(PropertyTy, TSInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if ((Attributes & ObjCDeclSpec::DQ_PR_atomic) &&
|
||||
(Attributes & ObjCDeclSpec::DQ_PR_nonatomic)) {
|
||||
Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
|
||||
|
@ -4522,34 +4522,10 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State,
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Map a nullability attribute kind to a nullability kind.
|
||||
static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
|
||||
switch (kind) {
|
||||
case AttributeList::AT_TypeNonNull:
|
||||
return NullabilityKind::NonNull;
|
||||
|
||||
case AttributeList::AT_TypeNullable:
|
||||
return NullabilityKind::Nullable;
|
||||
|
||||
case AttributeList::AT_TypeNullUnspecified:
|
||||
return NullabilityKind::Unspecified;
|
||||
|
||||
default:
|
||||
llvm_unreachable("not a nullability attribute kind");
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a nullability type attribute.
|
||||
static bool handleNullabilityTypeAttr(TypeProcessingState &state,
|
||||
AttributeList &attr,
|
||||
QualType &type) {
|
||||
Sema &S = state.getSema();
|
||||
ASTContext &Context = S.Context;
|
||||
|
||||
// Determine the nullability.
|
||||
AttributeList::Kind kind = attr.getKind();
|
||||
NullabilityKind nullability = mapNullabilityAttrKind(kind);
|
||||
|
||||
bool Sema::checkNullabilityTypeSpecifier(QualType &type,
|
||||
NullabilityKind nullability,
|
||||
SourceLocation nullabilityLoc,
|
||||
bool isContextSensitive) {
|
||||
// Check for existing nullability attributes on the type.
|
||||
QualType desugared = type;
|
||||
while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
|
||||
@ -4557,19 +4533,24 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
|
||||
if (auto existingNullability = attributed->getImmediateNullability()) {
|
||||
// Duplicated nullability.
|
||||
if (nullability == *existingNullability) {
|
||||
S.Diag(attr.getLoc(), diag::warn_duplicate_nullability)
|
||||
<< static_cast<unsigned>(nullability);
|
||||
return true;
|
||||
}
|
||||
Diag(nullabilityLoc, diag::warn_nullability_duplicate)
|
||||
<< static_cast<unsigned>(nullability)
|
||||
<< isContextSensitive
|
||||
<< FixItHint::CreateRemoval(nullabilityLoc);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Conflicting nullability.
|
||||
S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
|
||||
<< static_cast<unsigned>(nullability)
|
||||
<< static_cast<unsigned>(*existingNullability);
|
||||
Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< static_cast<unsigned>(nullability)
|
||||
<< isContextSensitive
|
||||
<< static_cast<unsigned>(*existingNullability)
|
||||
<< false;
|
||||
return true;
|
||||
}
|
||||
|
||||
desugared = attributed->getEquivalentType();
|
||||
desugared = attributed->getModifiedType();
|
||||
}
|
||||
|
||||
// If there is already a different nullability specifier, complain.
|
||||
@ -4578,8 +4559,9 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
|
||||
// provide a useful Fix-It.
|
||||
if (auto existingNullability = desugared->getNullability(Context)) {
|
||||
if (nullability != *existingNullability) {
|
||||
S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
|
||||
Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< static_cast<unsigned>(nullability)
|
||||
<< isContextSensitive
|
||||
<< static_cast<unsigned>(*existingNullability);
|
||||
|
||||
// Try to find the typedef with the existing nullability specifier.
|
||||
@ -4589,7 +4571,7 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
|
||||
if (auto typedefNullability
|
||||
= AttributedType::stripOuterNullability(underlyingType)) {
|
||||
if (*typedefNullability == *existingNullability) {
|
||||
S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
|
||||
Diag(typedefDecl->getLocation(), diag::note_nullability_here)
|
||||
<< static_cast<unsigned>(*existingNullability);
|
||||
}
|
||||
}
|
||||
@ -4600,31 +4582,35 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
|
||||
}
|
||||
|
||||
// If this definitely isn't a pointer type, reject the specifier.
|
||||
if (!type->canHaveNullability()) {
|
||||
S.Diag(attr.getLoc(), diag::err_nullability_nonpointer)
|
||||
<< static_cast<unsigned>(nullability) << type;
|
||||
if (!desugared->canHaveNullability()) {
|
||||
Diag(nullabilityLoc, diag::err_nullability_nonpointer)
|
||||
<< static_cast<unsigned>(nullability) << isContextSensitive << type;
|
||||
return true;
|
||||
}
|
||||
|
||||
// For the context-sensitive keywords/Objective-C property
|
||||
// attributes, require that the type be a single-level pointer.
|
||||
if (isContextSensitive) {
|
||||
// Make sure that the pointee isn't itself a pointer type.
|
||||
QualType pointeeType = desugared->getPointeeType();
|
||||
if (pointeeType->isAnyPointerType() ||
|
||||
pointeeType->isObjCObjectPointerType() ||
|
||||
pointeeType->isMemberPointerType()) {
|
||||
Diag(nullabilityLoc, diag::err_nullability_cs_multilevel)
|
||||
<< static_cast<unsigned>(nullability)
|
||||
<< type;
|
||||
Diag(nullabilityLoc, diag::note_nullability_type_specifier)
|
||||
<< static_cast<unsigned>(nullability)
|
||||
<< type
|
||||
<< FixItHint::CreateReplacement(nullabilityLoc,
|
||||
getNullabilitySpelling(nullability));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Form the attributed type.
|
||||
AttributedType::Kind typeAttrKind;
|
||||
switch (kind) {
|
||||
case AttributeList::AT_TypeNonNull:
|
||||
typeAttrKind = AttributedType::attr_nonnull;
|
||||
break;
|
||||
|
||||
case AttributeList::AT_TypeNullable:
|
||||
typeAttrKind = AttributedType::attr_nullable;
|
||||
break;
|
||||
|
||||
case AttributeList::AT_TypeNullUnspecified:
|
||||
typeAttrKind = AttributedType::attr_null_unspecified;
|
||||
break;
|
||||
|
||||
default:
|
||||
llvm_unreachable("Not a nullability specifier");
|
||||
}
|
||||
type = S.Context.getAttributedType(typeAttrKind, type, type);
|
||||
type = Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(nullability), type, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4642,6 +4628,23 @@ static bool hasNullabilityAttr(const AttributeList *attrs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Map a nullability attribute kind to a nullability kind.
|
||||
static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
|
||||
switch (kind) {
|
||||
case AttributeList::AT_TypeNonNull:
|
||||
return NullabilityKind::NonNull;
|
||||
|
||||
case AttributeList::AT_TypeNullable:
|
||||
return NullabilityKind::Nullable;
|
||||
|
||||
case AttributeList::AT_TypeNullUnspecified:
|
||||
return NullabilityKind::Unspecified;
|
||||
|
||||
default:
|
||||
llvm_unreachable("not a nullability attribute kind");
|
||||
}
|
||||
}
|
||||
|
||||
/// Distribute a nullability type attribute that cannot be applied to
|
||||
/// the type specifier to a pointer, block pointer, or member pointer
|
||||
/// declarator, complaining if necessary.
|
||||
@ -5233,7 +5236,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
|
||||
// dependent type, because that complicates the user model.
|
||||
if (type->canHaveNullability() || type->isDependentType() ||
|
||||
!distributeNullabilityTypeAttr(state, type, attr)) {
|
||||
if (handleNullabilityTypeAttr(state, attr, type)) {
|
||||
if (state.getSema().checkNullabilityTypeSpecifier(
|
||||
type,
|
||||
mapNullabilityAttrKind(attr.getKind()),
|
||||
attr.getLoc(),
|
||||
attr.isContextSensitiveKeywordAttribute())) {
|
||||
attr.setInvalid();
|
||||
}
|
||||
|
||||
|
@ -5392,7 +5392,7 @@ QualType TreeTransform<Derived>::TransformAttributedType(
|
||||
if (auto nullability = oldType->getImmediateNullability()) {
|
||||
if (!modifiedType->canHaveNullability()) {
|
||||
SemaRef.Diag(TL.getAttrNameLoc(), diag::err_nullability_nonpointer)
|
||||
<< static_cast<unsigned>(*nullability) << modifiedType;
|
||||
<< static_cast<unsigned>(*nullability) << false << modifiedType;
|
||||
return QualType();
|
||||
}
|
||||
}
|
||||
|
@ -1042,7 +1042,9 @@ void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
|
||||
VisitNamedDecl(D);
|
||||
D->setAtLoc(ReadSourceLocation(Record, Idx));
|
||||
D->setLParenLoc(ReadSourceLocation(Record, Idx));
|
||||
D->setType(GetTypeSourceInfo(Record, Idx));
|
||||
QualType T = Reader.readType(F, Record, Idx);
|
||||
TypeSourceInfo *TSI = GetTypeSourceInfo(Record, Idx);
|
||||
D->setType(T, TSI);
|
||||
// FIXME: stable encoding
|
||||
D->setPropertyAttributes(
|
||||
(ObjCPropertyDecl::PropertyAttributeKind)Record[Idx++]);
|
||||
|
@ -542,7 +542,7 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) {
|
||||
|
||||
// FIXME: stable encoding for @required/@optional
|
||||
Record.push_back(D->getImplementationControl());
|
||||
// FIXME: stable encoding for in/out/inout/bycopy/byref/oneway
|
||||
// FIXME: stable encoding for in/out/inout/bycopy/byref/oneway/nullability
|
||||
Record.push_back(D->getObjCDeclQualifier());
|
||||
Record.push_back(D->hasRelatedResultType());
|
||||
Writer.AddTypeRef(D->getReturnType(), Record);
|
||||
@ -678,6 +678,7 @@ void ASTDeclWriter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
|
||||
VisitNamedDecl(D);
|
||||
Writer.AddSourceLocation(D->getAtLoc(), Record);
|
||||
Writer.AddSourceLocation(D->getLParenLoc(), Record);
|
||||
Writer.AddTypeRef(D->getType(), Record);
|
||||
Writer.AddTypeSourceInfo(D->getTypeSourceInfo(), Record);
|
||||
// FIXME: stable encoding
|
||||
Record.push_back((unsigned)D->getPropertyAttributes());
|
||||
|
@ -20,19 +20,19 @@
|
||||
* \param[in] range output value is unsigned int
|
||||
* \result return index
|
||||
*/
|
||||
- (unsigned int)MethodMyProto:(id)anObject inRange:(unsigned int)range;
|
||||
- (unsigned int)MethodMyProto:(nullable id)anObject inRange:(unsigned int)range;
|
||||
/**
|
||||
* \brief PropertyMyProto - This is protocol's property.
|
||||
*/
|
||||
@property (copy) id PropertyMyProto;
|
||||
@property (copy, nonnull) id PropertyMyProto;
|
||||
/**
|
||||
* \brief ClassMethodMyProto
|
||||
*/
|
||||
+ ClassMethodMyProto;
|
||||
@end
|
||||
// CHECK: <Declaration>@protocol MyProto\n@end</Declaration>
|
||||
// CHECK: <Declaration>- (unsigned int)MethodMyProto:(id)anObject inRange:(unsigned int)range;</Declaration>
|
||||
// CHECK: <Declaration>@optional\n@property(readwrite, copy, atomic) id PropertyMyProto;</Declaration>
|
||||
// CHECK: <Declaration>- (unsigned int)MethodMyProto:(nullable id)anObject inRange:(unsigned int)range;</Declaration>
|
||||
// CHECK: <Declaration>@optional\n@property(readwrite, copy, atomic, nonnull) id PropertyMyProto;</Declaration>
|
||||
// CHECK: <Declaration>+ (id)ClassMethodMyProto;</Declaration>
|
||||
|
||||
/**
|
||||
|
@ -79,3 +79,11 @@
|
||||
@property (readwrite) id frr;
|
||||
@end
|
||||
|
||||
// rdar://20152386
|
||||
@interface NSObject @end
|
||||
|
||||
@interface rdar20152386_2: NSObject
|
||||
@property(nonatomic, weak, nonnull) id delegate; // expected-error {{property attributes 'nonnull' and 'weak' are mutually exclusive}}
|
||||
@property(nonatomic, weak, nonnull, readonly) id ReadDelegate; // no warning
|
||||
@end
|
||||
|
||||
|
@ -56,7 +56,7 @@ __attribute__((objc_arc_weak_reference_unavailable))
|
||||
@interface I
|
||||
{
|
||||
}
|
||||
@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}}
|
||||
@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont * __nullable', which does not support weak references}}
|
||||
@end
|
||||
|
||||
@implementation I // expected-note {{when implemented by class I}}
|
||||
@ -65,7 +65,7 @@ __attribute__((objc_arc_weak_reference_unavailable))
|
||||
|
||||
// rdar://13676793
|
||||
@protocol MyProtocol
|
||||
@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}}
|
||||
@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont * __nullable', which does not support weak references}}
|
||||
@end
|
||||
|
||||
@interface I1 <MyProtocol>
|
||||
@ -76,7 +76,7 @@ __attribute__((objc_arc_weak_reference_unavailable))
|
||||
@end
|
||||
|
||||
@interface Super
|
||||
@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}}
|
||||
@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont * __nullable', which does not support weak references}}
|
||||
@end
|
||||
|
||||
|
||||
|
@ -20,9 +20,6 @@ typedef __nonnull NSFoo * __nullable conflict_NSFoo_ptr_2; // expected-error{{'_
|
||||
void testBlocksPrinting(NSFoo * __nullable (^bp)(int)) {
|
||||
int *ip = bp; // expected-error{{'NSFoo * __nullable (^)(int)'}}
|
||||
}
|
||||
void test_accepts_nonnull_null_pointer_literal(NSFoo *foo) {
|
||||
[foo methodTakingIntPtr: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
|
||||
}
|
||||
|
||||
// Check returning nil from a __nonnull-returning method.
|
||||
@implementation NSFoo
|
||||
@ -31,3 +28,151 @@ void test_accepts_nonnull_null_pointer_literal(NSFoo *foo) {
|
||||
return 0; // no warning
|
||||
}
|
||||
@end
|
||||
|
||||
// Context-sensitive keywords and property attributes for nullability.
|
||||
__attribute__((objc_root_class))
|
||||
@interface NSBar
|
||||
- (nonnull NSFoo *)methodWithFoo:(nonnull NSFoo *)foo;
|
||||
|
||||
- (nonnull NSFoo **)invalidMethod1; // expected-error{{nullability keyword 'nonnull' cannot be applied to multi-level pointer type 'NSFoo **'}}
|
||||
// expected-note@-1{{use nullability type specifier '__nonnull' to affect the innermost pointer type of 'NSFoo **'}}
|
||||
- (nonnull NSFoo * __nullable)conflictingMethod1; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
|
||||
- (nonnull NSFoo * __nonnull)redundantMethod1; // expected-warning{{duplicate nullability specifier '__nonnull'}}
|
||||
|
||||
@property(nonnull,retain) NSFoo *property1;
|
||||
@property(nullable,assign) NSFoo ** invalidProperty1; // expected-error{{nullability keyword 'nullable' cannot be applied to multi-level pointer type 'NSFoo **'}}
|
||||
// expected-note@-1{{use nullability type specifier '__nullable' to affect the innermost pointer type of 'NSFoo **'}}
|
||||
@property(null_unspecified,retain) NSFoo * __nullable conflictingProperty1; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__null_unspecified'}}
|
||||
@property(retain,nonnull) NSFoo * __nonnull redundantProperty1; // expected-warning{{duplicate nullability specifier '__nonnull'}}
|
||||
|
||||
@property(null_unspecified,retain,nullable) NSFoo *conflictingProperty3; // expected-error{{nullability specifier 'nullable' conflicts with existing specifier 'null_unspecified'}}
|
||||
@property(nullable,retain,nullable) NSFoo *redundantProperty3; // expected-warning{{duplicate nullability specifier 'nullable'}}
|
||||
@end
|
||||
|
||||
@interface NSBar ()
|
||||
@property(nonnull,retain) NSFoo *property2;
|
||||
@property(nullable,assign) NSFoo ** invalidProperty2; // expected-error{{nullability keyword 'nullable' cannot be applied to multi-level pointer type 'NSFoo **'}}
|
||||
// expected-note@-1{{use nullability type specifier '__nullable' to affect the innermost pointer type of 'NSFoo **'}}
|
||||
@property(null_unspecified,retain) NSFoo * __nullable conflictingProperty2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__null_unspecified'}}
|
||||
@property(retain,nonnull) NSFoo * __nonnull redundantProperty2; // expected-warning{{duplicate nullability specifier '__nonnull'}}
|
||||
@end
|
||||
|
||||
void test_accepts_nonnull_null_pointer_literal(NSFoo *foo, __nonnull NSBar *bar) {
|
||||
[foo methodTakingIntPtr: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
|
||||
[bar methodWithFoo: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
|
||||
bar.property1 = 0; // expected-warning{{null passed to a callee that requires a non-null argument}}
|
||||
bar.property2 = 0; // expected-warning{{null passed to a callee that requires a non-null argument}}
|
||||
[bar setProperty1: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
|
||||
[bar setProperty2: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
|
||||
int *ptr = bar.property1; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'NSFoo * __nonnull'}}
|
||||
}
|
||||
|
||||
// Check returning nil from a nonnull-returning method.
|
||||
@implementation NSBar
|
||||
- (nonnull NSFoo *)methodWithFoo:(nonnull NSFoo *)foo {
|
||||
return 0; // no warning
|
||||
}
|
||||
|
||||
- (NSFoo **)invalidMethod1 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (NSFoo *)conflictingMethod1 {
|
||||
return 0; // no warning
|
||||
}
|
||||
- (NSFoo *)redundantMethod1 {
|
||||
int *ip = 0;
|
||||
return ip; // expected-warning{{result type 'NSFoo * __nonnull'}}
|
||||
}
|
||||
@end
|
||||
|
||||
__attribute__((objc_root_class))
|
||||
@interface NSMerge
|
||||
- (nonnull NSFoo *)methodA:(nonnull NSFoo*)foo;
|
||||
- (nonnull NSFoo *)methodB:(nonnull NSFoo*)foo;
|
||||
- (NSFoo *)methodC:(NSFoo*)foo;
|
||||
@end
|
||||
|
||||
@implementation NSMerge
|
||||
- (NSFoo *)methodA:(NSFoo*)foo {
|
||||
int *ptr = foo; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'NSFoo * __nonnull'}}
|
||||
return ptr; // expected-warning{{result type 'NSFoo * __nonnull'}}
|
||||
}
|
||||
|
||||
- (nullable NSFoo *)methodB:(null_unspecified NSFoo*)foo { // expected-error{{nullability specifier 'nullable' conflicts with existing specifier 'nonnull'}} \
|
||||
// expected-error{{nullability specifier 'null_unspecified' conflicts with existing specifier 'nonnull'}}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (nonnull NSFoo *)methodC:(nullable NSFoo*)foo {
|
||||
int *ip = 0;
|
||||
return ip; // expected-warning{{result type 'NSFoo * __nonnull'}}
|
||||
}
|
||||
@end
|
||||
|
||||
// Checking merging of nullability when sending a message.
|
||||
@interface NSMergeReceiver
|
||||
- (id)returnsNone;
|
||||
- (nonnull id)returnsNonNull;
|
||||
- (nullable id)returnsNullable;
|
||||
- (null_unspecified id)returnsNullUnspecified;
|
||||
@end
|
||||
|
||||
void test_receiver_merge(NSMergeReceiver *none,
|
||||
__nonnull NSMergeReceiver *nonnull,
|
||||
__nullable NSMergeReceiver *nullable,
|
||||
__null_unspecified NSMergeReceiver *null_unspecified) {
|
||||
int *ptr;
|
||||
|
||||
ptr = [nullable returnsNullable]; // expected-warning{{'id __nullable'}}
|
||||
ptr = [nullable returnsNullUnspecified]; // expected-warning{{'id __nullable'}}
|
||||
ptr = [nullable returnsNonNull]; // expected-warning{{'id __nullable'}}
|
||||
ptr = [nullable returnsNone]; // expected-warning{{'id __nullable'}}
|
||||
|
||||
ptr = [null_unspecified returnsNullable]; // expected-warning{{'id __nullable'}}
|
||||
ptr = [null_unspecified returnsNullUnspecified]; // expected-warning{{'id __null_unspecified'}}
|
||||
ptr = [null_unspecified returnsNonNull]; // expected-warning{{'id __null_unspecified'}}
|
||||
ptr = [null_unspecified returnsNone]; // expected-warning{{'id'}}
|
||||
|
||||
ptr = [nonnull returnsNullable]; // expected-warning{{'id __nullable'}}
|
||||
ptr = [nonnull returnsNullUnspecified]; // expected-warning{{'id __null_unspecified'}}
|
||||
ptr = [nonnull returnsNonNull]; // expected-warning{{'id __nonnull'}}
|
||||
ptr = [nonnull returnsNone]; // expected-warning{{'id'}}
|
||||
|
||||
ptr = [none returnsNullable]; // expected-warning{{'id __nullable'}}
|
||||
ptr = [none returnsNullUnspecified]; // expected-warning{{'id'}}
|
||||
ptr = [none returnsNonNull]; // expected-warning{{'id'}}
|
||||
ptr = [none returnsNone]; // expected-warning{{'id'}}
|
||||
|
||||
}
|
||||
|
||||
// instancetype
|
||||
@protocol Initializable
|
||||
- (instancetype)initWithBlah:(id)blah;
|
||||
@end
|
||||
|
||||
__attribute__((objc_root_class))
|
||||
@interface InitializableClass <Initializable>
|
||||
- (nonnull instancetype)initWithBlah:(nonnull id)blah;
|
||||
- (nullable instancetype)returnMe;
|
||||
+ (nullable instancetype)returnInstanceOfMe;
|
||||
@end
|
||||
|
||||
void test_instancetype(InitializableClass * __nonnull ic, id __nonnull object) {
|
||||
int *ip = [ic returnMe]; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'InitializableClass * __nullable'}}
|
||||
ip = [InitializableClass returnMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'id __nullable'}}
|
||||
ip = [InitializableClass returnInstanceOfMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'InitializableClass * __nullable'}}
|
||||
ip = [object returnMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'id __nullable'}}
|
||||
}
|
||||
// rdar://problem/19814852
|
||||
@interface MultiProp
|
||||
@property (nullable, copy) id a, b, c;
|
||||
@property (nullable, copy) MultiProp *d, *(^e)(int);
|
||||
@end
|
||||
|
||||
void testMultiProp(MultiProp *foo) {
|
||||
int *ip;
|
||||
ip = foo.a; // expected-warning{{from 'id __nullable'}}
|
||||
ip = foo.d; // expected-warning{{from 'MultiProp * __nullable'}}
|
||||
ip = foo.e; // expected-error{{incompatible type 'MultiProp *(^ __nullable)(int)'}}
|
||||
}
|
||||
|
18
clang/test/SemaObjC/nullable-weak-property.m
Normal file
18
clang/test/SemaObjC/nullable-weak-property.m
Normal file
@ -0,0 +1,18 @@
|
||||
// RUN: %clang_cc1 -fobjc-arc -fobjc-runtime-has-weak -Wnullable-to-nonnull-conversion %s -verify
|
||||
|
||||
|
||||
// rdar://19985330
|
||||
@interface NSObject @end
|
||||
|
||||
@class NSFoo;
|
||||
void foo (NSFoo * __nonnull);
|
||||
|
||||
@interface NSBar : NSObject
|
||||
@property(weak) NSFoo *property1;
|
||||
@end
|
||||
|
||||
@implementation NSBar
|
||||
- (void) Meth {
|
||||
foo (self.property1); // expected-warning {{implicit conversion from nullable pointer 'NSFoo * __nullable' to non-nullable pointer type 'NSFoo * __nonnull'}}
|
||||
}
|
||||
@end
|
15
clang/test/SemaObjC/override-nullability.m
Normal file
15
clang/test/SemaObjC/override-nullability.m
Normal file
@ -0,0 +1,15 @@
|
||||
// RUN: %clang_cc1 -fobjc-arc -fobjc-runtime-has-weak -Wnonnull %s -verify
|
||||
//rdar://19211059
|
||||
|
||||
@interface NSObject @end
|
||||
|
||||
@interface Base : NSObject
|
||||
- (nonnull id)bad:(nullable id)obj; // expected-note 2 {{previous declaration is here}}
|
||||
- (nullable id)notAsBad:(nonnull id)obj;
|
||||
@end
|
||||
|
||||
@interface Sub : Base
|
||||
- (nullable id)bad:(nonnull id)obj; // expected-warning {{conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'}} \
|
||||
// expected-warning {{conflicting nullability specifier on parameter types, 'nonnull' conflicts with existing specifier 'nullable'}}
|
||||
- (nonnull id)notAsBad:(nullable id)obj;
|
||||
@end
|
Loading…
x
Reference in New Issue
Block a user