/// checking. Should always return true.
bool isLinkageValid() const;
+ /// Determine the nullability of the given type.
+ ///
+ /// Note that nullability is only captured as sugar within the type
+ /// system, not as part of the canonical type, so nullability will
+ /// be lost by canonicalization and desugaring.
+ Optional<NullabilityKind> getNullability(const ASTContext &context) const;
+
+ /// Determine whether the given type can have a nullability
+ /// specifier applied to it, i.e., if it is any kind of pointer type
+ /// or a dependent type that could instantiate to any kind of
+ /// pointer type.
+ bool canHaveNullability() const;
+
const char *getTypeClassName() const;
QualType getCanonicalTypeInternal() const {
attr_ptr32,
attr_ptr64,
attr_sptr,
- attr_uptr
+ attr_uptr,
+ attr_nonnull,
+ attr_nullable,
+ attr_null_unspecified,
};
private:
bool isCallingConv() const;
+ llvm::Optional<NullabilityKind> getImmediateNullability() const;
+
+ /// Retrieve the attribute kind corresponding to the given
+ /// nullability kind.
+ static Kind getNullabilityAttrKind(NullabilityKind kind) {
+ switch (kind) {
+ case NullabilityKind::NonNull:
+ return attr_nonnull;
+
+ case NullabilityKind::Nullable:
+ return attr_nullable;
+
+ case NullabilityKind::Unspecified:
+ return attr_null_unspecified;
+ }
+ }
+
+ /// Strip off the top-level nullability annotation on the given
+ /// type, if it's there.
+ ///
+ /// \param T The type to strip. If the type is exactly an
+ /// AttributedType specifying nullability (without looking through
+ /// type sugar), the nullability is returned and this type changed
+ /// to the underlying modified type.
+ ///
+ /// \returns the top-level nullability, if present.
+ static Optional<NullabilityKind> stripOuterNullability(QualType &T);
+
void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getAttrKind(), ModifiedType, EquivalentType);
}
let Documentation = [Undocumented];
}
+// Nullability type attributes.
+def TypeNonNull : TypeAttr {
+ let Spellings = [Keyword<"__nonnull">];
+ let Documentation = [Undocumented];
+}
+
+def TypeNullable : TypeAttr {
+ let Spellings = [Keyword<"__nullable">];
+ let Documentation = [Undocumented];
+}
+
+def TypeNullUnspecified : TypeAttr {
+ let Spellings = [Keyword<"__null_unspecified">];
+ let Documentation = [Undocumented];
+}
+
def AssumeAligned : InheritableAttr {
let Spellings = [GCC<"assume_aligned">];
let Subjects = SubjectList<[ObjCMethod, Function]>;
}
+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'">,
+ InGroup<Nullability>;
+
+def note_nullability_here : Note<
+ "'%select{__nonnull|__nullable|__null_unspecified}0' specified here">;
+
+}
+
// Sema && Lex
def ext_c99_longlong : Extension<
"'long long' is an extension when C99 mode is not enabled">,
def ModuleBuild : DiagGroup<"module-build">;
def ModuleConflict : DiagGroup<"module-conflict">;
def NewlineEOF : DiagGroup<"newline-eof">;
+def Nullability : DiagGroup<"nullability">;
+def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
def NullArithmetic : DiagGroup<"null-arithmetic">;
def NullCharacter : DiagGroup<"null-character">;
def NullDereference : DiagGroup<"null-dereference">;
"%select{here|for the remainder of the translation unit}1">,
InGroup<KeywordCompat>;
+def ext_nullability : Extension<
+ "type nullability specifier %0 is a Clang extension">,
+ InGroup<DiagGroup<"nullability-extension">>;
+
def error_empty_enum : Error<"use of empty enum">;
def err_invalid_sign_spec : Error<"'%0' cannot be signed or unsigned">;
def err_invalid_short_spec : Error<"'short %0' is invalid">;
} // end of instrumentation issue category
+let CategoryName = "Nullability Issue" in {
+
+def warn_duplicate_nullability : Warning<
+ "duplicate nullability specifier "
+ "'%select{__nonnull|__nullable|__null_unspecified}0'">,
+ InGroup<Nullability>;
+
+def warn_nullability_declspec : Warning<
+ "nullability specifier "
+ "'%select{__nonnull|__nullable|__null_unspecified}0' cannot be applied "
+ "to non-pointer type %1; did you mean to apply the specifier to the "
+ "%select{pointer|block pointer|member pointer|function pointer|"
+ "member function pointer}2?">,
+ InGroup<NullabilityDeclSpec>,
+ DefaultError;
+
+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'">;
+
+}
+
} // end of sema component.
#ifndef LLVM_CLANG_BASIC_SPECIFIERS_H
#define LLVM_CLANG_BASIC_SPECIFIERS_H
+#include "llvm/Support/DataTypes.h"
+
namespace clang {
/// \brief Specifies the width of a type, e.g., short, long, or long long.
enum TypeSpecifierWidth {
SD_Static, ///< Static storage duration.
SD_Dynamic ///< Dynamic storage duration.
};
+
+ /// Describes the nullability of a particular type.
+ enum class NullabilityKind : uint8_t {
+ /// Values of this type can never be null.
+ NonNull = 0,
+ /// Values of this type can be null.
+ Nullable,
+ /// Whether values of this type can be null is (explicitly)
+ /// unspecified. This captures a (fairly rare) case where we
+ /// can't conclude anything about the nullability of the type even
+ /// though it has been considered.
+ Unspecified
+ };
} // end namespace clang
#endif // LLVM_CLANG_BASIC_SPECIFIERS_H
ALIAS("__volatile" , volatile , KEYALL)
ALIAS("__volatile__" , volatile , KEYALL)
+// Type nullability.
+KEYWORD(__nonnull , KEYALL)
+KEYWORD(__nullable , KEYALL)
+KEYWORD(__null_unspecified , KEYALL)
+
// Microsoft extensions which should be disabled in strict conformance mode
KEYWORD(__ptr64 , KEYMS)
KEYWORD(__ptr32 , KEYMS)
void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
void ParseOpenCLAttributes(ParsedAttributes &attrs);
void ParseOpenCLQualifiers(ParsedAttributes &Attrs);
+ void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs);
VersionTuple ParseVersionTuple(SourceRange &Range);
void ParseAvailabilityAttribute(IdentifierInfo &Availability,
case attr_objc_gc:
case attr_objc_ownership:
case attr_noreturn:
- return false;
+ case attr_nonnull:
+ case attr_nullable:
+ case attr_null_unspecified:
+ return false;
case attr_pcs:
case attr_pcs_vfp:
case attr_cdecl:
return LV;
}
+Optional<NullabilityKind> Type::getNullability(const ASTContext &context) const {
+ QualType type(this, 0);
+ do {
+ // Check whether this is an attributed type with nullability
+ // information.
+ if (auto attributed = dyn_cast<AttributedType>(type.getTypePtr())) {
+ if (auto nullability = attributed->getImmediateNullability())
+ return nullability;
+ }
+
+ // Desugar the type. If desugaring does nothing, we're done.
+ QualType desugared = type.getSingleStepDesugaredType(context);
+ if (desugared.getTypePtr() == type.getTypePtr())
+ return None;
+
+ type = desugared;
+ } while (true);
+}
+
+bool Type::canHaveNullability() const {
+ QualType type = getCanonicalTypeInternal();
+
+ switch (type->getTypeClass()) {
+ // We'll only see canonical types here.
+#define NON_CANONICAL_TYPE(Class, Parent) \
+ case Type::Class: \
+ llvm_unreachable("non-canonical type");
+#define TYPE(Class, Parent)
+#include "clang/AST/TypeNodes.def"
+
+ // Pointer types.
+ case Type::Pointer:
+ case Type::BlockPointer:
+ case Type::MemberPointer:
+ case Type::ObjCObjectPointer:
+ return true;
+
+ // Dependent types that could instantiate to pointer types.
+ case Type::UnresolvedUsing:
+ case Type::TypeOfExpr:
+ case Type::TypeOf:
+ case Type::Decltype:
+ case Type::UnaryTransform:
+ case Type::TemplateTypeParm:
+ case Type::SubstTemplateTypeParmPack:
+ case Type::DependentName:
+ case Type::DependentTemplateSpecialization:
+ return true;
+
+ // Dependent template specializations can instantiate to pointer
+ // types unless they're known to be specializations of a class
+ // template.
+ case Type::TemplateSpecialization:
+ if (TemplateDecl *templateDecl
+ = cast<TemplateSpecializationType>(type.getTypePtr())
+ ->getTemplateName().getAsTemplateDecl()) {
+ if (isa<ClassTemplateDecl>(templateDecl))
+ return false;
+ }
+ return true;
+
+ // auto is considered dependent when it isn't deduced.
+ case Type::Auto:
+ return !cast<AutoType>(type.getTypePtr())->isDeduced();
+
+ case Type::Builtin:
+ switch (cast<BuiltinType>(type.getTypePtr())->getKind()) {
+ // Signed, unsigned, and floating-point types cannot have nullability.
+#define SIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define UNSIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define FLOATING_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define BUILTIN_TYPE(Id, SingletonId)
+#include "clang/AST/BuiltinTypes.def"
+ return false;
+
+ // Dependent types that could instantiate to a pointer type.
+ case BuiltinType::Dependent:
+ case BuiltinType::Overload:
+ case BuiltinType::BoundMember:
+ case BuiltinType::PseudoObject:
+ case BuiltinType::UnknownAny:
+ case BuiltinType::ARCUnbridgedCast:
+ return true;
+
+ case BuiltinType::Void:
+ case BuiltinType::ObjCId:
+ case BuiltinType::ObjCClass:
+ case BuiltinType::ObjCSel:
+ case BuiltinType::OCLImage1d:
+ case BuiltinType::OCLImage1dArray:
+ case BuiltinType::OCLImage1dBuffer:
+ case BuiltinType::OCLImage2d:
+ case BuiltinType::OCLImage2dArray:
+ case BuiltinType::OCLImage3d:
+ case BuiltinType::OCLSampler:
+ case BuiltinType::OCLEvent:
+ case BuiltinType::BuiltinFn:
+ case BuiltinType::NullPtr:
+ return false;
+ }
+
+ // Non-pointer types.
+ case Type::Complex:
+ case Type::LValueReference:
+ case Type::RValueReference:
+ case Type::ConstantArray:
+ case Type::IncompleteArray:
+ case Type::VariableArray:
+ case Type::DependentSizedArray:
+ case Type::DependentSizedExtVector:
+ case Type::Vector:
+ case Type::ExtVector:
+ case Type::FunctionProto:
+ case Type::FunctionNoProto:
+ case Type::Record:
+ case Type::Enum:
+ case Type::InjectedClassName:
+ case Type::PackExpansion:
+ case Type::ObjCObject:
+ case Type::ObjCInterface:
+ case Type::Atomic:
+ return false;
+ }
+}
+
+llvm::Optional<NullabilityKind> AttributedType::getImmediateNullability() const {
+ if (getAttrKind() == AttributedType::attr_nonnull)
+ return NullabilityKind::NonNull;
+ if (getAttrKind() == AttributedType::attr_nullable)
+ return NullabilityKind::Nullable;
+ if (getAttrKind() == AttributedType::attr_null_unspecified)
+ return NullabilityKind::Unspecified;
+ return None;
+}
+
+Optional<NullabilityKind> AttributedType::stripOuterNullability(QualType &T) {
+ if (auto attributed = dyn_cast<AttributedType>(T.getTypePtr())) {
+ if (auto nullability = attributed->getImmediateNullability()) {
+ T = attributed->getModifiedType();
+ return nullability;
+ }
+ }
+
+ return None;
+}
+
Qualifiers::ObjCLifetime Type::getObjCARCImplicitLifetime() const {
if (isObjCARCImplicitlyUnretainedType())
return Qualifiers::OCL_ExplicitNone;
}
spaceBeforePlaceHolder(OS);
}
+
+ // Print nullability type specifiers.
+ if (T->getAttrKind() == AttributedType::attr_nonnull ||
+ T->getAttrKind() == AttributedType::attr_nullable ||
+ T->getAttrKind() == AttributedType::attr_null_unspecified) {
+ if (T->getAttrKind() == AttributedType::attr_nonnull)
+ OS << " __nonnull";
+ else if (T->getAttrKind() == AttributedType::attr_nullable)
+ OS << " __nullable";
+ else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
+ OS << " __null_unspecified";
+ else
+ llvm_unreachable("unhandled nullability");
+ spaceBeforePlaceHolder(OS);
+ }
}
void TypePrinter::printAttributedAfter(const AttributedType *T,
if (T->isMSTypeSpec())
return;
+ // Nothing to print after.
+ if (T->getAttrKind() == AttributedType::attr_nonnull ||
+ T->getAttrKind() == AttributedType::attr_nullable ||
+ T->getAttrKind() == AttributedType::attr_null_unspecified)
+ return printAfter(T->getModifiedType(), OS);
+
// If this is a calling convention attribute, don't print the implicit CC from
// the modified type.
SaveAndRestore<bool> MaybeSuppressCC(InsideCCAttribute, T->isCallingConv());
printAfter(T->getModifiedType(), OS);
+ // Print nullability type specifiers that occur after
+ if (T->getAttrKind() == AttributedType::attr_nonnull ||
+ T->getAttrKind() == AttributedType::attr_nullable ||
+ T->getAttrKind() == AttributedType::attr_null_unspecified) {
+ if (T->getAttrKind() == AttributedType::attr_nonnull)
+ OS << " __nonnull";
+ else if (T->getAttrKind() == AttributedType::attr_nullable)
+ OS << " __nullable";
+ else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
+ OS << " __null_unspecified";
+ else
+ llvm_unreachable("unhandled nullability");
+
+ return;
+ }
+
OS << " __attribute__((";
switch (T->getAttrKind()) {
default: llvm_unreachable("This attribute should have been handled already");
.Case("cxx_exceptions", LangOpts.CXXExceptions)
.Case("cxx_rtti", LangOpts.RTTI)
.Case("enumerator_attributes", true)
+ .Case("nullability", LangOpts.ObjC1 || LangOpts.GNUMode)
.Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
.Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread))
.Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow))
// Because we inherit the feature list from HasFeature, this string switch
// must be less restrictive than HasFeature's.
return llvm::StringSwitch<bool>(Extension)
+ .Case("nullability", true)
// C11 features supported by other languages as extensions.
.Case("c_alignas", true)
.Case("c_alignof", true)
AttributeList::AS_Keyword);
}
+void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) {
+ // Treat these like attributes, even though they're type specifiers.
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified: {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ if (!getLangOpts().ObjC1)
+ Diag(AttrNameLoc, diag::ext_nullability)
+ << AttrName;
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ AttributeList::AS_Keyword);
+ break;
+ }
+ default:
+ return;
+ }
+ }
+}
+
static bool VersionNumberSeparator(const char Separator) {
return (Separator == '.' || Separator == '_');
}
ParseOpenCLAttributes(DS.getAttributes());
continue;
+ // Nullability type specifiers.
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+ ParseNullabilityTypeSpecifiers(DS.getAttributes());
+ continue;
+
// storage-class-specifier
case tok::kw_typedef:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
case tok::kw___pascal:
case tok::kw___unaligned:
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+
case tok::kw___private:
case tok::kw___local:
case tok::kw___global:
case tok::kw___pascal:
case tok::kw___unaligned:
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+
case tok::kw___private:
case tok::kw___local:
case tok::kw___global:
continue;
}
goto DoneWithTypeQuals;
+
+ // Nullability type specifiers.
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+ ParseNullabilityTypeSpecifiers(DS.getAttributes());
+ continue;
+
case tok::kw___attribute:
if (AttrReqs & AR_GNUAttributesParsedAndRejected)
// When GNU attributes are expressly forbidden, diagnose their usage.
(Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
// ptr-operator
ConsumeToken();
- while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict))
+ while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict,
+ tok::kw___nonnull, tok::kw___nullable,
+ tok::kw___null_unspecified))
ConsumeToken();
} else {
return TPResult::True;
case tok::kw___ptr32:
case tok::kw___forceinline:
case tok::kw___unaligned:
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
return TPResult::True;
// Borland
if (!foundAny) newDecl->dropAttrs();
}
+static void mergeParamDeclTypes(ParmVarDecl *NewParam,
+ const ParmVarDecl *OldParam,
+ Sema &S) {
+ if (auto Oldnullability = OldParam->getType()->getNullability(S.Context)) {
+ if (auto Newnullability = NewParam->getType()->getNullability(S.Context)) {
+ if (*Oldnullability != *Newnullability) {
+ S.Diag(NewParam->getLocation(), diag::warn_mismatched_nullability_attr)
+ << static_cast<unsigned>(*Newnullability)
+ << static_cast<unsigned>(*Oldnullability);
+ S.Diag(OldParam->getLocation(), diag::note_previous_declaration);
+ }
+ }
+ else {
+ QualType NewT = NewParam->getType();
+ NewT = S.Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(*Oldnullability),
+ NewT, NewT);
+ NewParam->setType(NewT);
+ }
+ }
+}
+
namespace {
/// Used in MergeFunctionDecl to keep track of function parameters in
// Merge attributes from the parameters. These can mismatch with K&R
// declarations.
if (New->getNumParams() == Old->getNumParams())
- for (unsigned i = 0, e = New->getNumParams(); i != e; ++i)
- mergeParamDeclAttributes(New->getParamDecl(i), Old->getParamDecl(i),
- *this);
+ for (unsigned i = 0, e = New->getNumParams(); i != e; ++i) {
+ ParmVarDecl *NewParam = New->getParamDecl(i);
+ ParmVarDecl *OldParam = Old->getParamDecl(i);
+ mergeParamDeclAttributes(NewParam, OldParam, *this);
+ mergeParamDeclTypes(NewParam, OldParam, *this);
+ }
if (getLangOpts().CPlusPlus)
return MergeCXXFunctionDecl(New, Old, S);
#include "clang/AST/Expr.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeLocVisitor.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Parse/ParseDiagnostic.h"
case AttributeList::AT_SPtr: \
case AttributeList::AT_UPtr
+// Nullability qualifiers.
+#define NULLABILITY_TYPE_ATTRS_CASELIST \
+ case AttributeList::AT_TypeNonNull: \
+ case AttributeList::AT_TypeNullable: \
+ case AttributeList::AT_TypeNullUnspecified
+
namespace {
/// An object which stores processing state for the entire
/// GetTypeForDeclarator process.
///
/// \param i - a notional index which the search will start
/// immediately inside
+///
+/// \param onlyBlockPointers Whether we should only look into block
+/// pointer types (vs. all pointer types).
static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator,
- unsigned i) {
+ unsigned i,
+ bool onlyBlockPointers) {
assert(i <= declarator.getNumTypeObjects());
DeclaratorChunk *result = nullptr;
return result;
// If we do find a function declarator, scan inwards from that,
- // looking for a block-pointer declarator.
+ // looking for a (block-)pointer declarator.
case DeclaratorChunk::Function:
for (--i; i != 0; --i) {
- DeclaratorChunk &blockChunk = declarator.getTypeObject(i-1);
- switch (blockChunk.Kind) {
+ DeclaratorChunk &ptrChunk = declarator.getTypeObject(i-1);
+ switch (ptrChunk.Kind) {
case DeclaratorChunk::Paren:
- case DeclaratorChunk::Pointer:
case DeclaratorChunk::Array:
case DeclaratorChunk::Function:
case DeclaratorChunk::Reference:
- case DeclaratorChunk::MemberPointer:
continue;
+
+ case DeclaratorChunk::MemberPointer:
+ case DeclaratorChunk::Pointer:
+ if (onlyBlockPointers)
+ continue;
+
+ // fallthrough
+
case DeclaratorChunk::BlockPointer:
- result = &blockChunk;
+ result = &ptrChunk;
goto continue_outer;
}
llvm_unreachable("bad declarator chunk kind");
DeclaratorChunk *destChunk = nullptr;
if (state.isProcessingDeclSpec() &&
attr.getKind() == AttributeList::AT_ObjCOwnership)
- destChunk = maybeMovePastReturnType(declarator, i - 1);
+ destChunk = maybeMovePastReturnType(declarator, i - 1,
+ /*onlyBlockPointers=*/true);
if (!destChunk) destChunk = &chunk;
moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
case DeclaratorChunk::Function:
if (state.isProcessingDeclSpec() &&
attr.getKind() == AttributeList::AT_ObjCOwnership) {
- if (DeclaratorChunk *dest = maybeMovePastReturnType(declarator, i)) {
+ if (DeclaratorChunk *dest = maybeMovePastReturnType(
+ declarator, i,
+ /*onlyBlockPointers=*/true)) {
moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
dest->getAttrListRef());
return;
// Microsoft type attributes cannot go after the declarator-id.
continue;
+ NULLABILITY_TYPE_ATTRS_CASELIST:
+ // Nullability specifiers cannot go after the declarator-id.
+ continue;
+
default:
break;
}
return AttributeList::AT_SPtr;
case AttributedType::attr_uptr:
return AttributeList::AT_UPtr;
+ case AttributedType::attr_nonnull:
+ return AttributeList::AT_TypeNonNull;
+ case AttributedType::attr_nullable:
+ return AttributeList::AT_TypeNullable;
+ case AttributedType::attr_null_unspecified:
+ return AttributeList::AT_TypeNullUnspecified;
}
llvm_unreachable("unexpected attribute kind!");
}
// just be the return type of a block pointer.
if (state.isProcessingDeclSpec()) {
Declarator &D = state.getDeclarator();
- if (maybeMovePastReturnType(D, D.getNumTypeObjects()))
+ if (maybeMovePastReturnType(D, D.getNumTypeObjects(),
+ /*onlyBlockPointers=*/true))
return false;
}
}
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);
+
+ // Check for existing nullability attributes on the type.
+ QualType desugared = type;
+ while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
+ // Check whether there is already a null
+ if (auto existingNullability = attributed->getImmediateNullability()) {
+ // Duplicated nullability.
+ if (nullability == *existingNullability) {
+ S.Diag(attr.getLoc(), diag::warn_duplicate_nullability)
+ << static_cast<unsigned>(nullability);
+ return true;
+ }
+
+ // Conflicting nullability.
+ S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
+ << static_cast<unsigned>(nullability)
+ << static_cast<unsigned>(*existingNullability);
+ return true;
+ }
+
+ desugared = attributed->getEquivalentType();
+ }
+
+ // If there is already a different nullability specifier, complain.
+ // This (unlike the code above) looks through typedefs that might
+ // have nullability specifiers on them, which means we cannot
+ // provide a useful Fix-It.
+ if (auto existingNullability = desugared->getNullability(Context)) {
+ if (nullability != *existingNullability) {
+ S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
+ << static_cast<unsigned>(nullability)
+ << static_cast<unsigned>(*existingNullability);
+
+ // Try to find the typedef with the existing nullability specifier.
+ if (auto typedefType = desugared->getAs<TypedefType>()) {
+ TypedefNameDecl *typedefDecl = typedefType->getDecl();
+ QualType underlyingType = typedefDecl->getUnderlyingType();
+ if (auto typedefNullability
+ = AttributedType::stripOuterNullability(underlyingType)) {
+ if (*typedefNullability == *existingNullability) {
+ S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
+ << static_cast<unsigned>(*existingNullability);
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+
+ // 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;
+ 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);
+ return false;
+}
+
+/// Check whether there is a nullability attribute of any kind in the given
+/// attribute list.
+static bool hasNullabilityAttr(const AttributeList *attrs) {
+ for (const AttributeList *attr = attrs; attr;
+ attr = attr->getNext()) {
+ if (attr->getKind() == AttributeList::AT_TypeNonNull ||
+ attr->getKind() == AttributeList::AT_TypeNullable ||
+ attr->getKind() == AttributeList::AT_TypeNullUnspecified)
+ return true;
+ }
+
+ return false;
+}
+
+/// 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.
+///
+/// \returns true if the nullability annotation was distributed, false
+/// otherwise.
+static bool distributeNullabilityTypeAttr(TypeProcessingState &state,
+ QualType type,
+ AttributeList &attr) {
+ Declarator &declarator = state.getDeclarator();
+
+ /// Attempt to move the attribute to the specified chunk.
+ auto moveToChunk = [&](DeclaratorChunk &chunk, bool inFunction) -> bool {
+ // If there is already a nullability attribute there, don't add
+ // one.
+ if (hasNullabilityAttr(chunk.getAttrListRef()))
+ return false;
+
+ // Complain about the nullability qualifier being in the wrong
+ // place.
+ unsigned pointerKind
+ = chunk.Kind == DeclaratorChunk::Pointer ? (inFunction ? 3 : 0)
+ : chunk.Kind == DeclaratorChunk::BlockPointer ? 1
+ : inFunction? 4 : 2;
+
+ auto diag = state.getSema().Diag(attr.getLoc(),
+ diag::warn_nullability_declspec)
+ << static_cast<unsigned>(mapNullabilityAttrKind(attr.getKind()))
+ << type
+ << pointerKind;
+
+ // FIXME: MemberPointer chunks don't carry the location of the *.
+ if (chunk.Kind != DeclaratorChunk::MemberPointer) {
+ diag << FixItHint::CreateRemoval(attr.getLoc())
+ << FixItHint::CreateInsertion(
+ state.getSema().getPreprocessor()
+ .getLocForEndOfToken(chunk.Loc),
+ " " + attr.getName()->getName().str() + " ");
+ }
+
+ moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
+ chunk.getAttrListRef());
+ return true;
+ };
+
+ // Move it to the outermost pointer, member pointer, or block
+ // pointer declarator.
+ for (unsigned i = state.getCurrentChunkIndex(); i != 0; --i) {
+ DeclaratorChunk &chunk = declarator.getTypeObject(i-1);
+ switch (chunk.Kind) {
+ case DeclaratorChunk::Pointer:
+ case DeclaratorChunk::BlockPointer:
+ case DeclaratorChunk::MemberPointer:
+ return moveToChunk(chunk, false);
+
+ case DeclaratorChunk::Paren:
+ case DeclaratorChunk::Array:
+ continue;
+
+ case DeclaratorChunk::Function:
+ // Try to move past the return type to a function/block/member
+ // function pointer.
+ if (DeclaratorChunk *dest = maybeMovePastReturnType(
+ declarator, i,
+ /*onlyBlockPointers=*/false)) {
+ return moveToChunk(*dest, true);
+ }
+
+ return false;
+
+ // Don't walk through these.
+ case DeclaratorChunk::Reference:
+ return false;
+ }
+ }
+
+ return false;
+}
+
static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) {
assert(!Attr.isInvalid());
switch (Attr.getKind()) {
attr.setUsedAsTypeAttr();
break;
+ NULLABILITY_TYPE_ATTRS_CASELIST:
+ // Either add nullability here or try to distribute it. We
+ // don't want to distribute the nullability specifier past any
+ // dependent type, because that complicates the user model.
+ if (type->canHaveNullability() || type->isDependentType() ||
+ !distributeNullabilityTypeAttr(state, type, attr)) {
+ if (handleNullabilityTypeAttr(state, attr, type)) {
+ attr.setInvalid();
+ }
+
+ attr.setUsedAsTypeAttr();
+ }
+ break;
+
case AttributeList::AT_NSReturnsRetained:
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
break;
= getDerived().TransformType(oldType->getEquivalentType());
if (equivalentType.isNull())
return QualType();
+
+ // Check whether we can add nullability; it is only represented as
+ // type sugar, and therefore cannot be diagnosed in any other way.
+ if (auto nullability = oldType->getImmediateNullability()) {
+ if (!modifiedType->canHaveNullability()) {
+ SemaRef.Diag(TL.getAttrNameLoc(), diag::err_nullability_nonpointer)
+ << static_cast<unsigned>(*nullability) << modifiedType;
+ return QualType();
+ }
+ }
+
result = SemaRef.Context.getAttributedType(oldType->getAttrKind(),
modifiedType,
equivalentType);
--- /dev/null
+// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -x c++ -verify %s
+
+// RUN: cp %s %t
+// RUN: not %clang_cc1 -fixit -fblocks -Werror=nullability-declspec -x c++ %t
+// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -x c++ %t
+
+__nullable int *ip1; // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the pointer?}}
+__nullable int (*fp1)(int); // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the function pointer?}}
+__nonnull int (^bp1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the block pointer?}}
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -Wno-nullability-declspec -pedantic %s -verify
+
+__nonnull int *ptr; // expected-warning{{type nullability specifier '__nonnull' is a Clang extension}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-extension"
+__nonnull int *ptr2; // no-warning
+#pragma clang diagnostic pop
+
+#if __has_feature(nullability)
+# error Nullability should not be supported in C under -pedantic -std=c99
+#endif
+
+#if !__has_extension(nullability)
+# error Nullability should always be supported as an extension
+#endif
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -Wnonnull -Wnullability %s -verify
+// rdar://19160762
+
+#if __has_feature(nullability)
+#else
+# error nullability feature should be defined
+#endif
+
+
+int * __nullable foo(int * __nonnull x);
+
+int *__nonnull ret_nonnull();
+
+int *foo(int *x) {
+ return 0;
+}
+
+int * __nullable foo1(int * __nonnull x); // expected-note {{previous declaration is here}}
+
+int *foo1(int * __nullable x) { // expected-warning {{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
+ return 0;
+}
+
+int * __nullable foo2(int * __nonnull x);
+
+int *foo2(int * __nonnull x) {
+ return 0;
+}
+
+int * __nullable foo3(int * __nullable x); // expected-note {{previous declaration is here}}
+
+int *foo3(int * __nonnull x) { // expected-warning {{nullability specifier '__nonnull' conflicts with existing specifier '__nullable'}}
+ return 0;
+}
+
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-nullability-declspec %s -verify
+
+#if __has_feature(nullability)
+#else
+# error nullability feature should be defined
+#endif
+
+typedef int * int_ptr;
+
+// Parse nullability type specifiers.
+typedef int * __nonnull nonnull_int_ptr; // expected-note{{'__nonnull' specified here}}
+typedef int * __nullable nullable_int_ptr;
+typedef int * __null_unspecified null_unspecified_int_ptr;
+
+// Redundant nullability type specifiers.
+typedef int * __nonnull __nonnull redundant_1; // expected-warning{{duplicate nullability specifier '__nonnull'}}
+
+// Conflicting nullability type specifiers.
+typedef int * __nonnull __nullable conflicting_1; // expected-error{{nullability specifier '__nonnull' conflicts with existing specifier '__nullable'}}
+typedef int * __null_unspecified __nonnull conflicting_2; // expected-error{{nullability specifier '__null_unspecified' conflicts with existing specifier '__nonnull'}}
+
+// Redundant nullability specifiers via a typedef are okay.
+typedef nonnull_int_ptr __nonnull redundant_okay_1;
+
+// Conflicting nullability specifiers via a typedef are not.
+typedef nonnull_int_ptr __nullable conflicting_2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
+typedef nonnull_int_ptr nonnull_int_ptr_typedef;
+typedef nonnull_int_ptr_typedef __nullable conflicting_2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
+typedef nonnull_int_ptr_typedef nonnull_int_ptr_typedef_typedef;
+typedef nonnull_int_ptr_typedef_typedef __null_unspecified conflicting_3; // expected-error{{nullability specifier '__null_unspecified' conflicts with existing specifier '__nonnull'}}
+
+// Nullability applies to all pointer types.
+typedef int (* __nonnull function_pointer_type_1)(int, int);
+typedef int (^ __nonnull block_type_1)(int, int);
+
+// Nullability must be on a pointer type.
+typedef int __nonnull int_type_1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
+
+// Nullability can move out to a pointer/block pointer declarator
+// (with a suppressed warning).
+typedef __nonnull int * nonnull_int_ptr_2;
+typedef int __nullable * nullable_int_ptr_2;
+typedef __nonnull int (* function_pointer_type_2)(int, int);
+typedef __nonnull int (^ block_type_2)(int, int);
+typedef __nonnull int * * __nullable nonnull_int_ptr_ptr_1;
+typedef __nonnull int *(^ block_type_3)(int, int);
+typedef __nonnull int *(* function_pointer_type_3)(int, int);
+typedef __nonnull int_ptr (^ block_type_4)(int, int);
+typedef __nonnull int_ptr (* function_pointer_type_4)(int, int);
+
+void acceptFunctionPtr(__nonnull int *(*)(void));
+void acceptBlockPtr(__nonnull int *(^)(void));
+
+void testBlockFunctionPtrNullability() {
+ float *fp;
+ fp = (function_pointer_type_3)0; // expected-warning{{from 'function_pointer_type_3' (aka 'int * __nonnull (*)(int, int)')}}
+ fp = (block_type_3)0; // expected-error{{from incompatible type 'block_type_3' (aka 'int * __nonnull (^)(int, int)')}}
+ fp = (function_pointer_type_4)0; // expected-warning{{from 'function_pointer_type_4' (aka 'int_ptr __nonnull (*)(int, int)')}}
+ fp = (block_type_4)0; // expected-error{{from incompatible type 'block_type_4' (aka 'int_ptr __nonnull (^)(int, int)')}}
+
+ acceptFunctionPtr(0); // no-warning
+ acceptBlockPtr(0); // no-warning
+}
+
+// Moving nullability where it creates a conflict.
+typedef __nonnull int * __nullable * conflict_int_ptr_ptr_2; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
+
+// Nullability is not part of the canonical type.
+typedef int * __nonnull ambiguous_int_ptr;
+typedef int * ambiguous_int_ptr;
+typedef int * __nullable ambiguous_int_ptr;
+
+// Printing of nullability.
+float f;
+int * __nonnull ip_1 = &f; // expected-warning{{incompatible pointer types initializing 'int * __nonnull' with an expression of type 'float *'}}
+
+// Check printing of nullability specifiers.
+void printing_nullability(void) {
+ int * __nonnull iptr;
+ float *fptr = iptr; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * __nonnull'}}
+
+ int * * __nonnull iptrptr;
+ float **fptrptr = iptrptr; // expected-warning{{incompatible pointer types initializing 'float **' with an expression of type 'int ** __nonnull'}}
+
+ int * __nullable * __nonnull iptrptr2;
+ float * *fptrptr2 = iptrptr2; // expected-warning{{incompatible pointer types initializing 'float **' with an expression of type 'int * __nullable * __nonnull'}}
+}
--- /dev/null
+// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -verify %s
+
+struct X { };
+
+__nullable int *ip1; // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the pointer?}}
+__nullable int (*fp1)(int); // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the function pointer?}}
+__nonnull int (^bp1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the block pointer?}}
+__nonnull int X::*pmd1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the member pointer?}}
+__nonnull int (X::*pmf1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the member function pointer?}}
--- /dev/null
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-nullability-declspec %s -verify
+
+typedef decltype(nullptr) nullptr_t;
+
+class X {
+};
+
+// Nullability applies to all pointer types.
+typedef int (X::* __nonnull member_function_type_1)(int);
+typedef int X::* __nonnull member_data_type_1;
+typedef nullptr_t __nonnull nonnull_nullptr_t; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'nullptr_t'}}
+
+// Nullability can move into member pointers (this is suppressing a warning).
+typedef __nonnull int (X::* member_function_type_2)(int);
+typedef int (X::* __nonnull member_function_type_3)(int);
+typedef __nonnull int X::* member_data_type_2;
+
+// Adding non-null via a template.
+template<typename T>
+struct AddNonNull {
+ typedef __nonnull T type; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
+ // expected-error@-1{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'nullptr_t'}}
+};
+
+typedef AddNonNull<int *>::type nonnull_int_ptr_1;
+typedef AddNonNull<int * __nullable>::type nonnull_int_ptr_2; // FIXME: check that it was overridden
+typedef AddNonNull<nullptr_t>::type nonnull_int_ptr_3; // expected-note{{in instantiation of template class}}
+
+typedef AddNonNull<int>::type nonnull_non_pointer_1; // expected-note{{in instantiation of template class 'AddNonNull<int>' requested here}}
+
+// Non-null checking within a template.
+template<typename T>
+struct AddNonNull2 {
+ typedef __nonnull AddNonNull<T> invalid1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull<T>'}}
+ typedef __nonnull AddNonNull2 invalid2; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull2<T>'}}
+ typedef __nonnull AddNonNull2<T> invalid3; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull2<T>'}}
+ typedef __nonnull typename AddNonNull<T>::type okay1;
+
+ // Don't move past a dependent type even if we know that nullability
+ // cannot apply to that specific dependent type.
+ typedef __nonnull AddNonNull<T> (*invalid4); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull<T>'}}
+};
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Woverriding-method-mismatch -Wno-nullability-declspec %s -verify
+
+@interface NSFoo
+@end
+
+// Nullability applies to all pointer types.
+typedef NSFoo * __nonnull nonnull_NSFoo_ptr;
+typedef id __nonnull nonnull_id;
+typedef SEL __nonnull nonnull_SEL;
+
+// Nullability can move into Objective-C pointer types.
+typedef __nonnull NSFoo * nonnull_NSFoo_ptr_2;
+
+// Conflicts from nullability moving into Objective-C pointer type.
+typedef __nonnull NSFoo * __nullable conflict_NSFoo_ptr_2; // expected-error{{'__nonnull' cannot be applied to non-pointer type 'NSFoo'}}
+
+void testBlocksPrinting(NSFoo * __nullable (^bp)(int)) {
+ int *ip = bp; // expected-error{{'NSFoo * __nullable (^)(int)'}}
+}