Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -216,6 +216,10 @@ HelpText<"Warns when a nullable pointer is returned from a function that has _Nonnull return type.">, DescFile<"NullabilityChecker.cpp">; +def TrustNonnullChecker : Checker<"TrustNonnull">, + HelpText<"Trust that returns from framework methods annotated with _Nonnull are not null">, + DescFile<"NullabilityChecker.cpp">; + } // end "nullability" //===----------------------------------------------------------------------===// Index: lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -872,7 +872,7 @@ // are either item retrieval related or not interesting nullability wise. // Using this fact, to keep the code easier to read just ignore the return // value of every instance method of dictionaries. - if (M.isInstanceMessage() && Name.find("Dictionary") != StringRef::npos) { + if (M.isInstanceMessage() && Name.contains("Dictionary")) { State = State->set(ReturnRegion, Nullability::Contradicted); C.addTransition(State); @@ -880,7 +880,7 @@ } // For similar reasons ignore some methods of Cocoa arrays. StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0); - if (Name.find("Array") != StringRef::npos && + if (Name.contains("Array") && (FirstSelectorSlot == "firstObject" || FirstSelectorSlot == "lastObject")) { State = @@ -893,7 +893,7 @@ // encodings are used. Using lossless encodings is so frequent that ignoring // this class of methods reduced the emitted diagnostics by about 30% on // some projects (and all of that was false positives). - if (Name.find("String") != StringRef::npos) { + if (Name.contains("String")) { for (auto Param : M.parameters()) { if (Param->getName() == "encoding") { State = State->set(ReturnRegion, @@ -1212,6 +1212,31 @@ } } +/// Trust that APIs annotated with _Nonnull actually return an object +/// which is not null for classes in namespace NS. +class TrustNonnullChecker : public Checker { +public: + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { + auto Decl = M.getDecl(); + if (!Decl) + return; + QualType RetType = Decl->getReturnType(); + if (!RetType->isAnyPointerType()) + return; + + // Only trust annotations for system headers. + if (!M.isInSystemHeader()) + return; + + ProgramStateRef State = C.getState(); + if (getNullabilityAnnotation(RetType) == Nullability::Nonnull) + if (auto L = M.getReturnValue().getAs()) + State = State->assume(*L, /*Assumption=*/true); + + C.addTransition(State); + } +}; + #define REGISTER_CHECKER(name, trackingRequired) \ void ento::register##name##Checker(CheckerManager &mgr) { \ NullabilityChecker *checker = mgr.registerChecker(); \ @@ -1234,3 +1259,7 @@ REGISTER_CHECKER(NullableDereferenced, true) REGISTER_CHECKER(NullablePassedToNonnull, true) REGISTER_CHECKER(NullableReturnedFromNonnull, true) + + void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); + } Index: test/Analysis/Inputs/system-header-simulator-for-nullability.h =================================================================== --- test/Analysis/Inputs/system-header-simulator-for-nullability.h +++ test/Analysis/Inputs/system-header-simulator-for-nullability.h @@ -32,6 +32,7 @@ @interface NSString : NSObject - (BOOL)isEqualToString : (NSString *)aString; - (NSString *)stringByAppendingString:(NSString *)aString; ++ (_Nonnull NSString *) generateString; @end void NSSystemFunctionTakingNonnull(NSString *s); Index: test/Analysis/trustnonnullchecker_test.m =================================================================== --- /dev/null +++ test/Analysis/trustnonnullchecker_test.m @@ -0,0 +1,17 @@ +// RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-checker=core,nullability -verify %s + +#include "Inputs/system-header-simulator-for-nullability.h" + +NSString* _Nonnull trust_nonnull_framework_annotation() { + NSString* out = [NSString generateString]; + if (out) {} + return out; // no-warning +} + +NSString* _Nonnull nonnull_please_trust_me(); + +NSString* _Nonnull distrust_local_nonnull_annotation() { + NSString* out = nonnull_please_trust_me(); + if (out) {} + return out; // expected-warning{{}} +}