diff options
| author | Erik Pilkington <erik.pilkington@gmail.com> | 2016-07-16 00:35:23 +0000 |
|---|---|---|
| committer | Erik Pilkington <erik.pilkington@gmail.com> | 2016-07-16 00:35:23 +0000 |
| commit | 29099ded0c1546dac891cf5510fd611a1d1992a5 (patch) | |
| tree | 599ab477fd7cd169e7633e40a4bc3e3ad776d4b8 /clang/lib/Parse/ParseExpr.cpp | |
| parent | 21b3a032af8bcacbe2b3712de96b033b7ff3f208 (diff) | |
| download | bcm5719-llvm-29099ded0c1546dac891cf5510fd611a1d1992a5.tar.gz bcm5719-llvm-29099ded0c1546dac891cf5510fd611a1d1992a5.zip | |
[ObjC] Implement @available in the Parser and AST
This patch adds a new AST node: ObjCAvailabilityCheckExpr, and teaches the
Parser and Sema to generate it. This node represents an availability check of
the form:
@available(macos 10.10, *);
Which will eventually compile to a runtime check of the host's OS version. This
is the first patch of the feature I proposed here:
http://lists.llvm.org/pipermail/cfe-dev/2016-July/049851.html
Differential Revision: https://reviews.llvm.org/D22171
llvm-svn: 275654
Diffstat (limited to 'clang/lib/Parse/ParseExpr.cpp')
| -rw-r--r-- | clang/lib/Parse/ParseExpr.cpp | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 4e39d42ea6a..47d1326b798 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1009,6 +1009,8 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw__Generic: // primary-expression: generic-selection [C11 6.5.1] Res = ParseGenericSelectionExpression(); break; + case tok::kw___builtin_available: + return ParseAvailabilityCheckExpr(Tok.getLocation()); case tok::kw___builtin_va_arg: case tok::kw___builtin_offsetof: case tok::kw___builtin_choose_expr: @@ -2869,3 +2871,117 @@ ExprResult Parser::ParseObjCBoolLiteral() { tok::TokenKind Kind = Tok.getKind(); return Actions.ActOnObjCBoolLiteral(ConsumeToken(), Kind); } + +/// Validate availability spec list, emitting diagnostics if necessary. Returns +/// true if invalid. +static bool CheckAvailabilitySpecList(Parser &P, + ArrayRef<AvailabilitySpec> AvailSpecs) { + llvm::SmallSet<StringRef, 4> Platforms; + bool HasOtherPlatformSpec = false; + bool Valid = true; + for (const auto &Spec : AvailSpecs) { + if (Spec.isOtherPlatformSpec()) { + if (HasOtherPlatformSpec) { + P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_star); + Valid = false; + } + + HasOtherPlatformSpec = true; + continue; + } + + bool Inserted = Platforms.insert(Spec.getPlatform()).second; + if (!Inserted) { + // Rule out multiple version specs referring to the same platform. + // For example, we emit an error for: + // @available(macos 10.10, macos 10.11, *) + StringRef Platform = Spec.getPlatform(); + P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_platform) + << Spec.getEndLoc() << Platform; + Valid = false; + } + } + + if (!HasOtherPlatformSpec) { + SourceLocation InsertWildcardLoc = AvailSpecs.back().getEndLoc(); + P.Diag(InsertWildcardLoc, diag::err_availability_query_wildcard_required) + << FixItHint::CreateInsertion(InsertWildcardLoc, ", *"); + return true; + } + + return !Valid; +} + +/// Parse availability query specification. +/// +/// availability-spec: +/// '*' +/// identifier version-tuple +Optional<AvailabilitySpec> Parser::ParseAvailabilitySpec() { + if (Tok.is(tok::star)) { + return AvailabilitySpec(ConsumeToken()); + } else { + // Parse the platform name. + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_avail_query_expected_platform_name); + return None; + } + + IdentifierLoc *PlatformIdentifier = ParseIdentifierLoc(); + SourceRange VersionRange; + VersionTuple Version = ParseVersionTuple(VersionRange); + + if (Version.empty()) + return None; + + StringRef Platform = PlatformIdentifier->Ident->getName(); + + if (AvailabilityAttr::getPrettyPlatformName(Platform).empty()) { + Diag(PlatformIdentifier->Loc, + diag::err_avail_query_unrecognized_platform_name) + << Platform; + return None; + } + + return AvailabilitySpec(Version, Platform, PlatformIdentifier->Loc, + VersionRange.getEnd()); + } +} + +ExprResult Parser::ParseAvailabilityCheckExpr(SourceLocation BeginLoc) { + assert(Tok.is(tok::kw___builtin_available) || + Tok.isObjCAtKeyword(tok::objc_available)); + + // Eat the available or __builtin_available. + ConsumeToken(); + + BalancedDelimiterTracker Parens(*this, tok::l_paren); + if (Parens.expectAndConsume()) + return ExprError(); + + SmallVector<AvailabilitySpec, 4> AvailSpecs; + bool HasError = false; + while (true) { + Optional<AvailabilitySpec> Spec = ParseAvailabilitySpec(); + if (!Spec) + HasError = true; + else + AvailSpecs.push_back(*Spec); + + if (!TryConsumeToken(tok::comma)) + break; + } + + if (HasError) { + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + CheckAvailabilitySpecList(*this, AvailSpecs); + + if (Parens.consumeClose()) + return ExprError(); + + return Actions.ActOnObjCAvailabilityCheckExpr(AvailSpecs, BeginLoc, + Parens.getCloseLocation()); +} |

