r240156 - Introduced pragmas for audited nullability regions.
Douglas Gregor
dgregor at apple.com
Fri Jun 19 11:25:58 PDT 2015
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;
+ } 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)
+ 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;
+
+ 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'}}
+}
More information about the cfe-commits
mailing list