diff options
author | Douglas Gregor <dgregor@apple.com> | 2015-06-19 18:25:57 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2015-06-19 18:25:57 +0000 |
commit | 2a20bd1a945705e3a3845e3d4ef6c3cb68d938c7 (patch) | |
tree | fdda46a505a45931089192a51a5c231fe66f7298 /clang/lib/Lex | |
parent | 849ebc269fe17def169c812f929746cb98955664 (diff) | |
download | bcm5719-llvm-2a20bd1a945705e3a3845e3d4ef6c3cb68d938c7.tar.gz bcm5719-llvm-2a20bd1a945705e3a3845e3d4ef6c3cb68d938c7.zip |
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.
llvm-svn: 240156
Diffstat (limited to 'clang/lib/Lex')
-rw-r--r-- | clang/lib/Lex/PPDirectives.cpp | 9 | ||||
-rw-r--r-- | clang/lib/Lex/PPLexerChange.cpp | 11 | ||||
-rw-r--r-- | clang/lib/Lex/PPMacroExpansion.cpp | 1 | ||||
-rw-r--r-- | clang/lib/Lex/Pragma.cpp | 55 |
4 files changed, 76 insertions, 0 deletions
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 43def2b32c5..33ce7992281 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1575,6 +1575,15 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, 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 diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp index e68fb7df8e8..1a35d32dedb 100644 --- a/clang/lib/Lex/PPLexerChange.cpp +++ b/clang/lib/Lex/PPLexerChange.cpp @@ -355,6 +355,17 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) { 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()) { diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 62498c64464..ad115bace99 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1052,6 +1052,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .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) diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 26ed674f65a..5eb665549e8 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1342,6 +1342,60 @@ struct PragmaARCCFCodeAuditedHandler : public PragmaHandler { } }; +/// 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::RegisterBuiltinPragmas() { 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()); |