From: Douglas Gregor Date: Fri, 19 Jun 2015 18:27:45 +0000 (+0000) Subject: Check for consistent use of nullability type specifiers in a header. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d442f4a26c67e5688e6cd309be4326c8ce38ed89;p=clang Check for consistent use of nullability type specifiers in a header. Adds a new warning (under -Wnullability-completeness) that complains about pointer, block pointer, or member pointer declarations that have not been annotated with nullability information (directly or inferred) within a header that contains some nullability annotations. This is intended to be used to help maintain the completeness of nullability information within a header that has already been audited. Note that, for performance reasons, this warning will underrepresent the number of non-annotated pointers in the case where more than one pointer is seen before the first nullability type specifier, because we're only tracking one piece of information per header. Part of rdar://problem/18868820. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@240158 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 296b5f05d8..85796cc0d3 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -251,6 +251,7 @@ def NewlineEOF : DiagGroup<"newline-eof">; def Nullability : DiagGroup<"nullability">; def NullabilityDeclSpec : DiagGroup<"nullability-declspec">; def NullableToNonNullConversion : DiagGroup<"nullable-to-nonnull-conversion">; +def NullabilityCompleteness : DiagGroup<"nullability-completeness">; 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 c86342b659..18fe09a98b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7718,6 +7718,11 @@ def warn_null_resettable_setter : Warning< "synthesized setter %0 for null_resettable property %1 does not handle nil">, InGroup; +def warn_nullability_missing : Warning< + "%select{pointer|block pointer|member pointer}0 is missing a nullability " + "type specifier (__nonnull, __nullable, or __null_unspecified)">, + InGroup; + } } // end of sema component. diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index b55f38a4e6..4d18633cd3 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -81,6 +81,8 @@ public: AS_Declspec, /// __ptr16, alignas(...), etc. AS_Keyword, + /// Context-sensitive version of a keyword attribute. + AS_ContextSensitiveKeyword, /// #pragma ... AS_Pragma }; @@ -120,9 +122,6 @@ 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 @@ -223,8 +222,7 @@ private: ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), - HasParsedType(false), IsContextSensitiveKeyword(false), - NextInPosition(nullptr), NextInPool(nullptr) { + HasParsedType(false), NextInPosition(nullptr), NextInPool(nullptr) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -242,8 +240,8 @@ private: ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - IsContextSensitiveKeyword(false), UnavailableLoc(unavailable), - MessageExpr(messageExpr), NextInPosition(nullptr), NextInPool(nullptr) { + UnavailableLoc(unavailable), MessageExpr(messageExpr), + NextInPosition(nullptr), NextInPool(nullptr) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); @@ -263,8 +261,7 @@ private: ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(3), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - IsContextSensitiveKeyword(false), NextInPosition(nullptr), - NextInPool(nullptr) { + NextInPosition(nullptr), NextInPool(nullptr) { ArgsVector Args; Args.push_back(Parm1); Args.push_back(Parm2); @@ -282,8 +279,7 @@ private: ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), - IsContextSensitiveKeyword(false), NextInPosition(nullptr), - NextInPool(nullptr) { + NextInPosition(nullptr), NextInPool(nullptr) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -301,8 +297,7 @@ private: ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), - IsContextSensitiveKeyword(false), NextInPosition(nullptr), - NextInPool(nullptr) { + NextInPosition(nullptr), NextInPool(nullptr) { new (&getTypeBuffer()) ParsedType(typeArg); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -316,8 +311,7 @@ private: ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), - IsContextSensitiveKeyword(false), NextInPosition(nullptr), - NextInPool(nullptr) { + NextInPosition(nullptr), NextInPool(nullptr) { new (&getPropertyDataBuffer()) PropertyData(getterId, setterId); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -351,22 +345,19 @@ public: bool isAlignasAttribute() const { // FIXME: Use a better mechanism to determine this. - return getKind() == AT_Aligned && SyntaxUsed == AS_Keyword; + return getKind() == AT_Aligned && isKeywordAttribute(); } bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; } bool isCXX11Attribute() const { return SyntaxUsed == AS_CXX11 || isAlignasAttribute(); } - bool isKeywordAttribute() const { return SyntaxUsed == AS_Keyword; } - - bool isContextSensitiveKeywordAttribute() const { - return IsContextSensitiveKeyword; + bool isKeywordAttribute() const { + return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword; } - void setContextSensitiveKeywordAttribute() { - assert(SyntaxUsed == AS_Keyword); - IsContextSensitiveKeyword = true; + bool isContextSensitiveKeywordAttribute() const { + return SyntaxUsed == AS_ContextSensitiveKeyword; } bool isInvalid() const { return Invalid; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index e54cc7ade3..cb75b969f1 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -210,6 +210,50 @@ namespace threadSafety { typedef std::pair, SourceLocation> UnexpandedParameterPack; +/// Describes whether we've seen any nullability information for the given +/// file. +struct FileNullability { + /// The first pointer declarator (of any pointer kind) in the file that does + /// not have a corresponding nullability annotation. + SourceLocation PointerLoc; + + /// Which kind of pointer declarator we saw. + uint8_t PointerKind; + + /// Whether we saw any type nullability annotations in the given file. + bool SawTypeNullability = false; +}; + +/// A mapping from file IDs to a record of whether we've seen nullability +/// information in that file. +class FileNullabilityMap { + /// A mapping from file IDs to the nullability information for each file ID. + llvm::DenseMap Map; + + /// A single-element cache based on the file ID. + struct { + FileID File; + FileNullability Nullability; + } Cache; + +public: + FileNullability &operator[](FileID file) { + // Check the single-element cache. + if (file == Cache.File) + return Cache.Nullability; + + // It's not in the single-element cache; flush the cache if we have one. + if (!Cache.File.isInvalid()) { + Map[Cache.File] = Cache.Nullability; + } + + // Pull this entry into the cache. + Cache.File = file; + Cache.Nullability = Map[file]; + return Cache.Nullability; + } +}; + /// Sema - This implements semantic analysis and AST building for C. class Sema { Sema(const Sema &) = delete; @@ -341,6 +385,9 @@ public: PragmaStack ConstSegStack; PragmaStack CodeSegStack; + /// A mapping that describes the nullability we've seen in each header file. + FileNullabilityMap NullabilityMap; + /// Last section used with #pragma init_seg. StringLiteral *CurInitSeg; SourceLocation CurInitSegLoc; diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index a3cf4831e4..de347181bf 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -317,14 +317,12 @@ static void addContextSensitiveTypeNullability(Parser &P, 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; + return D.getAttributePool().create( + P.getNullabilityKeyword(nullability), + SourceRange(nullabilityLoc), + nullptr, SourceLocation(), + nullptr, 0, + AttributeList::AS_ContextSensitiveKeyword); }; if (D.getNumTypeObjects() > 0) { diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 979491af5c..234e72244f 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2759,6 +2759,71 @@ static PointerDeclaratorKind classifyPointerDeclarator(Sema &S, } } +static FileID getNullabilityCompletenessCheckFileID(Sema &S, + SourceLocation loc) { + // If we're anywhere in a function, method, or closure context, don't perform + // completeness checks. + for (DeclContext *ctx = S.CurContext; ctx; ctx = ctx->getParent()) { + if (ctx->isFunctionOrMethod()) + return FileID(); + + if (ctx->isFileContext()) + break; + } + + // We only care about the expansion location. + loc = S.SourceMgr.getExpansionLoc(loc); + FileID file = S.SourceMgr.getFileID(loc); + if (file.isInvalid()) + return FileID(); + + // Retrieve file information. + bool invalid = false; + const SrcMgr::SLocEntry &sloc = S.SourceMgr.getSLocEntry(file, &invalid); + if (invalid || !sloc.isFile()) + return FileID(); + + // We don't want to perform completeness checks on the main file or in + // system headers. + const SrcMgr::FileInfo &fileInfo = sloc.getFile(); + if (fileInfo.getIncludeLoc().isInvalid() || + fileInfo.getFileCharacteristic() != SrcMgr::C_User) + return FileID(); + + return file; +} + +/// Check for consistent use of nullability. +static void checkNullabilityConsistency(TypeProcessingState &state, + 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()) + return; + + // If we haven't seen any type nullability in this file, we won't warn now + // about anything. + FileNullability &fileNullability = S.NullabilityMap[file]; + if (!fileNullability.SawTypeNullability) { + // If this is the first pointer declarator in the file, record it. + if (fileNullability.PointerLoc.isInvalid() && + !S.Context.getDiagnostics().isIgnored(diag::warn_nullability_missing, + pointerLoc)) { + fileNullability.PointerLoc = pointerLoc; + fileNullability.PointerKind = static_cast(pointerKind); + } + + return; + } + + // Complain about missing nullability. + S.Diag(pointerLoc, diag::warn_nullability_missing) + << static_cast(pointerKind); +} + static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -2836,6 +2901,22 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, !state.getDeclarator().isObjCWeakProperty() && !S.deduceWeakPropertyFromType(T)) { inAssumeNonNullRegion = true; + // Determine which file we saw the assume-nonnull region in. + FileID file = getNullabilityCompletenessCheckFileID( + S, S.PP.getPragmaAssumeNonNullLoc()); + if (!file.isInvalid()) { + 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) + << fileNullability.PointerKind; + } + + fileNullability.SawTypeNullability = true; + } + } } // Whether to complain about missing nullability specifiers or not. @@ -2857,7 +2938,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // inner pointers. complainAboutMissingNullability = CAMN_InnerPointers; - if (T->canHaveNullability()) { + if (T->canHaveNullability() && !T->getNullability(S.Context)) { ++NumPointersRemaining; } @@ -2967,27 +3048,42 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // If we're supposed to infer nullability, do so now. if (inferNullability) { + auto syntax = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword + : AttributeList::AS_Keyword; AttributeList *nullabilityAttr = state.getDeclarator().getAttributePool() .create( S.getNullabilityKeyword( *inferNullability), SourceRange(pointerLoc), nullptr, SourceLocation(), - nullptr, 0, - AttributeList::AS_Keyword); - if (inferNullabilityCS) - nullabilityAttr->setContextSensitiveKeywordAttribute(); + nullptr, 0, syntax); spliceAttrIntoList(*nullabilityAttr, attrs); return nullabilityAttr; } + // If we're supposed to complain about missing nullability, do so + // now if it's truly missing. + switch (complainAboutMissingNullability) { + case CAMN_No: + break; + + case CAMN_InnerPointers: + if (NumPointersRemaining == 0) + break; + // Fallthrough. + + case CAMN_Yes: + checkNullabilityConsistency(state, pointerKind, pointerLoc); + } + return nullptr; }; // If the type itself could have nullability but does not, infer pointer - // nullability. - if (T->canHaveNullability() && S.ActiveTemplateInstantiations.empty()) { + // 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; @@ -4919,10 +5015,27 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State, return false; } -bool Sema::checkNullabilityTypeSpecifier(QualType &type, +bool Sema::checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability, SourceLocation nullabilityLoc, bool isContextSensitive) { + // 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) + << fileNullability.PointerKind; + } + + fileNullability.SawTypeNullability = true; + } + } + // Check for existing nullability attributes on the type. QualType desugared = type; while (auto attributed = dyn_cast(desugared.getTypePtr())) { diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-1.h b/test/SemaObjCXX/Inputs/nullability-consistency-1.h new file mode 100644 index 0000000000..4d6bf79f9a --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-1.h @@ -0,0 +1,17 @@ +void f1(int *ptr); // expected-warning{{pointer is missing a nullability type specifier}} + +void f2(int * __nonnull); + +#include "nullability-consistency-2.h" + +void f3(int *ptr) { // expected-warning{{pointer is missing a nullability type specifier}} + int *other = ptr; // shouldn't warn +} + +class X { + void mf(int *ptr); // expected-warning{{pointer is missing a nullability type specifier}} + int X:: *memptr; // expected-warning{{member pointer is missing a nullability type specifier}} +}; + + + diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-2.h b/test/SemaObjCXX/Inputs/nullability-consistency-2.h new file mode 100644 index 0000000000..8efdfa8394 --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-2.h @@ -0,0 +1,16 @@ +void g1(int * __nonnull); + +void g2(int (^block)(int, int)); // expected-warning{{block pointer is missing a nullability type specifier}} + +void g3(const + id // expected-warning{{missing a nullability type specifier}} + volatile + * // expected-warning{{missing a nullability type specifier}} + ); + +@interface SomeClass +@property (retain,nonnull) id property1; +@property (retain,nullable) SomeClass *property2; +- (nullable SomeClass *)method1; +- (void)method2:(nonnull SomeClass *)param; +@end diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-3.h b/test/SemaObjCXX/Inputs/nullability-consistency-3.h new file mode 100644 index 0000000000..a0c0d381bb --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-3.h @@ -0,0 +1 @@ +void double_declarator1(int *__nonnull *); // expected-warning{{pointer is missing a nullability type specifier (__nonnull, __nullable, or __null_unspecified)}} diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-4.h b/test/SemaObjCXX/Inputs/nullability-consistency-4.h new file mode 100644 index 0000000000..984280c17e --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-4.h @@ -0,0 +1 @@ +void double_declarator1(int * * __nonnull); // expected-warning{{pointer is missing a nullability type specifier (__nonnull, __nullable, or __null_unspecified)}} diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-5.h b/test/SemaObjCXX/Inputs/nullability-consistency-5.h new file mode 100644 index 0000000000..3a685af614 --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-5.h @@ -0,0 +1,14 @@ +#define SUPPRESS_NULLABILITY_WARNING(Type) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wnullability-completeness\"") \ + Type \ + _Pragma("clang diagnostic pop") + +void suppress1(SUPPRESS_NULLABILITY_WARNING(int *) ptr); // no warning + +void shouldwarn5(int *ptr); //expected-warning{{missing a nullability type specifier}} + +void trigger5(int * __nonnull); + +void suppress2(SUPPRESS_NULLABILITY_WARNING(int *) ptr); // no warning + diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-6.h b/test/SemaObjCXX/Inputs/nullability-consistency-6.h new file mode 100644 index 0000000000..cb712e94c6 --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-6.h @@ -0,0 +1,8 @@ +int *ptr; // expected-warning {{missing a nullability type specifier}} + +#pragma clang assume_nonnull begin + +extern void **blah; // expected-warning 2{{missing a nullability type specifier}} + +#pragma clang assume_nonnull end + diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-7.h b/test/SemaObjCXX/Inputs/nullability-consistency-7.h new file mode 100644 index 0000000000..ddbdfada79 --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-7.h @@ -0,0 +1,40 @@ +#ifndef SOMEKIT_H +#define SOMEKIT_H + +__attribute__((objc_root_class)) +#ifndef NS_ASSUME_NONNULL_BEGIN +#if __has_feature(assume_nonnull) +#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") +#else +#define NS_ASSUME_NONNULL_BEGIN +#define NS_ASSUME_NONNULL_END +#endif +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface A +-(null_unspecified A*)transform:(null_unspecified A*)input __attribute__((unavailable("anything but this"))); +-(A*)transform:(A*)input integer:(int)integer; + +@property (null_unspecified, nonatomic, readonly, retain) A* someA; +@property (null_unspecified, nonatomic, retain) A* someOtherA; + +@property (nonatomic) int intValue __attribute__((unavailable("wouldn't work anyway"))); +@end + +NS_ASSUME_NONNULL_END + + +__attribute__((unavailable("just don't"))) +@interface B : A +@end + +@interface C : A +- (instancetype)init; // expected-warning{{pointer is missing a nullability type specifier}} +- (instancetype)initWithA:( A*)a __attribute__((objc_designated_initializer)); // expected-warning 2{{pointer is missing a nullability type specifier}} +@end + +#endif + diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-8.h b/test/SemaObjCXX/Inputs/nullability-consistency-8.h new file mode 100644 index 0000000000..e7cf4b3ee8 --- /dev/null +++ b/test/SemaObjCXX/Inputs/nullability-consistency-8.h @@ -0,0 +1,11 @@ +typedef int* __nonnull mynonnull; + +__attribute__((objc_root_class)) +@interface typedefClass +- (void) func1:(mynonnull)i; +@end + +void func2(mynonnull i); + +void func3(int *); // expected-warning{{pointer is missing a nullability type specifier}} + diff --git a/test/SemaObjCXX/Inputs/nullability-pragmas-1.h b/test/SemaObjCXX/Inputs/nullability-pragmas-1.h index 76f9af4e4e..9501116924 100644 --- a/test/SemaObjCXX/Inputs/nullability-pragmas-1.h +++ b/test/SemaObjCXX/Inputs/nullability-pragmas-1.h @@ -8,7 +8,7 @@ __attribute__((objc_root_class)) struct X { }; -void f1(int *x); +void f1(int *x); // expected-warning{{pointer is missing a nullability type specifier}} typedef struct __attribute__((objc_bridge(NSError))) __CFError *CFErrorRef; typedef NSError *NSErrorPtr; @@ -38,14 +38,16 @@ A *f14(void); int * __null_unspecified f15(void); A * __null_unspecified f16(void); void f17(CFErrorRef *error); // expected-note{{no known conversion from 'A * __nonnull' to 'CFErrorRef __nullable * __nullable' (aka '__CFError **') for 1st argument}} -void f18(A **); -void f19(CFErrorRefPtr error); +void f18(A **); // expected-warning 2{{pointer is missing a nullability type specifier}} +void f19(CFErrorRefPtr error); // expected-warning{{pointer is missing a nullability type specifier}} void g1(int (^)(int, int)); -void g2(int (^ *bp)(int, int)); -void g3(block_ptr *bp); +void g2(int (^ *bp)(int, int)); // expected-warning{{block pointer is missing a nullability type specifier}} +// expected-warning@-1{{pointer is missing a nullability type specifier}} +void g3(block_ptr *bp); // expected-warning{{block pointer is missing a nullability type specifier}} +// expected-warning@-1{{pointer is missing a nullability type specifier}} void g4(int (*fp)(int, int)); -void g5(int (**fp)(int, int)); +void g5(int (**fp)(int, int)); // expected-warning 2{{pointer is missing a nullability type specifier}} @interface A(Pragmas1) + (instancetype)aWithA:(A *)a; @@ -54,9 +56,10 @@ void g5(int (**fp)(int, int)); - (void)method3:(NSError **)error; // expected-note{{passing argument to parameter 'error' here}} - (void)method4:(NSErrorPtr *)error; // expected-note{{passing argument to parameter 'error' here}} - (void)method5:(NSErrorPtrPtr)error; +// expected-warning@-1{{pointer is missing a nullability type specifier}} @property A *aProp; -@property NSError **anError; +@property NSError **anError; // expected-warning 2{{pointer is missing a nullability type specifier}} @end int *global_int_ptr; @@ -64,7 +67,7 @@ int *global_int_ptr; // typedefs not inferred __nonnull typedef int *int_ptr_2; -typedef int * +typedef int * // expected-warning{{pointer is missing a nullability type specifier}} *int_ptr_ptr; static inline void f30(void) { @@ -86,13 +89,13 @@ static inline void f30(void) { #pragma clang assume_nonnull end -void f20(A *a); -void f21(int_ptr x); -void f22(A_ptr y); +void f20(A *a); // expected-warning{{pointer is missing a nullability type specifier}} +void f21(int_ptr x); // expected-warning{{pointer is missing a nullability type specifier}} +void f22(A_ptr y); // expected-warning{{pointer is missing a nullability type specifier}} void f23(int_ptr __nullable x); void f24(A_ptr __nullable y); -void f25(int_ptr_2 x); +void f25(int_ptr_2 x); // expected-warning{{pointer is missing a nullability type specifier}} @interface A(OutsidePragmas1) -+ (instancetype)aWithInt:(int)value; ++ (instancetype)aWithInt:(int)value; // expected-warning{{pointer is missing a nullability type specifier}} @end diff --git a/test/SemaObjCXX/nullability-consistency.mm b/test/SemaObjCXX/nullability-consistency.mm new file mode 100644 index 0000000000..7497e84a36 --- /dev/null +++ b/test/SemaObjCXX/nullability-consistency.mm @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs %s -verify + +#include "nullability-consistency-1.h" +#include "nullability-consistency-3.h" +#include "nullability-consistency-4.h" +#include "nullability-consistency-5.h" +#include "nullability-consistency-5.h" +#include "nullability-consistency-6.h" +#include "nullability-consistency-7.h" +#include "nullability-consistency-8.h" + +void h1(int *ptr) { } // don't warn + +void h2(int * __nonnull) { } diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 11a766c7a4..f79c4a5f92 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -2721,7 +2721,8 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { StringMatcher("Name", Declspec, OS).Emit(); OS << " } else if (AttributeList::AS_CXX11 == Syntax) {\n"; StringMatcher("Name", CXX11, OS).Emit(); - OS << " } else if (AttributeList::AS_Keyword == Syntax) {\n"; + OS << " } else if (AttributeList::AS_Keyword == Syntax || "; + OS << "AttributeList::AS_ContextSensitiveKeyword == Syntax) {\n"; StringMatcher("Name", Keywords, OS).Emit(); OS << " } else if (AttributeList::AS_Pragma == Syntax) {\n"; StringMatcher("Name", Pragma, OS).Emit();