Merge remote-tracking branch 'origin/swift-3.1-branch' into stable
* origin/swift-3.1-branch:
Don't require nullability on 'va_list', even when it's a pointer.
Don't require nullability on 'va_list' harder.
[APINotes] Add support for nullability on arrays.
Don't require nullability on 'va_list'.
Warn when 'assume_nonnull' infers nullability within an array.
Add -Wnullability-completeness-on-arrays.
Accept nullability qualifiers on array parameters.
Don't require nullability on template parameters in typedefs.
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index 732df76..2ad440d 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -2263,19 +2263,15 @@
/// Represents a pointer type decayed from an array or function type.
class DecayedType : public AdjustedType {
- DecayedType(QualType OriginalType, QualType DecayedPtr, QualType CanonicalPtr)
- : AdjustedType(Decayed, OriginalType, DecayedPtr, CanonicalPtr) {
- assert(isa<PointerType>(getAdjustedType()));
- }
+ inline
+ DecayedType(QualType OriginalType, QualType Decayed, QualType Canonical);
friend class ASTContext; // ASTContext creates these.
public:
QualType getDecayedType() const { return getAdjustedType(); }
- QualType getPointeeType() const {
- return cast<PointerType>(getDecayedType())->getPointeeType();
- }
+ inline QualType getPointeeType() const;
static bool classof(const Type *T) { return T->getTypeClass() == Decayed; }
};
@@ -5947,6 +5943,23 @@
return cast<ArrayType>(getUnqualifiedDesugaredType());
}
+DecayedType::DecayedType(QualType OriginalType, QualType DecayedPtr,
+ QualType CanonicalPtr)
+ : AdjustedType(Decayed, OriginalType, DecayedPtr, CanonicalPtr) {
+#ifndef NDEBUG
+ QualType Adjusted = getAdjustedType();
+ (void)AttributedType::stripOuterNullability(Adjusted);
+ assert(isa<PointerType>(Adjusted));
+#endif
+}
+
+QualType DecayedType::getPointeeType() const {
+ QualType Decayed = getDecayedType();
+ (void)AttributedType::stripOuterNullability(Decayed);
+ return cast<PointerType>(Decayed)->getPointeeType();
+}
+
+
} // end namespace clang
#endif
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index 5d664fc..0c9192d 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -279,8 +279,11 @@
def NewlineEOF : DiagGroup<"newline-eof">;
def Nullability : DiagGroup<"nullability">;
def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
+def NullabilityInferredOnNestedType : DiagGroup<"nullability-inferred-on-nested-type">;
def NullableToNonNullConversion : DiagGroup<"nullable-to-nonnull-conversion">;
-def NullabilityCompleteness : DiagGroup<"nullability-completeness">;
+def NullabilityCompletenessOnArrays : DiagGroup<"nullability-completeness-on-arrays">;
+def NullabilityCompleteness : DiagGroup<"nullability-completeness",
+ [NullabilityCompletenessOnArrays]>;
def NullArithmetic : DiagGroup<"null-arithmetic">;
def NullCharacter : DiagGroup<"null-character">;
def NullDereference : DiagGroup<"null-dereference">;
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index cc5e9fb..d5e3aa9 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8707,6 +8707,15 @@
"%select{pointer|block pointer|member pointer}0 is missing a nullability "
"type specifier (_Nonnull, _Nullable, or _Null_unspecified)">,
InGroup<NullabilityCompleteness>;
+def warn_nullability_missing_array : Warning<
+ "array parameter is missing a nullability type specifier (_Nonnull, "
+ "_Nullable, or _Null_unspecified)">,
+ InGroup<NullabilityCompletenessOnArrays>;
+
+def warn_nullability_inferred_on_nested_type : Warning<
+ "inferring '_Nonnull' for pointer type within %select{array|reference}0 is "
+ "deprecated">,
+ InGroup<NullabilityInferredOnNestedType>;
def err_objc_type_arg_explicit_nullability : Error<
"type argument %0 cannot explicitly specify nullability">;
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 87dffa6..e652afb 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -3106,6 +3106,9 @@
/// method) or an Objective-C property attribute, rather than as an
/// underscored type specifier.
///
+ /// \param allowArrayTypes Whether to accept nullability specifiers on an
+ /// array type (e.g., because it will decay to a pointer).
+ ///
/// \param overrideExisting Whether to override an existing, locally-specified
/// nullability specifier rather than complaining about the conflict.
///
@@ -3113,6 +3116,7 @@
bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool isContextSensitive,
+ bool allowArrayTypes,
bool implicit,
bool overrideExisting = false);
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 7e3bca4..f75adef 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -4771,7 +4771,15 @@
QualType PtrTy = getPointerType(PrettyArrayType->getElementType());
// int x[restrict 4] -> int *restrict
- return getQualifiedType(PtrTy, PrettyArrayType->getIndexTypeQualifiers());
+ QualType Result = getQualifiedType(PtrTy,
+ PrettyArrayType->getIndexTypeQualifiers());
+
+ // int x[_Nullable] -> int * _Nullable
+ if (auto Nullability = Ty->getNullability(*this)) {
+ Result = const_cast<ASTContext *>(this)->getAttributedType(
+ AttributedType::getNullabilityAttrKind(*Nullability), Result, Result);
+ }
+ return Result;
}
QualType ASTContext::getBaseElementType(const ArrayType *array) const {
diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp
index 9881ae2..4d478bd 100644
--- a/lib/CodeGen/CGDebugInfo.cpp
+++ b/lib/CodeGen/CGDebugInfo.cpp
@@ -2366,12 +2366,18 @@
case Type::SubstTemplateTypeParm:
T = cast<SubstTemplateTypeParmType>(T)->getReplacementType();
break;
- case Type::Auto:
+ case Type::Auto: {
QualType DT = cast<AutoType>(T)->getDeducedType();
assert(!DT.isNull() && "Undeduced types shouldn't reach here.");
T = DT;
break;
}
+ case Type::Adjusted:
+ case Type::Decayed:
+ // Decayed and adjusted types use the adjusted type in LLVM and DWARF.
+ T = cast<AdjustedType>(T)->getAdjustedType();
+ break;
+ }
assert(T != LastT && "Type unwrapping failed to unwrap!");
(void)LastT;
@@ -2490,11 +2496,6 @@
return CreateType(cast<ComplexType>(Ty));
case Type::Pointer:
return CreateType(cast<PointerType>(Ty), Unit);
- case Type::Adjusted:
- case Type::Decayed:
- // Decayed and adjusted types use the adjusted type in LLVM and DWARF.
- return CreateType(
- cast<PointerType>(cast<AdjustedType>(Ty)->getAdjustedType()), Unit);
case Type::BlockPointer:
return CreateType(cast<BlockPointerType>(Ty), Unit);
case Type::Typedef:
@@ -2530,6 +2531,8 @@
case Type::Auto:
case Type::Attributed:
+ case Type::Adjusted:
+ case Type::Decayed:
case Type::Elaborated:
case Type::Paren:
case Type::SubstTemplateTypeParm:
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index 5029935..62f3cce 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -1091,6 +1091,7 @@
.Case("enumerator_attributes", true)
.Case("generalized_swift_name", true)
.Case("nullability", true)
+ .Case("nullability_on_arrays", true)
.Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
.Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread))
.Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow))
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 82195de..da162c1 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -6275,8 +6275,7 @@
T.consumeClose();
- ParsedAttributes attrs(AttrFactory);
- MaybeParseCXX11Attributes(attrs);
+ MaybeParseCXX11Attributes(DS.getAttributes());
// Remember that we parsed a array type, and remember its features.
D.AddTypeInfo(DeclaratorChunk::getArray(DS.getTypeQualifiers(),
@@ -6284,7 +6283,7 @@
NumElements.get(),
T.getOpenLocation(),
T.getCloseLocation()),
- attrs, T.getCloseLocation());
+ DS.getAttributes(), T.getCloseLocation());
}
/// Diagnose brackets before an identifier.
diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp
index 064bbe8..cf88f73 100644
--- a/lib/Sema/SemaAPINotes.cpp
+++ b/lib/Sema/SemaAPINotes.cpp
@@ -64,7 +64,7 @@
QualType origType = type;
S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(),
/*isContextSensitive=*/false,
- /*implicit=*/true,
+ isa<ParmVarDecl>(decl), /*implicit=*/true,
overrideExisting);
if (type.getTypePtr() == origType.getTypePtr())
return;
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index f173510..3b8345f 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -3185,6 +3185,7 @@
Pointer,
BlockPointer,
MemberPointer,
+ Array,
};
} // end anonymous namespace
@@ -3246,15 +3247,27 @@
// NSError**
NSErrorPointerPointer,
};
+
+ /// Describes a declarator chunk wrapping a pointer that marks inference as
+ /// unexpected.
+ // These values must be kept in sync with diagnostics.
+ enum class PointerWrappingDeclaratorKind {
+ /// Pointer is top-level.
+ None = -1,
+ /// Pointer is an array element.
+ Array = 0,
+ /// Pointer is the referent type of a C++ reference.
+ Reference = 1
+ };
} // end anonymous namespace
/// Classify the given declarator, whose type-specified is \c type, based on
/// what kind of pointer it refers to.
///
/// This is used to determine the default nullability.
-static PointerDeclaratorKind classifyPointerDeclarator(Sema &S,
- QualType type,
- Declarator &declarator) {
+static PointerDeclaratorKind
+classifyPointerDeclarator(Sema &S, QualType type, Declarator &declarator,
+ PointerWrappingDeclaratorKind &wrappingKind) {
unsigned numNormalPointers = 0;
// For any dependent type, we consider it a non-pointer.
@@ -3266,6 +3279,10 @@
DeclaratorChunk &chunk = declarator.getTypeObject(i);
switch (chunk.Kind) {
case DeclaratorChunk::Array:
+ if (numNormalPointers == 0)
+ wrappingKind = PointerWrappingDeclaratorKind::Array;
+ break;
+
case DeclaratorChunk::Function:
case DeclaratorChunk::Pipe:
break;
@@ -3276,14 +3293,18 @@
: PointerDeclaratorKind::SingleLevelPointer;
case DeclaratorChunk::Paren:
+ break;
+
case DeclaratorChunk::Reference:
- continue;
+ if (numNormalPointers == 0)
+ wrappingKind = PointerWrappingDeclaratorKind::Reference;
+ break;
case DeclaratorChunk::Pointer:
++numNormalPointers;
if (numNormalPointers > 2)
return PointerDeclaratorKind::MultiLevelPointer;
- continue;
+ break;
}
}
@@ -3430,12 +3451,15 @@
return file;
}
-/// Check for consistent use of nullability.
-static void checkNullabilityConsistency(TypeProcessingState &state,
+/// Complains about missing nullability if the file containing \p pointerLoc
+/// has other uses of nullability (either the keywords or the \c assume_nonnull
+/// pragma).
+///
+/// If the file has \e not seen other uses of nullability, this particular
+/// pointer is saved for possible later diagnosis. See recordNullabilitySeen().
+static void checkNullabilityConsistency(Sema &S,
SimplePointerKind pointerKind,
SourceLocation pointerLoc) {
- Sema &S = state.getSema();
-
// Determine which file we're performing consistency checking for.
FileID file = getNullabilityCompletenessCheckFileID(S, pointerLoc);
if (file.isInvalid())
@@ -3445,10 +3469,16 @@
// about anything.
FileNullability &fileNullability = S.NullabilityMap[file];
if (!fileNullability.SawTypeNullability) {
- // If this is the first pointer declarator in the file, record it.
+ // If this is the first pointer declarator in the file, and the appropriate
+ // warning is on, record it in case we need to diagnose it retroactively.
+ diag::kind diagKind;
+ if (pointerKind == SimplePointerKind::Array)
+ diagKind = diag::warn_nullability_missing_array;
+ else
+ diagKind = diag::warn_nullability_missing;
+
if (fileNullability.PointerLoc.isInvalid() &&
- !S.Context.getDiagnostics().isIgnored(diag::warn_nullability_missing,
- pointerLoc)) {
+ !S.Context.getDiagnostics().isIgnored(diagKind, pointerLoc)) {
fileNullability.PointerLoc = pointerLoc;
fileNullability.PointerKind = static_cast<unsigned>(pointerKind);
}
@@ -3457,8 +3487,76 @@
}
// Complain about missing nullability.
- S.Diag(pointerLoc, diag::warn_nullability_missing)
- << static_cast<unsigned>(pointerKind);
+ if (pointerKind == SimplePointerKind::Array) {
+ S.Diag(pointerLoc, diag::warn_nullability_missing_array);
+ } else {
+ S.Diag(pointerLoc, diag::warn_nullability_missing)
+ << static_cast<unsigned>(pointerKind);
+ }
+}
+
+/// Marks that a nullability feature has been used in the file containing
+/// \p loc.
+///
+/// If this file already had pointer types in it that were missing nullability,
+/// the first such instance is retroactively diagnosed.
+///
+/// \sa checkNullabilityConsistency
+static void recordNullabilitySeen(Sema &S, SourceLocation loc) {
+ FileID file = getNullabilityCompletenessCheckFileID(S, loc);
+ if (file.isInvalid())
+ return;
+
+ FileNullability &fileNullability = S.NullabilityMap[file];
+ if (fileNullability.SawTypeNullability)
+ return;
+ fileNullability.SawTypeNullability = true;
+
+ // If we haven't seen any type nullability before, now we have. Retroactively
+ // diagnose the first unannotated pointer, if there was one.
+ if (fileNullability.PointerLoc.isInvalid())
+ return;
+
+ if (fileNullability.PointerKind ==
+ static_cast<unsigned>(SimplePointerKind::Array)) {
+ S.Diag(fileNullability.PointerLoc, diag::warn_nullability_missing_array);
+ } else {
+ S.Diag(fileNullability.PointerLoc, diag::warn_nullability_missing)
+ << fileNullability.PointerKind;
+ }
+}
+
+/// Returns true if any of the declarator chunks before \p endIndex include a
+/// level of indirection: array, pointer, reference, or pointer-to-member.
+///
+/// Because declarator chunks are stored in outer-to-inner order, testing
+/// every chunk before \p endIndex is testing all chunks that embed the current
+/// chunk as part of their type.
+///
+/// It is legal to pass the result of Declarator::getNumTypeObjects() as the
+/// end index, in which case all chunks are tested.
+static bool hasOuterPointerLikeChunk(const Declarator &D, unsigned endIndex) {
+ unsigned i = endIndex;
+ while (i != 0) {
+ // Walk outwards along the declarator chunks.
+ --i;
+ const DeclaratorChunk &DC = D.getTypeObject(i);
+ switch (DC.Kind) {
+ case DeclaratorChunk::Paren:
+ break;
+ case DeclaratorChunk::Array:
+ case DeclaratorChunk::Pointer:
+ case DeclaratorChunk::Reference:
+ case DeclaratorChunk::MemberPointer:
+ return true;
+ case DeclaratorChunk::Function:
+ case DeclaratorChunk::BlockPointer:
+ case DeclaratorChunk::Pipe:
+ // These are invalid anyway, so just ignore.
+ break;
+ }
+ }
+ return false;
}
static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
@@ -3538,24 +3636,10 @@
// Are we in an assume-nonnull region?
bool inAssumeNonNullRegion = false;
- if (S.PP.getPragmaAssumeNonNullLoc().isValid()) {
+ SourceLocation assumeNonNullLoc = S.PP.getPragmaAssumeNonNullLoc();
+ if (assumeNonNullLoc.isValid()) {
inAssumeNonNullRegion = true;
- // Determine which file we saw the assume-nonnull region in.
- FileID file = getNullabilityCompletenessCheckFileID(
- S, S.PP.getPragmaAssumeNonNullLoc());
- if (file.isValid()) {
- FileNullability &fileNullability = S.NullabilityMap[file];
-
- // If we haven't seen any type nullability before, now we have.
- if (!fileNullability.SawTypeNullability) {
- if (fileNullability.PointerLoc.isValid()) {
- S.Diag(fileNullability.PointerLoc, diag::warn_nullability_missing)
- << static_cast<unsigned>(fileNullability.PointerKind);
- }
-
- fileNullability.SawTypeNullability = true;
- }
- }
+ recordNullabilitySeen(S, assumeNonNullLoc);
}
// Whether to complain about missing nullability specifiers or not.
@@ -3570,6 +3654,7 @@
CAMN_Yes
} complainAboutMissingNullability = CAMN_No;
unsigned NumPointersRemaining = 0;
+ auto complainAboutInferringWithinChunk = PointerWrappingDeclaratorKind::None;
if (IsTypedefName) {
// For typedefs, we do not infer any nullability (the default),
@@ -3577,7 +3662,17 @@
// inner pointers.
complainAboutMissingNullability = CAMN_InnerPointers;
- if (T->canHaveNullability() && !T->getNullability(S.Context)) {
+ auto isDependentNonPointerType = [](QualType T) -> bool {
+ // Note: This is intended to be the same check as Type::canHaveNullability
+ // except with all of the ambiguous cases being treated as 'false' rather
+ // than 'true'.
+ return T->isDependentType() && !T->isAnyPointerType() &&
+ !T->isBlockPointerType() && !T->isMemberPointerType();
+ };
+
+ if (T->canHaveNullability() && !T->getNullability(S.Context) &&
+ !isDependentNonPointerType(T)) {
+ // Note that we allow but don't require nullability on dependent types.
++NumPointersRemaining;
}
@@ -3628,11 +3723,12 @@
// fallthrough
case Declarator::FileContext:
- case Declarator::KNRTypeListContext:
+ case Declarator::KNRTypeListContext: {
complainAboutMissingNullability = CAMN_Yes;
// Nullability inference depends on the type and declarator.
- switch (classifyPointerDeclarator(S, T, D)) {
+ auto wrappingKind = PointerWrappingDeclaratorKind::None;
+ switch (classifyPointerDeclarator(S, T, D, wrappingKind)) {
case PointerDeclaratorKind::NonPointer:
case PointerDeclaratorKind::MultiLevelPointer:
// Cannot infer nullability.
@@ -3641,6 +3737,7 @@
case PointerDeclaratorKind::SingleLevelPointer:
// Infer _Nonnull if we are in an assumes-nonnull region.
if (inAssumeNonNullRegion) {
+ complainAboutInferringWithinChunk = wrappingKind;
inferNullability = NullabilityKind::NonNull;
inferNullabilityCS = (context == Declarator::ObjCParameterContext ||
context == Declarator::ObjCResultContext);
@@ -3681,6 +3778,7 @@
break;
}
break;
+ }
case Declarator::ConversionIdContext:
complainAboutMissingNullability = CAMN_Yes;
@@ -3706,6 +3804,23 @@
}
}
+ // Local function that returns true if its argument looks like a va_list.
+ auto isVaList = [&S](QualType T) -> bool {
+ auto *typedefTy = T->getAs<TypedefType>();
+ if (!typedefTy)
+ return false;
+ TypedefDecl *vaListTypedef = S.Context.getBuiltinVaListDecl();
+ do {
+ if (typedefTy->getDecl() == vaListTypedef)
+ return true;
+ if (auto *name = typedefTy->getDecl()->getIdentifier())
+ if (name->isStr("va_list"))
+ return true;
+ typedefTy = typedefTy->desugar()->getAs<TypedefType>();
+ } while (typedefTy);
+ return false;
+ };
+
// Local function that checks the nullability for a given pointer declarator.
// Returns true if _Nonnull was inferred.
auto inferPointerNullability = [&](SimplePointerKind pointerKind,
@@ -3739,6 +3854,25 @@
->setObjCDeclQualifier(ObjCDeclSpec::DQ_CSNullability);
}
+ if (pointerLoc.isValid() &&
+ complainAboutInferringWithinChunk !=
+ PointerWrappingDeclaratorKind::None) {
+ SourceLocation fixItLoc = S.getLocForEndOfToken(pointerLoc);
+ StringRef insertionText = " _Nonnull ";
+ if (const char *nextChar = S.SourceMgr.getCharacterData(fixItLoc)) {
+ if (isWhitespace(*nextChar)) {
+ insertionText = insertionText.drop_back();
+ } else if (!isIdentifierBody(nextChar[0], /*allow dollar*/true) &&
+ !isIdentifierBody(nextChar[-1], /*allow dollar*/true)) {
+ insertionText = insertionText.drop_back().drop_front();
+ }
+ }
+
+ S.Diag(pointerLoc, diag::warn_nullability_inferred_on_nested_type)
+ << static_cast<int>(complainAboutInferringWithinChunk)
+ << FixItHint::CreateInsertion(fixItLoc, insertionText);
+ }
+
if (inferNullabilityInnerOnly)
inferNullabilityInnerOnlyComplete = true;
return nullabilityAttr;
@@ -3756,27 +3890,42 @@
// Fallthrough.
case CAMN_Yes:
- checkNullabilityConsistency(state, pointerKind, pointerLoc);
+ checkNullabilityConsistency(S, pointerKind, pointerLoc);
}
return nullptr;
};
// If the type itself could have nullability but does not, infer pointer
// nullability and perform consistency checking.
- if (T->canHaveNullability() && S.ActiveTemplateInstantiations.empty() &&
- !T->getNullability(S.Context)) {
- SimplePointerKind pointerKind = SimplePointerKind::Pointer;
- if (T->isBlockPointerType())
- pointerKind = SimplePointerKind::BlockPointer;
- else if (T->isMemberPointerType())
- pointerKind = SimplePointerKind::MemberPointer;
+ if (S.ActiveTemplateInstantiations.empty()) {
+ if (T->canHaveNullability() && !T->getNullability(S.Context)) {
+ if (isVaList(T)) {
+ // Record that we've seen a pointer, but do nothing else.
+ if (NumPointersRemaining > 0)
+ --NumPointersRemaining;
+ } else {
+ SimplePointerKind pointerKind = SimplePointerKind::Pointer;
+ if (T->isBlockPointerType())
+ pointerKind = SimplePointerKind::BlockPointer;
+ else if (T->isMemberPointerType())
+ pointerKind = SimplePointerKind::MemberPointer;
- if (auto *attr = inferPointerNullability(
- pointerKind, D.getDeclSpec().getTypeSpecTypeLoc(),
- D.getMutableDeclSpec().getAttributes().getListRef())) {
- T = Context.getAttributedType(
- AttributedType::getNullabilityAttrKind(*inferNullability), T, T);
- attr->setUsedAsTypeAttr();
+ if (auto *attr = inferPointerNullability(
+ pointerKind, D.getDeclSpec().getTypeSpecTypeLoc(),
+ D.getMutableDeclSpec().getAttributes().getListRef())) {
+ T = Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(*inferNullability),T,T);
+ attr->setUsedAsTypeAttr();
+ }
+ }
+ }
+
+ if (complainAboutMissingNullability == CAMN_Yes &&
+ T->isArrayType() && !T->getNullability(S.Context) && !isVaList(T) &&
+ D.isPrototypeContext() &&
+ !hasOuterPointerLikeChunk(D, D.getNumTypeObjects())) {
+ checkNullabilityConsistency(S, SimplePointerKind::Array,
+ D.getDeclSpec().getTypeSpecTypeLoc());
}
}
@@ -3902,31 +4051,13 @@
// C99 6.7.5.2p1: ... and then only in the outermost array type
// derivation.
- unsigned x = chunkIndex;
- while (x != 0) {
- // Walk outwards along the declarator chunks.
- x--;
- const DeclaratorChunk &DC = D.getTypeObject(x);
- switch (DC.Kind) {
- case DeclaratorChunk::Paren:
- continue;
- case DeclaratorChunk::Array:
- case DeclaratorChunk::Pointer:
- case DeclaratorChunk::Reference:
- case DeclaratorChunk::MemberPointer:
- S.Diag(DeclType.Loc, diag::err_array_static_not_outermost) <<
- (ASM == ArrayType::Static ? "'static'" : "type qualifier");
- if (ASM == ArrayType::Static)
- ASM = ArrayType::Normal;
- ATI.TypeQuals = 0;
- D.setInvalidType(true);
- break;
- case DeclaratorChunk::Function:
- case DeclaratorChunk::BlockPointer:
- case DeclaratorChunk::Pipe:
- // These are invalid anyway, so just ignore.
- break;
- }
+ if (hasOuterPointerLikeChunk(D, chunkIndex)) {
+ S.Diag(DeclType.Loc, diag::err_array_static_not_outermost) <<
+ (ASM == ArrayType::Static ? "'static'" : "type qualifier");
+ if (ASM == ArrayType::Static)
+ ASM = ArrayType::Normal;
+ ATI.TypeQuals = 0;
+ D.setInvalidType(true);
}
}
const AutoType *AT = T->getContainedAutoType();
@@ -3941,6 +4072,16 @@
break;
}
+ // Array parameters can be marked nullable as well, although it's not
+ // necessary if they're marked 'static'.
+ if (complainAboutMissingNullability == CAMN_Yes &&
+ !hasNullabilityAttr(DeclType.getAttrs()) &&
+ ASM != ArrayType::Static &&
+ D.isPrototypeContext() &&
+ !hasOuterPointerLikeChunk(D, chunkIndex)) {
+ checkNullabilityConsistency(S, SimplePointerKind::Array, DeclType.Loc);
+ }
+
T = S.BuildArrayType(T, ASM, ArraySize, ATI.TypeQuals,
SourceRange(DeclType.Loc, DeclType.EndLoc), Name);
break;
@@ -5808,26 +5949,11 @@
NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool isContextSensitive,
+ bool allowOnArrayType,
bool implicit,
bool overrideExisting) {
- if (!implicit) {
- // We saw a nullability type specifier. If this is the first one for
- // this file, note that.
- FileID file = getNullabilityCompletenessCheckFileID(*this, nullabilityLoc);
- if (!file.isInvalid()) {
- FileNullability &fileNullability = NullabilityMap[file];
- if (!fileNullability.SawTypeNullability) {
- // If we have already seen a pointer declarator without a nullability
- // annotation, complain about it.
- if (fileNullability.PointerLoc.isValid()) {
- Diag(fileNullability.PointerLoc, diag::warn_nullability_missing)
- << static_cast<unsigned>(fileNullability.PointerKind);
- }
-
- fileNullability.SawTypeNullability = true;
- }
- }
- }
+ if (!implicit)
+ recordNullabilitySeen(*this, nullabilityLoc);
// Check for existing nullability attributes on the type.
QualType desugared = type;
@@ -5889,7 +6015,8 @@
}
// If this definitely isn't a pointer type, reject the specifier.
- if (!desugared->canHaveNullability()) {
+ if (!desugared->canHaveNullability() &&
+ !(allowOnArrayType && desugared->isArrayType())) {
if (!implicit) {
Diag(nullabilityLoc, diag::err_nullability_nonpointer)
<< DiagNullabilityKind(nullability, isContextSensitive) << type;
@@ -5901,7 +6028,12 @@
// 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();
+ const Type *pointeeType;
+ if (desugared->isArrayType())
+ pointeeType = desugared->getArrayElementTypeNoTypeQual();
+ else
+ pointeeType = desugared->getPointeeType().getTypePtr();
+
if (pointeeType->isAnyPointerType() ||
pointeeType->isObjCObjectPointerType() ||
pointeeType->isMemberPointerType()) {
@@ -6647,13 +6779,22 @@
// don't want to distribute the nullability specifier past any
// dependent type, because that complicates the user model.
if (type->canHaveNullability() || type->isDependentType() ||
+ type->isArrayType() ||
!distributeNullabilityTypeAttr(state, type, attr)) {
+ unsigned endIndex;
+ if (TAL == TAL_DeclChunk)
+ endIndex = state.getCurrentChunkIndex();
+ else
+ endIndex = state.getDeclarator().getNumTypeObjects();
+ bool allowOnArrayType =
+ state.getDeclarator().isPrototypeContext() &&
+ !hasOuterPointerLikeChunk(state.getDeclarator(), endIndex);
if (state.getSema().checkNullabilityTypeSpecifier(
type,
mapNullabilityAttrKind(attr.getKind()),
attr.getLoc(),
attr.isContextSensitiveKeywordAttribute(),
- /*implicit=*/false)) {
+ allowOnArrayType, /*implicit=*/false)) {
attr.setInvalid();
}
diff --git a/test/APINotes/Inputs/Headers/HeaderLib.apinotes b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
index f1cd086..c822964 100644
--- a/test/APINotes/Inputs/Headers/HeaderLib.apinotes
+++ b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
@@ -10,6 +10,12 @@
- Name: do_something_with_pointers
NullabilityOfRet: O
Nullability: [ N, O ]
+ - Name: do_something_with_arrays
+ Parameters:
+ - Position: 0
+ Nullability: N
+ - Position: 1
+ Nullability: N
- Name: take_pointer_and_int
Parameters:
- Position: 0
diff --git a/test/APINotes/Inputs/Headers/HeaderLib.h b/test/APINotes/Inputs/Headers/HeaderLib.h
index ec66166..8065249 100644
--- a/test/APINotes/Inputs/Headers/HeaderLib.h
+++ b/test/APINotes/Inputs/Headers/HeaderLib.h
@@ -9,6 +9,7 @@
int unavailable_global_int;
void do_something_with_pointers(int *ptr1, int *ptr2);
+void do_something_with_arrays(int simple[], int nested[][2]);
typedef int unavailable_typedef;
struct unavailable_struct { int x, y, z; };
diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m
index 5537316..6a3034c 100644
--- a/test/APINotes/availability.m
+++ b/test/APINotes/availability.m
@@ -13,10 +13,10 @@
// expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}}
unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}}
- // expected-note@HeaderLib.h:13{{'unavailable_typedef' has been explicitly marked unavailable here}}
+ // expected-note@HeaderLib.h:14{{'unavailable_typedef' has been explicitly marked unavailable here}}
struct unavailable_struct s; // expected-error{{'unavailable_struct' is unavailable}}
- // expected-note@HeaderLib.h:14{{'unavailable_struct' has been explicitly marked unavailable here}}
+ // expected-note@HeaderLib.h:15{{'unavailable_struct' has been explicitly marked unavailable here}}
B *b = 0; // expected-error{{'B' is unavailable: just don't}}
// expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}}
diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c
index 36507f1..1fcd0ee 100644
--- a/test/APINotes/nullability.c
+++ b/test/APINotes/nullability.c
@@ -8,6 +8,11 @@
int i = 0;
do_something_with_pointers(&i, 0);
do_something_with_pointers(0, &i); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ extern void *p;
+ do_something_with_arrays(0, p); // expected-warning{{null passed to a callee that requires a non-null argument}}
+ do_something_with_arrays(p, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
take_pointer_and_int(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
float *fp = global_int; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * _Nonnull'}}
diff --git a/test/FixIt/nullability.mm b/test/FixIt/nullability.mm
new file mode 100644
index 0000000..2449066
--- /dev/null
+++ b/test/FixIt/nullability.mm
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -std=gnu++11 -verify %s
+// RUN: not %clang_cc1 -fdiagnostics-parseable-fixits -fblocks -std=gnu++11 %s 2>&1 | FileCheck %s
+
+#pragma clang assume_nonnull begin
+
+extern void *array[2]; // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:" _Nonnull "
+
+extern void* array2[2]; // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:13}:" _Nonnull"
+
+extern void *nestedArray[2][3]; // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:" _Nonnull "
+
+
+typedef const void *CFTypeRef;
+
+extern CFTypeRef typedefArray[2]; // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:" _Nonnull"
+
+
+extern void *&ref; // expected-warning {{inferring '_Nonnull' for pointer type within reference is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:"_Nonnull"
+
+extern void * &ref2; // expected-warning {{inferring '_Nonnull' for pointer type within reference is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:" _Nonnull"
+
+extern void *&&ref3; // expected-warning {{inferring '_Nonnull' for pointer type within reference is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:"_Nonnull"
+
+extern void * &&ref4; // expected-warning {{inferring '_Nonnull' for pointer type within reference is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:" _Nonnull"
+
+extern void *(&arrayRef)[2]; // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:"_Nonnull"
+
+extern void * (&arrayRef2)[2]; // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:14-[[@LINE-1]]:14}:" _Nonnull"
+
+extern CFTypeRef &typedefRef; // expected-warning {{inferring '_Nonnull' for pointer type within reference is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:" _Nonnull"
+extern CFTypeRef& typedefRef2; // expected-warning {{inferring '_Nonnull' for pointer type within reference is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:" _Nonnull "
+
+
+void arrayNameless(void *[]); // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:26-[[@LINE-1]]:26}:"_Nonnull"
+
+void arrayNameless2(void * []); // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:27-[[@LINE-1]]:27}:" _Nonnull"
+
+void arrayNamelessTypedef(CFTypeRef[]); // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:36-[[@LINE-1]]:36}:" _Nonnull "
+
+void arrayNamelessTypedef2(CFTypeRef []); // expected-warning {{inferring '_Nonnull' for pointer type within array is deprecated}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:37}:" _Nonnull"
+
+
+extern int (*pointerToArray)[2]; // no-warning
+int checkTypePTA = pointerToArray; // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type 'int (* _Nonnull)[2]'}}
+
+int **arrayOfNestedPointers[2]; // no-warning
+int checkTypeANP = arrayOfNestedPointers; // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type 'int **[2]'}}
+
+CFTypeRef *arrayOfNestedPointersTypedef[2]; // no-warning
+int checkTypeANPT = arrayOfNestedPointersTypedef; // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type 'CFTypeRef *[2]'}}
+
+#pragma clang assume_nonnull end
diff --git a/test/Parser/nullability.c b/test/Parser/nullability.c
index 7117bce..d70087d 100644
--- a/test/Parser/nullability.c
+++ b/test/Parser/nullability.c
@@ -11,6 +11,14 @@
# error Nullability should always be supported
#endif
+#if !__has_feature(nullability_on_arrays)
+# error Nullability on array parameters should always be supported
+#endif
+
#if !__has_extension(nullability)
# error Nullability should always be supported as an extension
#endif
+
+#if !__has_extension(nullability_on_arrays)
+# error Nullability on array parameters should always be supported as an extension
+#endif
diff --git a/test/Sema/nullability.c b/test/Sema/nullability.c
index 9d3145d..a0247e5 100644
--- a/test/Sema/nullability.c
+++ b/test/Sema/nullability.c
@@ -195,3 +195,55 @@
p = noneP ?: unspecifiedP;
p = noneP ?: noneP;
}
+
+extern int GLOBAL_LENGTH;
+
+// Nullability can appear on arrays when the arrays are in parameter lists.
+void arrays(int ints[_Nonnull],
+ void *ptrs[_Nullable],
+ void **nestedPtrs[_Nullable],
+ void * _Null_unspecified * _Nonnull nestedPtrs2[_Nullable],
+ int fixedSize[_Nonnull 2],
+ int staticSize[_Nonnull static 2],
+ int staticSize2[static _Nonnull 2],
+ int starSize[_Nonnull *],
+ int vla[_Nonnull GLOBAL_LENGTH],
+ void ** _Nullable reference);
+void testDecayedType() {
+ int produceAnErrorMessage = arrays; // expected-warning {{incompatible pointer to integer conversion initializing 'int' with an expression of type 'void (int * _Nonnull, void ** _Nullable, void *** _Nullable, void * _Null_unspecified * _Nonnull * _Nullable, int * _Nonnull, int * _Nonnull, int * _Nonnull, int * _Nonnull, int * _Nonnull, void ** _Nullable)'}}
+}
+
+int notInFunction[_Nullable 3]; // expected-error {{nullability specifier '_Nullable' cannot be applied to non-pointer type 'int [3]'}}
+
+void nestedArrays(int x[5][_Nonnull 1]) {} // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [1]'}}
+void nestedArrays2(int x[5][_Nonnull 1][2]) {} // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [1][2]'}}
+void nestedArraysOK(int x[_Nonnull 5][1]) {} // ok
+
+void nullabilityOnBase(_Nonnull int x[1], // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int'}}
+ int _Nonnull y[1]); // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int'}}
+
+typedef int INTS[4];
+typedef int BAD_INTS[_Nonnull 4]; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [4]'}}
+
+void typedefTest(INTS _Nonnull x,
+ _Nonnull INTS xx,
+ INTS _Nonnull y[2], // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+ INTS z[_Nonnull 2]);
+
+INTS _Nonnull x; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+_Nonnull INTS x; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+
+void arraysInBlocks() {
+ typedef int INTS[4];
+ void (^simple)(int [_Nonnull 2]) = ^(int x[_Nonnull 2]) {};
+ simple(0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ void (^nested)(void *_Nullable x[_Nonnull 2]) = ^(void *_Nullable x[_Nonnull 2]) {};
+ nested(0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ void (^nestedBad)(int x[2][_Nonnull 2]) = // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+ ^(int x[2][_Nonnull 2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+
+ void (^withTypedef)(INTS _Nonnull) = ^(INTS _Nonnull x) {};
+ withTypedef(0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ void (^withTypedefBad)(INTS _Nonnull [2]) = // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+ ^(INTS _Nonnull x[2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+}
diff --git a/test/SemaCXX/nullability.cpp b/test/SemaCXX/nullability.cpp
index 2af2573..160741b 100644
--- a/test/SemaCXX/nullability.cpp
+++ b/test/SemaCXX/nullability.cpp
@@ -117,3 +117,16 @@
p = c ? nullableD : nonnullB; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}}
p = c ? nullableD : nullableB; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}}
}
+
+void arraysInLambdas() {
+ typedef int INTS[4];
+ auto simple = [](int [_Nonnull 2]) {};
+ simple(nullptr); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ auto nested = [](void *_Nullable [_Nonnull 2]) {};
+ nested(nullptr); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ auto nestedBad = [](int [2][_Nonnull 2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+
+ auto withTypedef = [](INTS _Nonnull) {};
+ withTypedef(nullptr); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ auto withTypedefBad = [](INTS _Nonnull[2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+}
diff --git a/test/SemaObjC/nullability.m b/test/SemaObjC/nullability.m
index fbf014c..cbfe132 100644
--- a/test/SemaObjC/nullability.m
+++ b/test/SemaObjC/nullability.m
@@ -256,3 +256,26 @@
p = c ? noneP : unspecifiedP;
p = c ? noneP : noneP;
}
+
+typedef int INTS[4];
+@interface ArraysInMethods
+- (void)simple:(int [_Nonnull 2])x;
+- (void)nested:(void *_Nullable [_Nonnull 2])x;
+- (void)nestedBad:(int [2][_Nonnull 2])x; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+
+- (void)withTypedef:(INTS _Nonnull)x;
+- (void)withTypedefBad:(INTS _Nonnull[2])x; // expected-error{{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+
+- (void)simpleSugar:(nonnull int [2])x;
+- (void)nestedSugar:(nonnull void *_Nullable [2])x; // expected-error {{nullability keyword 'nonnull' cannot be applied to multi-level pointer type 'void * _Nullable [2]'}} expected-note {{use nullability type specifier '_Nonnull' to affect the innermost pointer type of 'void * _Nullable [2]'}}
+- (void)sugarWithTypedef:(nonnull INTS)x;
+@end
+
+void test(ArraysInMethods *obj) {
+ [obj simple:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+ [obj nested:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+ [obj withTypedef:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+
+ [obj simpleSugar:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+ [obj sugarWithTypedef:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+}
diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-1.h b/test/SemaObjCXX/Inputs/nullability-consistency-1.h
index 6ab48fe..a99f091 100644
--- a/test/SemaObjCXX/Inputs/nullability-consistency-1.h
+++ b/test/SemaObjCXX/Inputs/nullability-consistency-1.h
@@ -13,5 +13,13 @@
int X:: *memptr; // expected-warning{{member pointer is missing a nullability type specifier}}
};
+template <typename T>
+struct Typedefs {
+ typedef T *Base; // no-warning
+ typedef Base *type; // expected-warning{{pointer is missing a nullability type specifier}}
+};
+
+Typedefs<int> xx;
+Typedefs<void *> yy;
diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-arrays.h b/test/SemaObjCXX/Inputs/nullability-consistency-arrays.h
new file mode 100644
index 0000000..900930c
--- /dev/null
+++ b/test/SemaObjCXX/Inputs/nullability-consistency-arrays.h
@@ -0,0 +1,109 @@
+#include <stdarg.h>
+
+void firstThingInTheFileThatNeedsNullabilityIsAnArray(int ints[]);
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+
+int *secondThingInTheFileThatNeedsNullabilityIsAPointer;
+#if !ARRAYS_CHECKED
+// expected-warning@-2 {{pointer is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+
+int *_Nonnull triggerConsistencyWarnings;
+
+void test(
+ int ints[],
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+ void *ptrs[], // expected-warning {{pointer is missing a nullability type specifier}}
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+ void **nestedPtrs[]); // expected-warning 2 {{pointer is missing a nullability type specifier}}
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+
+void testArraysOK(
+ int ints[_Nonnull],
+ void *ptrs[_Nonnull], // expected-warning {{pointer is missing a nullability type specifier}}
+ void **nestedPtrs[_Nonnull]); // expected-warning 2 {{pointer is missing a nullability type specifier}}
+void testAllOK(
+ int ints[_Nonnull],
+ void * _Nullable ptrs[_Nonnull],
+ void * _Nullable * _Nullable nestedPtrs[_Nonnull]);
+
+void testVAList(va_list ok); // no warning
+
+#if __cplusplus
+// Carefully construct a test case such that if a platform's va_list is an array
+// or pointer type, it gets tested, but otherwise it does not.
+template<class T, class F>
+struct pointer_like_or { typedef F type; };
+template<class T, class F>
+struct pointer_like_or<T*, F> { typedef T *type; };
+template<class T, class F>
+struct pointer_like_or<T* const, F> { typedef T * const type; };
+template<class T, class F>
+struct pointer_like_or<T[], F> { typedef T type[]; };
+template<class T, class F, unsigned size>
+struct pointer_like_or<T[size], F> { typedef T type[size]; };
+
+void testVAListWithNullability(
+ pointer_like_or<va_list, void*>::type _Nonnull x); // no errors
+#endif
+
+void nestedArrays(int x[5][1]) {}
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+void nestedArraysOK(int x[_Nonnull 5][1]) {}
+
+#if !__cplusplus
+void staticOK(int x[static 5][1]){}
+#endif
+
+int globalArraysDoNotNeedNullability[5];
+
+typedef int INTS[4];
+
+void typedefTest(
+ INTS x,
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+ INTS _Nonnull x2,
+ _Nonnull INTS x3,
+ INTS y[2],
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+ INTS y2[_Nonnull 2]);
+
+
+#pragma clang assume_nonnull begin
+void testAssumeNonnull(
+ int ints[],
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+ void *ptrs[],
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+ void **nestedPtrs[]); // expected-warning 2 {{pointer is missing a nullability type specifier}}
+#if ARRAYS_CHECKED
+// expected-warning@-2 {{array parameter is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)}}
+#endif
+
+void testAssumeNonnullAllOK(
+ int ints[_Nonnull],
+ void * _Nullable ptrs[_Nonnull],
+ void * _Nullable * _Nullable nestedPtrs[_Nonnull]);
+void testAssumeNonnullAllOK2(
+ int ints[_Nonnull],
+ void * ptrs[_Nonnull], // backwards-compatibility
+ void * _Nullable * _Nullable nestedPtrs[_Nonnull]);
+#pragma clang assume_nonnull end
diff --git a/test/SemaObjCXX/nullability-consistency-arrays.mm b/test/SemaObjCXX/nullability-consistency-arrays.mm
new file mode 100644
index 0000000..dc17ce8
--- /dev/null
+++ b/test/SemaObjCXX/nullability-consistency-arrays.mm
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs -Wno-nullability-inferred-on-nested-type %s -D ARRAYS_CHECKED=1 -verify
+// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs -Wno-nullability-inferred-on-nested-type %s -D ARRAYS_CHECKED=0 -Wno-nullability-completeness-on-arrays -verify
+// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs -Wno-nullability-inferred-on-nested-type %s -Wno-nullability-completeness -Werror
+// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs -Wno-nullability-inferred-on-nested-type -x objective-c %s -D ARRAYS_CHECKED=1 -verify
+// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs -Wno-nullability-inferred-on-nested-type -x objective-c %s -D ARRAYS_CHECKED=0 -Wno-nullability-completeness-on-arrays -verify
+// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs -Wno-nullability-inferred-on-nested-type -x objective-c %s -Wno-nullability-completeness -Werror
+
+#include "nullability-consistency-arrays.h"
+
+void h1(int *ptr) { } // don't warn
+
+void h2(int * _Nonnull p) { }