r240156 - Introduced pragmas for audited nullability regions.
Aaron Ballman
aaron at aaronballman.com
Sat Jun 20 12:24:58 PDT 2015
On Fri, Jun 19, 2015 at 2:25 PM, Douglas Gregor <dgregor at apple.com> wrote:
> Author: dgregor
> Date: Fri Jun 19 13:25:57 2015
> New Revision: 240156
>
> URL: http://llvm.org/viewvc/llvm-project?rev=240156&view=rev
> Log:
> Introduced pragmas for audited nullability regions.
>
> Introduce the clang pragmas "assume_nonnull begin" and "assume_nonnull
> end" in which we make default assumptions about the nullability of many
> unannotated pointers:
>
> - Single-level pointers are inferred to __nonnull
> - NSError** in a (function or method) parameter list is inferred to
> NSError * __nullable * __nullable.
> - CFErrorRef * in a (function or method) parameter list is inferred
> to CFErrorRef __nullable * __nullable.
> - Other multi-level pointers are never inferred to anything.
>
> Implements rdar://problem/19191042.
>
> Added:
> cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h (with props)
> cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h (with props)
> cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-3.h (with props)
> cfe/trunk/test/SemaObjCXX/nullability-pragmas.mm
> Modified:
> cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td
> cfe/trunk/include/clang/Lex/Preprocessor.h
> cfe/trunk/include/clang/Parse/Parser.h
> cfe/trunk/include/clang/Sema/DeclSpec.h
> cfe/trunk/include/clang/Sema/Sema.h
> cfe/trunk/lib/Lex/PPDirectives.cpp
> cfe/trunk/lib/Lex/PPLexerChange.cpp
> cfe/trunk/lib/Lex/PPMacroExpansion.cpp
> cfe/trunk/lib/Lex/Pragma.cpp
> cfe/trunk/lib/Parse/ParseObjc.cpp
> cfe/trunk/lib/Sema/SemaExprObjC.cpp
> cfe/trunk/lib/Sema/SemaObjCProperty.cpp
> cfe/trunk/lib/Sema/SemaType.cpp
> cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td Fri Jun 19 13:25:57 2015
> @@ -646,5 +646,20 @@ def warn_header_guard : Warning<
> "%0 is used as a header guard here, followed by #define of a different macro">,
> InGroup<DiagGroup<"header-guard">>;
> def note_header_guard : Note<
> - "%0 is defined here; did you mean %1?">;
> + "%0 is defined here; did you mean %1?">;
> +
> +let CategoryName = "Nullability Issue" in {
> +
> +def err_pp_assume_nonnull_syntax : Error<"expected 'begin' or 'end'">;
> +def err_pp_double_begin_of_assume_nonnull : Error<
> + "already inside '#pragma clang assume_nonnull'">;
> +def err_pp_unmatched_end_of_assume_nonnull : Error<
> + "not currently inside '#pragma clang assume_nonnull'">;
> +def err_pp_include_in_assume_nonnull : Error<
> + "cannot #include files inside '#pragma clang assume_nonnull'">;
> +def err_pp_eof_in_assume_nonnull : Error<
> + "'#pragma clang assume_nonnull' was not ended within this file">;
> +
> +}
> +
> }
>
> Modified: cfe/trunk/include/clang/Lex/Preprocessor.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Preprocessor.h?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Lex/Preprocessor.h (original)
> +++ cfe/trunk/include/clang/Lex/Preprocessor.h Fri Jun 19 13:25:57 2015
> @@ -256,6 +256,10 @@ class Preprocessor : public RefCountedBa
> /// \#pragma clang arc_cf_code_audited begin.
> SourceLocation PragmaARCCFCodeAuditedLoc;
>
> + /// \brief The source location of the currently-active
> + /// \#pragma clang assume_nonnull begin.
> + SourceLocation PragmaAssumeNonNullLoc;
> +
> /// \brief True if we hit the code-completion point.
> bool CodeCompletionReached;
>
> @@ -1250,6 +1254,20 @@ public:
> PragmaARCCFCodeAuditedLoc = Loc;
> }
>
> + /// \brief The location of the currently-active \#pragma clang
> + /// assume_nonnull begin.
> + ///
> + /// Returns an invalid location if there is no such pragma active.
> + SourceLocation getPragmaAssumeNonNullLoc() const {
> + return PragmaAssumeNonNullLoc;
> + }
> +
> + /// \brief Set the location of the currently-active \#pragma clang
> + /// assume_nonnull begin. An invalid location ends the pragma.
> + void setPragmaAssumeNonNullLoc(SourceLocation Loc) {
> + PragmaAssumeNonNullLoc = Loc;
> + }
> +
> /// \brief Set the directory in which the main file should be considered
> /// to have been found, if it is not a real file.
> void setMainFileDir(const DirectoryEntry *Dir) {
>
> Modified: cfe/trunk/include/clang/Parse/Parser.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Parse/Parser.h (original)
> +++ cfe/trunk/include/clang/Parse/Parser.h Fri Jun 19 13:25:57 2015
> @@ -139,11 +139,6 @@ class Parser : public CodeCompletionHand
> // used as type traits.
> llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind> RevertibleTypeTraits;
>
> - /// Nullability type specifiers.
> - IdentifierInfo *Ident___nonnull = nullptr;
> - IdentifierInfo *Ident___nullable = nullptr;
> - IdentifierInfo *Ident___null_unspecified = nullptr;
> -
> std::unique_ptr<PragmaHandler> AlignHandler;
> std::unique_ptr<PragmaHandler> GCCVisibilityHandler;
> std::unique_ptr<PragmaHandler> OptionsHandler;
> @@ -308,9 +303,11 @@ public:
> return true;
> }
>
> - /// Retrieve the underscored keyword (__nonnull, __nullable,
> - /// __null_unspecified) that corresponds to the given nullability kind.
> - IdentifierInfo *getNullabilityKeyword(NullabilityKind nullability);
> + /// Retrieve the underscored keyword (__nonnull, __nullable) that corresponds
> + /// to the given nullability kind.
> + IdentifierInfo *getNullabilityKeyword(NullabilityKind nullability) {
> + return Actions.getNullabilityKeyword(nullability);
> + }
>
> private:
> //===--------------------------------------------------------------------===//
>
> Modified: cfe/trunk/include/clang/Sema/DeclSpec.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/DeclSpec.h?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/DeclSpec.h (original)
> +++ cfe/trunk/include/clang/Sema/DeclSpec.h Fri Jun 19 13:25:57 2015
> @@ -1650,7 +1650,13 @@ private:
> bool InlineParamsUsed;
>
> /// \brief true if the declaration is preceded by \c __extension__.
> - bool Extension : 1;
> + unsigned Extension : 1;
> +
> + /// Indicates whether this is an Objective-C instance variable.
> + unsigned ObjCIvar : 1;
> +
> + /// Indicates whether this is an Objective-C 'weak' property.
> + unsigned ObjCWeakProperty : 1;
>
> /// \brief If this is the second or subsequent declarator in this declaration,
> /// the location of the comma before this declarator.
> @@ -1669,7 +1675,8 @@ public:
> GroupingParens(false), FunctionDefinition(FDK_Declaration),
> Redeclaration(false),
> Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr),
> - InlineParamsUsed(false), Extension(false) {
> + InlineParamsUsed(false), Extension(false), ObjCIvar(false),
> + ObjCWeakProperty(false) {
> }
>
> ~Declarator() {
> @@ -1747,6 +1754,8 @@ public:
> Attrs.clear();
> AsmLabel = nullptr;
> InlineParamsUsed = false;
> + ObjCIvar = false;
> + ObjCWeakProperty = false;
> CommaLoc = SourceLocation();
> EllipsisLoc = SourceLocation();
> }
> @@ -2155,6 +2164,12 @@ public:
> void setExtension(bool Val = true) { Extension = Val; }
> bool getExtension() const { return Extension; }
>
> + void setObjCIvar(bool Val = true) { ObjCIvar = Val; }
> + bool isObjCIvar() const { return ObjCIvar; }
> +
> + void setObjCWeakProperty(bool Val = true) { ObjCWeakProperty = Val; }
> + bool isObjCWeakProperty() const { return ObjCWeakProperty; }
> +
> void setInvalidType(bool Val = true) { InvalidType = Val; }
> bool isInvalidType() const {
> return InvalidType || DS.getTypeSpecType() == DeclSpec::TST_error;
>
> Modified: cfe/trunk/include/clang/Sema/Sema.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/Sema.h (original)
> +++ cfe/trunk/include/clang/Sema/Sema.h Fri Jun 19 13:25:57 2015
> @@ -1157,6 +1157,16 @@ public:
>
> bool CheckFunctionReturnType(QualType T, SourceLocation Loc);
>
> + unsigned deduceWeakPropertyFromType(QualType T) {
> + if ((getLangOpts().getGC() != LangOptions::NonGC &&
> + T.isObjCGCWeak()) ||
> + (getLangOpts().ObjCAutoRefCount &&
> + T.getObjCLifetime() == Qualifiers::OCL_Weak))
> + return ObjCDeclSpec::DQ_PR_weak;
> + return 0;
> + }
> +
> +
> /// \brief Build a function type.
> ///
> /// This routine checks the function type according to C++ rules and
> @@ -8782,6 +8792,13 @@ private:
> mutable IdentifierInfo *Ident_super;
> mutable IdentifierInfo *Ident___float128;
>
> + /// Nullability type specifiers.
> + IdentifierInfo *Ident___nonnull = nullptr;
> + IdentifierInfo *Ident___nullable = nullptr;
> + IdentifierInfo *Ident___null_unspecified = nullptr;
> +
> + IdentifierInfo *Ident_NSError = nullptr;
> +
> protected:
> friend class Parser;
> friend class InitializationSequence;
> @@ -8790,6 +8807,15 @@ protected:
> friend class ASTWriter;
>
> public:
> + /// Retrieve the keyword associated
> + IdentifierInfo *getNullabilityKeyword(NullabilityKind nullability);
> +
> + /// The struct behind the CFErrorRef pointer.
> + RecordDecl *CFError = nullptr;
> +
> + /// Retrieve the identifier "NSError".
> + IdentifierInfo *getNSErrorIdent();
> +
> /// \brief Retrieve the parser's current scope.
> ///
> /// This routine must only be used when it is certain that semantic analysis
>
> Modified: cfe/trunk/lib/Lex/PPDirectives.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPDirectives.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Lex/PPDirectives.cpp (original)
> +++ cfe/trunk/lib/Lex/PPDirectives.cpp Fri Jun 19 13:25:57 2015
> @@ -1575,6 +1575,15 @@ void Preprocessor::HandleIncludeDirectiv
> PragmaARCCFCodeAuditedLoc = SourceLocation();
> }
>
> + // Complain about attempts to #include files in an assume-nonnull pragma.
> + if (PragmaAssumeNonNullLoc.isValid()) {
> + Diag(HashLoc, diag::err_pp_include_in_assume_nonnull);
> + Diag(PragmaAssumeNonNullLoc, diag::note_pragma_entered_here);
> +
> + // Immediately leave the pragma.
> + PragmaAssumeNonNullLoc = SourceLocation();
> + }
> +
> if (HeaderInfo.HasIncludeAliasMap()) {
> // Map the filename with the brackets still attached. If the name doesn't
> // map to anything, fall back on the filename we've already gotten the
>
> Modified: cfe/trunk/lib/Lex/PPLexerChange.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPLexerChange.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Lex/PPLexerChange.cpp (original)
> +++ cfe/trunk/lib/Lex/PPLexerChange.cpp Fri Jun 19 13:25:57 2015
> @@ -355,6 +355,17 @@ bool Preprocessor::HandleEndOfFile(Token
> PragmaARCCFCodeAuditedLoc = SourceLocation();
> }
>
> + // Complain about reaching a true EOF within assume_nonnull.
> + // We don't want to complain about reaching the end of a macro
> + // instantiation or a _Pragma.
> + if (PragmaAssumeNonNullLoc.isValid() &&
> + !isEndOfMacro && !(CurLexer && CurLexer->Is_PragmaLexer)) {
> + Diag(PragmaAssumeNonNullLoc, diag::err_pp_eof_in_assume_nonnull);
> +
> + // Recover by leaving immediately.
> + PragmaAssumeNonNullLoc = SourceLocation();
> + }
> +
> // If this is a #include'd file, pop it off the include stack and continue
> // lexing the #includer file.
> if (!IncludeMacroStack.empty()) {
>
> Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original)
> +++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Fri Jun 19 13:25:57 2015
> @@ -1052,6 +1052,7 @@ static bool HasFeature(const Preprocesso
> .Case("address_sanitizer",
> LangOpts.Sanitize.hasOneOf(SanitizerKind::Address |
> SanitizerKind::KernelAddress))
> + .Case("assume_nonnull", LangOpts.ObjC1 || LangOpts.GNUMode)
> .Case("attribute_analyzer_noreturn", true)
> .Case("attribute_availability", true)
> .Case("attribute_availability_with_message", true)
>
> Modified: cfe/trunk/lib/Lex/Pragma.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/Pragma.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Lex/Pragma.cpp (original)
> +++ cfe/trunk/lib/Lex/Pragma.cpp Fri Jun 19 13:25:57 2015
> @@ -1342,6 +1342,60 @@ struct PragmaARCCFCodeAuditedHandler : p
> }
> };
>
> +/// PragmaAssumeNonNullHandler -
> +/// \#pragma clang assume_nonnull begin/end
> +struct PragmaAssumeNonNullHandler : public PragmaHandler {
> + PragmaAssumeNonNullHandler() : PragmaHandler("assume_nonnull") {}
> + void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
> + Token &NameTok) override {
> + SourceLocation Loc = NameTok.getLocation();
> + bool IsBegin;
> +
> + Token Tok;
> +
> + // Lex the 'begin' or 'end'.
> + PP.LexUnexpandedToken(Tok);
> + const IdentifierInfo *BeginEnd = Tok.getIdentifierInfo();
> + if (BeginEnd && BeginEnd->isStr("begin")) {
> + IsBegin = true;
Elide braces (here and elsewhere).
> + } else if (BeginEnd && BeginEnd->isStr("end")) {
> + IsBegin = false;
> + } else {
> + PP.Diag(Tok.getLocation(), diag::err_pp_assume_nonnull_syntax);
> + return;
> + }
> +
> + // Verify that this is followed by EOD.
> + PP.LexUnexpandedToken(Tok);
> + if (Tok.isNot(tok::eod))
> + PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
> +
> + // The start location of the active audit.
> + SourceLocation BeginLoc = PP.getPragmaAssumeNonNullLoc();
> +
> + // The start location we want after processing this.
> + SourceLocation NewLoc;
> +
> + if (IsBegin) {
> + // Complain about attempts to re-enter an audit.
> + if (BeginLoc.isValid()) {
> + PP.Diag(Loc, diag::err_pp_double_begin_of_assume_nonnull);
> + PP.Diag(BeginLoc, diag::note_pragma_entered_here);
> + }
> + NewLoc = Loc;
> + } else {
> + // Complain about attempts to leave an audit that doesn't exist.
> + if (!BeginLoc.isValid()) {
> + PP.Diag(Loc, diag::err_pp_unmatched_end_of_assume_nonnull);
> + return;
> + }
> + NewLoc = SourceLocation();
> + }
> +
> + PP.setPragmaAssumeNonNullLoc(NewLoc);
> + }
> +};
> +
> /// \brief Handle "\#pragma region [...]"
> ///
> /// The syntax is
> @@ -1393,6 +1447,7 @@ void Preprocessor::RegisterBuiltinPragma
> AddPragmaHandler("clang", new PragmaDependencyHandler());
> AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang"));
> AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler());
> + AddPragmaHandler("clang", new PragmaAssumeNonNullHandler());
>
> AddPragmaHandler("STDC", new PragmaSTDC_FENV_ACCESSHandler());
> AddPragmaHandler("STDC", new PragmaSTDC_CX_LIMITED_RANGEHandler());
>
> Modified: cfe/trunk/lib/Parse/ParseObjc.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseObjc.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Parse/ParseObjc.cpp (original)
> +++ cfe/trunk/lib/Parse/ParseObjc.cpp Fri Jun 19 13:25:57 2015
> @@ -308,25 +308,6 @@ Decl *Parser::ParseObjCAtInterfaceDeclar
> return ClsType;
> }
>
> -IdentifierInfo *Parser::getNullabilityKeyword(NullabilityKind nullability) {
> - switch (nullability) {
> - case NullabilityKind::NonNull:
> - if (!Ident___nonnull)
> - Ident___nonnull = PP.getIdentifierInfo("__nonnull");
> - return Ident___nonnull;
> -
> - case NullabilityKind::Nullable:
> - if (!Ident___nullable)
> - Ident___nullable = PP.getIdentifierInfo("__nullable");
> - return Ident___nullable;
> -
> - case NullabilityKind::Unspecified:
> - if (!Ident___null_unspecified)
> - Ident___null_unspecified = PP.getIdentifierInfo("__null_unspecified");
> - return Ident___null_unspecified;
> - }
> -}
> -
> /// Add an attribute for a context-sensitive type nullability to the given
> /// declarator.
> static void addContextSensitiveTypeNullability(Parser &P,
> @@ -1063,31 +1044,28 @@ ParsedType Parser::ParseObjCTypeName(Obj
> SourceLocation loc = ConsumeToken();
> Ty = Actions.ActOnObjCInstanceType(loc);
>
> - // Map a nullability specifier to a context-sensitive keyword attribute.
> - if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability) {
> - // Synthesize an abstract declarator so we can use Sema::ActOnTypeName.
> - bool addedToDeclSpec = false;
> - const char *prevSpec;
> - unsigned diagID;
> - DeclSpec declSpec(AttrFactory);
> - declSpec.setObjCQualifiers(&DS);
> - declSpec.SetTypeSpecType(DeclSpec::TST_typename, loc, prevSpec, diagID,
> - Ty,
> - Actions.getASTContext().getPrintingPolicy());
> - declSpec.SetRangeEnd(loc);
> - Declarator declarator(declSpec, context);
> + // Synthesize an abstract declarator so we can use Sema::ActOnTypeName.
> + bool addedToDeclSpec = false;
> + const char *prevSpec;
> + unsigned diagID;
> + DeclSpec declSpec(AttrFactory);
> + declSpec.setObjCQualifiers(&DS);
> + declSpec.SetTypeSpecType(DeclSpec::TST_typename, loc, prevSpec, diagID,
> + Ty,
> + Actions.getASTContext().getPrintingPolicy());
> + declSpec.SetRangeEnd(loc);
> + Declarator declarator(declSpec, context);
>
> - // Add the context-sensitive keyword attribute.
> + // Map a nullability specifier to a context-sensitive keyword attribute.
> + if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability)
> addContextSensitiveTypeNullability(*this, declarator,
> DS.getNullability(),
> DS.getNullabilityLoc(),
> addedToDeclSpec);
>
> -
> - TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
> - if (!type.isInvalid())
> - Ty = type.get();
> - }
> + TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
> + if (!type.isInvalid())
> + Ty = type.get();
> }
> }
>
> @@ -1491,6 +1469,7 @@ void Parser::ParseObjCClassInstanceVaria
> auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
> Actions.ActOnObjCContainerStartDefinition(interfaceDecl);
> // Install the declarator into the interface decl.
> + FD.D.setObjCIvar(true);
> Decl *Field = Actions.ActOnIvar(
> getCurScope(), FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D,
> FD.BitfieldSize, visibility);
>
> Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Fri Jun 19 13:25:57 2015
> @@ -1231,6 +1231,10 @@ QualType Sema::getMessageSendResultType(
> isClassMessage,
> isSuperMessage);
>
> + // If this is a class message, ignore the nullability of the receiver.
> + if (isClassMessage)
> + return resultType;
> +
> // Map the nullability of the result into a table index.
> unsigned receiverNullabilityIdx = 0;
> if (auto nullability = ReceiverType->getNullability(Context))
>
> Modified: cfe/trunk/lib/Sema/SemaObjCProperty.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaObjCProperty.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaObjCProperty.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaObjCProperty.cpp Fri Jun 19 13:25:57 2015
> @@ -103,15 +103,6 @@ static void checkARCPropertyDecl(Sema &S
> << propertyLifetime;
> }
>
> -static unsigned deduceWeakPropertyFromType(Sema &S, QualType T) {
> - if ((S.getLangOpts().getGC() != LangOptions::NonGC &&
> - T.isObjCGCWeak()) ||
> - (S.getLangOpts().ObjCAutoRefCount &&
> - T.getObjCLifetime() == Qualifiers::OCL_Weak))
> - return ObjCDeclSpec::DQ_PR_weak;
> - return 0;
> -}
> -
> /// \brief Check this Objective-C property against a property declared in the
> /// given protocol.
> static void
> @@ -146,9 +137,10 @@ Decl *Sema::ActOnProperty(Scope *S, Sour
> tok::ObjCKeywordKind MethodImplKind,
> DeclContext *lexicalDC) {
> unsigned Attributes = ODS.getPropertyAttributes();
> + FD.D.setObjCWeakProperty((Attributes & ObjCDeclSpec::DQ_PR_weak) != 0);
> TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S);
> QualType T = TSI->getType();
> - Attributes |= deduceWeakPropertyFromType(*this, T);
> + Attributes |= deduceWeakPropertyFromType(T);
> bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) ||
> // default is readwrite!
> !(Attributes & ObjCDeclSpec::DQ_PR_readonly));
> @@ -433,7 +425,7 @@ Sema::HandlePropertyInClassExtension(Sco
> if (isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly)) {
> PIkind &= ~ObjCPropertyDecl::OBJC_PR_readonly;
> PIkind |= ObjCPropertyDecl::OBJC_PR_readwrite;
> - PIkind |= deduceWeakPropertyFromType(*this, PIDecl->getType());
> + PIkind |= deduceWeakPropertyFromType(PIDecl->getType());
> unsigned ClassExtensionMemoryModel = getOwnershipRule(Attributes);
> unsigned PrimaryClassMemoryModel = getOwnershipRule(PIkind);
> if (PrimaryClassMemoryModel && ClassExtensionMemoryModel &&
> @@ -2293,8 +2285,7 @@ void Sema::CheckObjCPropertyAttributes(D
> Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
> }
>
> - if ((Attributes & ObjCDeclSpec::DQ_PR_weak) &&
> - !(Attributes & ObjCDeclSpec::DQ_PR_readonly)) {
> + if (Attributes & ObjCDeclSpec::DQ_PR_weak) {
> // 'weak' and 'nonnull' are mutually exclusive.
> if (auto nullability = PropertyTy->getNullability(Context)) {
> if (*nullability == NullabilityKind::NonNull)
>
> Modified: cfe/trunk/lib/Sema/SemaType.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaType.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaType.cpp Fri Jun 19 13:25:57 2015
> @@ -25,6 +25,7 @@
> #include "clang/Lex/Preprocessor.h"
> #include "clang/Basic/PartialDiagnostic.h"
> #include "clang/Basic/TargetInfo.h"
> +#include "clang/Lex/Preprocessor.h"
> #include "clang/Parse/ParseDiagnostic.h"
> #include "clang/Sema/DeclSpec.h"
> #include "clang/Sema/DelayedDiagnostic.h"
> @@ -2553,6 +2554,211 @@ getCCForDeclaratorChunk(Sema &S, Declara
> return CC;
> }
>
> +namespace {
> + /// A simple notion of pointer kinds, which matches up with the various
> + /// pointer declarators.
> + enum class SimplePointerKind {
> + Pointer,
> + BlockPointer,
> + MemberPointer,
> + };
> +}
> +
> +IdentifierInfo *Sema::getNullabilityKeyword(NullabilityKind nullability) {
> + switch (nullability) {
> + case NullabilityKind::NonNull:
> + if (!Ident___nonnull)
> + Ident___nonnull = PP.getIdentifierInfo("__nonnull");
> + return Ident___nonnull;
> +
> + case NullabilityKind::Nullable:
> + if (!Ident___nullable)
> + Ident___nullable = PP.getIdentifierInfo("__nullable");
> + return Ident___nullable;
> +
> + case NullabilityKind::Unspecified:
> + if (!Ident___null_unspecified)
> + Ident___null_unspecified = PP.getIdentifierInfo("__null_unspecified");
> + return Ident___null_unspecified;
> + }
> +}
> +
> +/// Retrieve the identifier "NSError".
> +IdentifierInfo *Sema::getNSErrorIdent() {
> + if (!Ident_NSError)
> + Ident_NSError = PP.getIdentifierInfo("NSError");
> +
> + return Ident_NSError;
> +}
> +
> +/// 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;
> +}
> +
> +namespace {
> + /// Describes the kind of a pointer a declarator describes.
> + enum class PointerDeclaratorKind {
> + // Not a pointer.
> + NonPointer,
> + // Single-level pointer.
> + SingleLevelPointer,
> + // Multi-level pointer (of any pointer kind).
> + MultiLevelPointer,
> + // CFErrorRef*
> + CFErrorRefPointer,
> + // NSError**
> + NSErrorPointerPointer,
> + };
> +}
> +
> +/// 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) {
> + unsigned numNormalPointers = 0;
> +
> + // For any dependent type, we consider it a non-pointer.
> + if (type->isDependentType())
> + return PointerDeclaratorKind::NonPointer;
> +
> + // Look through the declarator chunks to identify pointers.
> + for (unsigned i = 0, n = declarator.getNumTypeObjects(); i != n; ++i) {
> + DeclaratorChunk &chunk = declarator.getTypeObject(i);
> + switch (chunk.Kind) {
> + case DeclaratorChunk::Array:
> + case DeclaratorChunk::Function:
> + break;
> +
> + case DeclaratorChunk::BlockPointer:
> + case DeclaratorChunk::MemberPointer:
> + return numNormalPointers > 0 ? PointerDeclaratorKind::MultiLevelPointer
> + : PointerDeclaratorKind::SingleLevelPointer;
> +
> + case DeclaratorChunk::Paren:
> + case DeclaratorChunk::Reference:
> + continue;
> +
> + case DeclaratorChunk::Pointer:
> + ++numNormalPointers;
> + if (numNormalPointers > 2)
Why > instead of >=?
> + return PointerDeclaratorKind::MultiLevelPointer;
> + continue;
> + }
> + }
> +
> + // Then, dig into the type specifier itself.
> + unsigned numTypeSpecifierPointers = 0;
> + do {
> + // Decompose normal pointers.
> + if (auto ptrType = type->getAs<PointerType>()) {
> + ++numNormalPointers;
> +
> + if (numNormalPointers > 2)
> + return PointerDeclaratorKind::MultiLevelPointer;
Same question here.
> +
> + type = ptrType->getPointeeType();
> + ++numTypeSpecifierPointers;
> + continue;
> + }
> +
> + // Decompose block pointers.
> + if (type->getAs<BlockPointerType>()) {
> + return numNormalPointers > 0 ? PointerDeclaratorKind::MultiLevelPointer
> + : PointerDeclaratorKind::SingleLevelPointer;
> + }
> +
> + // Decompose member pointers.
> + if (type->getAs<MemberPointerType>()) {
> + return numNormalPointers > 0 ? PointerDeclaratorKind::MultiLevelPointer
> + : PointerDeclaratorKind::SingleLevelPointer;
> + }
> +
> + // Look at Objective-C object pointers.
> + if (auto objcObjectPtr = type->getAs<ObjCObjectPointerType>()) {
> + ++numNormalPointers;
> + ++numTypeSpecifierPointers;
> +
> + // If this is NSError**, report that.
> + if (auto objcClassDecl = objcObjectPtr->getInterfaceDecl()) {
> + if (objcClassDecl->getIdentifier() == S.getNSErrorIdent() &&
> + numNormalPointers == 2 && numTypeSpecifierPointers < 2) {
> + return PointerDeclaratorKind::NSErrorPointerPointer;
> + }
> + }
> +
> + break;
> + }
> +
> + // Look at Objective-C class types.
> + if (auto objcClass = type->getAs<ObjCInterfaceType>()) {
> + if (objcClass->getInterface()->getIdentifier() == S.getNSErrorIdent()) {
> + if (numNormalPointers == 2 && numTypeSpecifierPointers < 2)
> + return PointerDeclaratorKind::NSErrorPointerPointer;;
> + }
> +
> + break;
> + }
> +
> + // If at this point we haven't seen a pointer, we won't see one.
> + if (numNormalPointers == 0)
> + return PointerDeclaratorKind::NonPointer;
> +
> + if (auto recordType = type->getAs<RecordType>()) {
> + RecordDecl *recordDecl = recordType->getDecl();
> +
> + bool isCFError = false;
> + if (S.CFError) {
> + // If we already know about CFError, test it directly.
> + isCFError = (S.CFError == recordDecl);
> + } else {
> + // Check whether this is CFError, which we identify based on its bridge
> + // to NSError.
> + if (recordDecl->getTagKind() == TTK_Struct && numNormalPointers > 0) {
> + if (auto bridgeAttr = recordDecl->getAttr<ObjCBridgeAttr>()) {
> + if (bridgeAttr->getBridgedType() == S.getNSErrorIdent()) {
> + S.CFError = recordDecl;
> + isCFError = true;
> + }
> + }
> + }
> + }
> +
> + // If this is CFErrorRef*, report it as such.
> + if (isCFError && numNormalPointers == 2 && numTypeSpecifierPointers < 2) {
> + return PointerDeclaratorKind::CFErrorRefPointer;
> + }
> + break;
> + }
> +
> + break;
> + } while (true);
> +
> +
> + switch (numNormalPointers) {
> + case 0:
> + return PointerDeclaratorKind::NonPointer;
> +
> + case 1:
> + return PointerDeclaratorKind::SingleLevelPointer;
> +
> + default:
> + return PointerDeclaratorKind::MultiLevelPointer;
> + }
> +}
> +
> static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
> QualType declSpecType,
> TypeSourceInfo *TInfo) {
> @@ -2620,6 +2826,183 @@ static TypeSourceInfo *GetFullTypeForDec
> }
> }
>
> + // Determine whether we should infer __nonnull on pointer types.
> + Optional<NullabilityKind> inferNullability;
> + bool inferNullabilityCS = false;
> +
> + // Are we in an assume-nonnull region?
> + bool inAssumeNonNullRegion = false;
> + if (S.PP.getPragmaAssumeNonNullLoc().isValid() &&
> + !state.getDeclarator().isObjCWeakProperty() &&
> + !S.deduceWeakPropertyFromType(T)) {
> + inAssumeNonNullRegion = true;
> + }
> +
> + // Whether to complain about missing nullability specifiers or not.
> + enum {
> + /// Never complain.
> + CAMN_No,
> + /// Complain on the inner pointers (but not the outermost
> + /// pointer).
> + CAMN_InnerPointers,
> + /// Complain about any pointers that don't have nullability
> + /// specified or inferred.
> + CAMN_Yes
> + } complainAboutMissingNullability = CAMN_No;
> + unsigned NumPointersRemaining = 0;
> +
> + if (IsTypedefName) {
> + // For typedefs, we do not infer any nullability (the default),
> + // and we only complain about missing nullability specifiers on
> + // inner pointers.
> + complainAboutMissingNullability = CAMN_InnerPointers;
> +
> + if (T->canHaveNullability()) {
> + ++NumPointersRemaining;
> + }
> +
> + for (unsigned i = 0, n = D.getNumTypeObjects(); i != n; ++i) {
> + DeclaratorChunk &chunk = D.getTypeObject(i);
> + switch (chunk.Kind) {
> + case DeclaratorChunk::Array:
> + case DeclaratorChunk::Function:
> + break;
> +
> + case DeclaratorChunk::BlockPointer:
> + case DeclaratorChunk::MemberPointer:
> + ++NumPointersRemaining;
> + break;
> +
> + case DeclaratorChunk::Paren:
> + case DeclaratorChunk::Reference:
> + continue;
> +
> + case DeclaratorChunk::Pointer:
> + ++NumPointersRemaining;
> + continue;
> + }
> + }
> + } else {
> + bool isFunctionOrMethod = false;
> + switch (auto context = state.getDeclarator().getContext()) {
> + case Declarator::ObjCParameterContext:
> + case Declarator::ObjCResultContext:
> + case Declarator::PrototypeContext:
> + case Declarator::TrailingReturnContext:
> + isFunctionOrMethod = true;
> + // fallthrough
> +
> + case Declarator::MemberContext:
> + if (state.getDeclarator().isObjCIvar() && !isFunctionOrMethod) {
> + complainAboutMissingNullability = CAMN_No;
> + break;
> + }
> + // fallthrough
> +
> + case Declarator::FileContext:
> + case Declarator::KNRTypeListContext:
> + complainAboutMissingNullability = CAMN_Yes;
> +
> + // Nullability inference depends on the type and declarator.
> + switch (classifyPointerDeclarator(S, T, D)) {
> + case PointerDeclaratorKind::NonPointer:
> + case PointerDeclaratorKind::MultiLevelPointer:
> + // Cannot infer nullability.
> + break;
> +
> + case PointerDeclaratorKind::SingleLevelPointer:
> + // Infer __nonnull if we are in an assumes-nonnull region.
> + if (inAssumeNonNullRegion) {
> + inferNullability = NullabilityKind::NonNull;
> + inferNullabilityCS = (context == Declarator::ObjCParameterContext ||
> + context == Declarator::ObjCResultContext);
> + }
> + break;
> +
> + case PointerDeclaratorKind::CFErrorRefPointer:
> + case PointerDeclaratorKind::NSErrorPointerPointer:
> + // Within a function or method signature, infer __nullable at both
> + // levels.
> + if (isFunctionOrMethod && inAssumeNonNullRegion)
> + inferNullability = NullabilityKind::Nullable;
> + break;
> + }
> + break;
> +
> + case Declarator::ConversionIdContext:
> + complainAboutMissingNullability = CAMN_Yes;
> + break;
> +
> + case Declarator::AliasDeclContext:
> + case Declarator::AliasTemplateContext:
> + case Declarator::BlockContext:
> + case Declarator::BlockLiteralContext:
> + case Declarator::ConditionContext:
> + case Declarator::CXXCatchContext:
> + case Declarator::CXXNewContext:
> + case Declarator::ForContext:
> + case Declarator::LambdaExprContext:
> + case Declarator::LambdaExprParameterContext:
> + case Declarator::ObjCCatchContext:
> + case Declarator::TemplateParamContext:
> + case Declarator::TemplateTypeArgContext:
> + case Declarator::TypeNameContext:
> + // Don't infer in these contexts.
> + break;
> + }
> + }
> +
> + // Local function that checks the nullability for a given pointer declarator.
> + // Returns true if __nonnull was inferred.
> + auto inferPointerNullability = [&](SimplePointerKind pointerKind,
> + SourceLocation pointerLoc,
> + AttributeList *&attrs) -> AttributeList * {
> + // We've seen a pointer.
> + if (NumPointersRemaining > 0)
> + --NumPointersRemaining;
> +
> + // If a nullability attribute is present, there's nothing to do.
> + if (hasNullabilityAttr(attrs))
> + return nullptr;
> +
> + // If we're supposed to infer nullability, do so now.
> + if (inferNullability) {
> + AttributeList *nullabilityAttr = state.getDeclarator().getAttributePool()
> + .create(
> + S.getNullabilityKeyword(
> + *inferNullability),
> + SourceRange(pointerLoc),
> + nullptr, SourceLocation(),
> + nullptr, 0,
> + AttributeList::AS_Keyword);
> + if (inferNullabilityCS)
> + nullabilityAttr->setContextSensitiveKeywordAttribute();
> +
> + spliceAttrIntoList(*nullabilityAttr, attrs);
> + return nullabilityAttr;
> + }
> +
> + return nullptr;
> + };
> +
> + // If the type itself could have nullability but does not, infer pointer
> + // nullability.
> + if (T->canHaveNullability() && S.ActiveTemplateInstantiations.empty()) {
> + 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();
> + }
> + }
> +
> // Walk the DeclTypeInfo, building the recursive type as we go.
> // DeclTypeInfos are ordered from the identifier out, which is
> // opposite of what we want :).
> @@ -2637,6 +3020,10 @@ static TypeSourceInfo *GetFullTypeForDec
> if (!LangOpts.Blocks)
> S.Diag(DeclType.Loc, diag::err_blocks_disable);
>
> + // Handle pointer nullability.
> + inferPointerNullability(SimplePointerKind::BlockPointer,
> + DeclType.Loc, DeclType.getAttrListRef());
> +
> T = S.BuildBlockPointerType(T, D.getIdentifierLoc(), Name);
> if (DeclType.Cls.TypeQuals)
> T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Cls.TypeQuals);
> @@ -2649,6 +3036,11 @@ static TypeSourceInfo *GetFullTypeForDec
> D.setInvalidType(true);
> // Build the type anyway.
> }
> +
> + // Handle pointer nullability
> + inferPointerNullability(SimplePointerKind::Pointer, DeclType.Loc,
> + DeclType.getAttrListRef());
> +
> if (LangOpts.ObjC1 && T->getAs<ObjCObjectType>()) {
> T = Context.getObjCObjectPointerType(T);
> if (DeclType.Ptr.TypeQuals)
> @@ -3090,6 +3482,11 @@ static TypeSourceInfo *GetFullTypeForDec
> // The scope spec must refer to a class, or be dependent.
> CXXScopeSpec &SS = DeclType.Mem.Scope();
> QualType ClsType;
> +
> + // Handle pointer nullability.
> + inferPointerNullability(SimplePointerKind::MemberPointer,
> + DeclType.Loc, DeclType.getAttrListRef());
> +
> if (SS.isInvalid()) {
> // Avoid emitting extra errors if we already errored on the scope.
> D.setInvalidType(true);
> @@ -4614,20 +5011,6 @@ bool Sema::checkNullabilityTypeSpecifier
> 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;
> -}
> -
> /// Map a nullability attribute kind to a nullability kind.
> static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
> switch (kind) {
>
> Modified: cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m?rev=240156&r1=240155&r2=240156&view=diff
> ==============================================================================
> --- cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m (original)
> +++ cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m Fri Jun 19 13:25:57 2015
> @@ -80,10 +80,28 @@
> @end
>
> // rdar://20152386
> +// rdar://20383235
> +
> @interface NSObject @end
>
> - at interface rdar20152386_2: NSObject
> +#pragma clang assume_nonnull begin
> + at interface I: NSObject
> + at property(nonatomic, weak) id delegate; // Do not warn, nullable is inferred.
> + at property(nonatomic, weak, readonly) id ROdelegate; // Do not warn, nullable is inferred.
> + at property(nonatomic, weak, nonnull) id NonNulldelete; // expected-error {{property attributes 'nonnull' and 'weak' are mutually exclusive}}
> + at property(nonatomic, weak, nullable) id Nullabledelete; // do not warn
> +
> +// strong cases.
> + at property(nonatomic, strong) id stdelegate; // Do not warn
> + at property(nonatomic, readonly) id stROdelegate; // Do not warn
> + at property(nonatomic, strong, nonnull) id stNonNulldelete; // Do not warn
> + at property(nonatomic, nullable) id stNullabledelete; // do not warn
> + at end
> +#pragma clang assume_nonnull end
> +
> + at interface J: NSObject
> + at property(nonatomic, weak) id ddd; // Do not warn, nullable is inferred.
> @property(nonatomic, weak, nonnull) id delegate; // expected-error {{property attributes 'nonnull' and 'weak' are mutually exclusive}}
> - at property(nonatomic, weak, nonnull, readonly) id ReadDelegate; // no warning
> + at property(nonatomic, weak, nonnull, readonly) id ROdelegate; // expected-error {{property attributes 'nonnull' and 'weak' are mutually exclusive}}
> @end
>
>
> Added: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h?rev=240156&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h (added)
> +++ cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h Fri Jun 19 13:25:57 2015
> @@ -0,0 +1,98 @@
> +__attribute__((objc_root_class))
> + at interface NSError
> + at end
> +
> +__attribute__((objc_root_class))
> + at interface A
> + at end
> +
> +struct X { };
> +
> +void f1(int *x);
> +
> +typedef struct __attribute__((objc_bridge(NSError))) __CFError *CFErrorRef;
> +typedef NSError *NSErrorPtr;
> +typedef NSError **NSErrorPtrPtr;
> +typedef CFErrorRef *CFErrorRefPtr;
> +typedef int *int_ptr;
> +typedef A *A_ptr;
> +typedef int (^block_ptr)(int, int);
> +
> +#pragma clang assume_nonnull begin
> +
> +void f2(int *x);
> +void f3(A* obj);
> +void f4(int (^block)(int, int));
> +void f5(int_ptr x);
> +void f6(A_ptr obj);
> +void f7(int * __nullable x);
> +void f8(A * __nullable obj);
> +void f9(int X::* mem_ptr);
> +void f10(int (X::*mem_func)(int, int));
> +void f11(int X::* __nullable mem_ptr);
> +void f12(int (X::* __nullable mem_func)(int, int));
> +
> +int_ptr f13(void);
> +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 g1(int (^)(int, int));
> +void g2(int (^ *bp)(int, int));
> +void g3(block_ptr *bp);
> +void g4(int (*fp)(int, int));
> +void g5(int (**fp)(int, int));
> +
> + at interface A(Pragmas1)
> ++ (instancetype)aWithA:(A *)a;
> +- (A *)method1:(A_ptr)ptr;
> +- (null_unspecified A *)method2;
> +- (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;
> +
> + at property A *aProp;
> + at property NSError **anError;
> + at end
> +
> +int *global_int_ptr;
> +
> +// typedefs not inferred __nonnull
> +typedef int *int_ptr_2;
> +
> +typedef int *
> + *int_ptr_ptr;
> +
> +static inline void f30(void) {
> + float *fp = global_int_ptr; // expected-error{{cannot initialize a variable of type 'float *' with an lvalue of type 'int * __nonnull'}}
> +
> + int_ptr_2 ip2;
> + float *fp2 = ip2; // expected-error{{cannot initialize a variable of type 'float *' with an lvalue of type 'int_ptr_2' (aka 'int *')}}
> +
> + int_ptr_ptr ipp;
> + float *fp3 = ipp; // expected-error{{lvalue of type 'int_ptr_ptr' (aka 'int **')}}
> +}
> +
> + at interface AA : A {
> + at public
> + id ivar1;
> + __nonnull id ivar2;
> +}
> + at end
> +
> +#pragma clang assume_nonnull end
> +
> +void f20(A *a);
> +void f21(int_ptr x);
> +void f22(A_ptr y);
> +void f23(int_ptr __nullable x);
> +void f24(A_ptr __nullable y);
> +void f25(int_ptr_2 x);
> +
> + at interface A(OutsidePragmas1)
> ++ (instancetype)aWithInt:(int)value;
> + at end
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-1.h
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h?rev=240156&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h (added)
> +++ cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h Fri Jun 19 13:25:57 2015
> @@ -0,0 +1,12 @@
> +#pragma clang assume_nonnull start // expected-error{{expected 'begin' or 'end'}}
> +
> +#pragma clang assume_nonnull begin // expected-note{{#pragma entered here}}
> +
> +#include "nullability-pragmas-3.h" // expected-error{{cannot #include files inside '#pragma clang assume_nonnull'}}
> +
> +#pragma clang assume_nonnull begin // expected-note{{#pragma entered here}}
> +#pragma clang assume_nonnull begin // expected-error{{already inside '#pragma clang assume_nonnull'}}
> +#pragma clang assume_nonnull end
> +
> +#pragma clang assume_nonnull begin // expected-error{{'#pragma clang assume_nonnull' was not ended within this file}}
> +
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-2.h
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-3.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-3.h?rev=240156&view=auto
> ==============================================================================
> (empty)
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-3.h
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-3.h
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/SemaObjCXX/Inputs/nullability-pragmas-3.h
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/SemaObjCXX/nullability-pragmas.mm
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/nullability-pragmas.mm?rev=240156&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaObjCXX/nullability-pragmas.mm (added)
> +++ cfe/trunk/test/SemaObjCXX/nullability-pragmas.mm Fri Jun 19 13:25:57 2015
> @@ -0,0 +1,41 @@
> +// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs %s -verify
> +
> +#include "nullability-pragmas-1.h"
> +#include "nullability-pragmas-2.h"
> +
> +#if !__has_feature(assume_nonnull)
> +# error assume_nonnull feature is not set
> +#endif
> +
> +void test_pragmas_1(A * __nonnull a, AA * __nonnull aa) {
> + f1(0); // okay: no nullability annotations
> + f2(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
> + f3(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
> + f4(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
> + f5(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
> + f6(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
> + f7(0); // okay
> + f8(0); // okay
> + f9(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
> + f10(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
> + f11(0); // okay
> + f12(0); // okay
> + [a method1:0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
> +
> + f17(a); // expected-error{{no matching function for call to 'f17'}}
> + [a method3: a]; // expected-error{{cannot initialize a parameter of type 'NSError * __nullable * __nullable' with an lvalue of type 'A * __nonnull'}}
> + [a method4: a]; // expected-error{{cannot initialize a parameter of type 'NSErrorPtr __nullable * __nullable' (aka 'NSError **') with an lvalue of type 'A * __nonnull'}}
> +
> + float *ptr;
> + ptr = f13(); // expected-error{{assigning to 'float *' from incompatible type 'int_ptr __nonnull' (aka 'int *')}}
> + ptr = f14(); // expected-error{{assigning to 'float *' from incompatible type 'A * __nonnull'}}
> + ptr = [a method1:a]; // expected-error{{assigning to 'float *' from incompatible type 'A * __nonnull'}}
> + ptr = a.aProp; // expected-error{{assigning to 'float *' from incompatible type 'A * __nonnull'}}
> + ptr = global_int_ptr; // expected-error{{assigning to 'float *' from incompatible type 'int * __nonnull'}}
> + ptr = f15(); // expected-error{{assigning to 'float *' from incompatible type 'int * __null_unspecified'}}
> + ptr = f16(); // expected-error{{assigning to 'float *' from incompatible type 'A * __null_unspecified'}}
> + ptr = [a method2]; // expected-error{{assigning to 'float *' from incompatible type 'A * __null_unspecified'}}
> +
> + ptr = aa->ivar1; // expected-error{{from incompatible type 'id'}}
> + ptr = aa->ivar2; // expected-error{{from incompatible type 'id __nonnull'}}
> +}
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
~Aaron
More information about the cfe-commits
mailing list