| //===--- ParseDecl.cpp - Swift Language Parser for Declarations -----------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Declaration Parsing and AST Building |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "DebuggerContextChange.h" |
| #include "swift/Parse/Parser.h" |
| #include "swift/Parse/CodeCompletionCallbacks.h" |
| #include "swift/Parse/ParsedSyntaxBuilders.h" |
| #include "swift/Parse/ParsedSyntaxRecorder.h" |
| #include "swift/Parse/ParseSILSupport.h" |
| #include "swift/Parse/SyntaxParsingContext.h" |
| #include "swift/Syntax/SyntaxKind.h" |
| #include "swift/Subsystems.h" |
| #include "swift/AST/Attr.h" |
| #include "swift/AST/LazyResolver.h" |
| #include "swift/AST/DebuggerClient.h" |
| #include "swift/AST/DiagnosticsParse.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/ParseRequests.h" |
| #include "swift/AST/SourceFile.h" |
| #include "swift/Basic/Defer.h" |
| #include "swift/Basic/Statistic.h" |
| #include "swift/Basic/StringExtras.h" |
| #include "clang/Basic/CharInfo.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/SaveAndRestore.h" |
| #include "llvm/ADT/PointerUnion.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/ADT/StringSet.h" |
| #include <algorithm> |
| |
| using namespace swift; |
| using namespace syntax; |
| |
| /// Main entrypoint for the parser. |
| /// |
| /// \verbatim |
| /// top-level: |
| /// stmt-brace-item* |
| /// decl-sil [[only in SIL mode] |
| /// decl-sil-stage [[only in SIL mode] |
| /// \endverbatim |
| bool Parser::parseTopLevel() { |
| SF.ASTStage = SourceFile::Parsing; |
| |
| // Prime the lexer. |
| if (Tok.is(tok::NUM_TOKENS)) |
| consumeTokenWithoutFeedingReceiver(); |
| |
| // Parse the body of the file. |
| SmallVector<ASTNode, 128> Items; |
| |
| // If we are in SIL mode, and if the first token is the start of a sil |
| // declaration, parse that one SIL function and return to the top level. This |
| // allows type declarations and other things to be parsed, name bound, and |
| // type checked in batches, similar to immediate mode. This also enforces |
| // that SIL bodies can only be at the top level. |
| switch (Tok.getKind()) { |
| default: |
| parseBraceItems(Items, allowTopLevelCode() |
| ? BraceItemListKind::TopLevelCode |
| : BraceItemListKind::TopLevelLibrary); |
| break; |
| |
| // For now, create 'UnknownDecl' for all SIL declarations. |
| #define CASE_SIL(KW, NAME) \ |
| case tok::kw_##KW: { \ |
| assert(isInSILMode() && "'" #KW "' should only be a keyword in SIL mode"); \ |
| SyntaxParsingContext itemCtxt(SyntaxContext, SyntaxKind::CodeBlockItem); \ |
| SyntaxParsingContext declCtxt(SyntaxContext, SyntaxContextKind::Decl); \ |
| SIL->parse##NAME(*this); \ |
| break; \ |
| } |
| CASE_SIL(sil, DeclSIL) |
| CASE_SIL(sil_stage, DeclSILStage) |
| CASE_SIL(sil_vtable, SILVTable) |
| CASE_SIL(sil_global, SILGlobal) |
| CASE_SIL(sil_witness_table, SILWitnessTable) |
| CASE_SIL(sil_default_witness_table, SILDefaultWitnessTable) |
| // SWIFT_ENABLE_TENSORFLOW |
| CASE_SIL(sil_differentiability_witness, SILDifferentiabilityWitness) |
| // SWIFT_ENABLE_TENSORFLOW END |
| CASE_SIL(sil_coverage_map, SILCoverageMap) |
| CASE_SIL(sil_property, SILProperty) |
| CASE_SIL(sil_scope, SILScope) |
| #undef CASE_SIL |
| } |
| |
| // In the case of a catastrophic parse error, consume any trailing |
| // #else, #elseif, or #endif and move on to the next statement or declaration |
| // block. |
| if (Tok.is(tok::pound_else) || Tok.is(tok::pound_elseif) || |
| Tok.is(tok::pound_endif)) { |
| diagnose(Tok.getLoc(), |
| diag::unexpected_conditional_compilation_block_terminator); |
| // Create 'UnknownDecl' for orphan directives. |
| SyntaxParsingContext itemCtxt(SyntaxContext, SyntaxKind::CodeBlockItem); |
| SyntaxParsingContext declCtxt(SyntaxContext, SyntaxContextKind::Decl); |
| |
| consumeToken(); |
| } |
| |
| // If this is a Main source file, determine if we found code that needs to be |
| // executed (this is used by the repl to know whether to compile and run the |
| // newly parsed stuff). |
| bool FoundTopLevelCodeToExecute = false; |
| if (allowTopLevelCode()) { |
| for (auto V : Items) { |
| if (isa<TopLevelCodeDecl>(V.get<Decl*>())) |
| FoundTopLevelCodeToExecute = true; |
| } |
| } |
| |
| // Add newly parsed decls to the module. |
| for (auto Item : Items) { |
| if (auto *D = Item.dyn_cast<Decl*>()) { |
| assert(!isa<AccessorDecl>(D) && "accessors should not be added here"); |
| SF.Decls.push_back(D); |
| } |
| } |
| |
| // Note that the source file is fully parsed and verify it. |
| SF.ASTStage = SourceFile::Parsed; |
| verify(SF); |
| |
| // Next time start relexing from the beginning of the comment so that we can |
| // attach it to the token. |
| State->markParserPosition(getParserPosition(), |
| InPoundLineEnvironment); |
| |
| // If we are done parsing the whole file, finalize the token receiver. |
| if (Tok.is(tok::eof)) { |
| SyntaxContext->addToken(Tok, LeadingTrivia, TrailingTrivia); |
| TokReceiver->finalize(); |
| } |
| |
| return FoundTopLevelCodeToExecute; |
| } |
| |
| ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList( |
| SourceLoc AtLoc, SourceLoc AttrLoc, StringRef AttrName) { |
| // Check 'Tok', return false if ':' or '=' cannot be found. |
| // Complain if '=' is found and suggest replacing it with ": ". |
| auto findAttrValueDelimiter = [&]() -> bool { |
| if (!Tok.is(tok::colon)) { |
| if (!Tok.is(tok::equal)) |
| return false; |
| |
| diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value) |
| .fixItReplace(Tok.getLoc(), ": "); |
| } |
| return true; |
| }; |
| |
| struct VersionArg { |
| llvm::VersionTuple Version; |
| SourceRange Range; |
| SourceLoc DelimiterLoc; |
| bool empty() const { |
| return Version.empty(); |
| } |
| }; |
| |
| StringRef Platform = Tok.getText(); |
| |
| StringRef Message, Renamed; |
| VersionArg Introduced, Deprecated, Obsoleted; |
| auto PlatformAgnostic = PlatformAgnosticAvailabilityKind::None; |
| |
| SyntaxParsingContext AvailabilitySpecContext( |
| SyntaxContext, SyntaxKind::AvailabilitySpecList); |
| |
| bool HasUpcomingEntry = false; |
| |
| { |
| SyntaxParsingContext EntryContext(SyntaxContext, |
| SyntaxKind::AvailabilityArgument); |
| consumeToken(); |
| if (consumeIf(tok::comma)) { |
| HasUpcomingEntry = true; |
| } |
| } |
| |
| bool AnyAnnotations = false; |
| bool AnyArgumentInvalid = false; |
| int ParamIndex = 0; |
| |
| while (HasUpcomingEntry) { |
| SyntaxParsingContext EntryContext(SyntaxContext, |
| SyntaxKind::AvailabilityArgument); |
| auto ArgumentLoc = Tok.getLoc(); |
| AnyAnnotations = true; |
| StringRef ArgumentKindStr = Tok.getText(); |
| ParamIndex++; |
| |
| enum { |
| IsMessage, IsRenamed, |
| IsIntroduced, IsDeprecated, IsObsoleted, |
| IsUnavailable, |
| IsInvalid |
| } ArgumentKind = IsInvalid; |
| |
| if (Tok.is(tok::identifier)) { |
| ArgumentKind = |
| llvm::StringSwitch<decltype(ArgumentKind)>(ArgumentKindStr) |
| .Case("message", IsMessage) |
| .Case("renamed", IsRenamed) |
| .Case("introduced", IsIntroduced) |
| .Case("deprecated", IsDeprecated) |
| .Case("obsoleted", IsObsoleted) |
| .Case("unavailable", IsUnavailable) |
| .Default(IsInvalid); |
| } |
| |
| if (ArgumentKind == IsInvalid) { |
| diagnose(ArgumentLoc, diag::attr_availability_expected_option, AttrName) |
| .highlight(SourceRange(ArgumentLoc)); |
| if (Tok.is(tok::code_complete) && CodeCompletion) { |
| CodeCompletion->completeDeclAttrParam(DAK_Available, ParamIndex); |
| consumeToken(tok::code_complete); |
| } else { |
| consumeIf(tok::identifier); |
| } |
| return nullptr; |
| } |
| |
| consumeToken(); |
| |
| auto diagnoseDuplicate = [&](bool WasEmpty) { |
| if (!WasEmpty) { |
| diagnose(ArgumentLoc, diag::attr_availability_invalid_duplicate, |
| ArgumentKindStr); |
| } |
| }; |
| |
| switch (ArgumentKind) { |
| case IsMessage: |
| case IsRenamed: { |
| // Items with string arguments. |
| if (findAttrValueDelimiter()) { |
| consumeToken(); |
| } else { |
| diagnose(Tok, diag::attr_availability_expected_equal, AttrName, |
| ArgumentKindStr); |
| AnyArgumentInvalid = true; |
| if (peekToken().isAny(tok::r_paren, tok::comma)) |
| consumeToken(); |
| break; |
| } |
| |
| if (!Tok.is(tok::string_literal)) { |
| diagnose(AttrLoc, diag::attr_expected_string_literal, AttrName); |
| AnyArgumentInvalid = true; |
| if (peekToken().isAny(tok::r_paren, tok::comma)) |
| consumeToken(); |
| break; |
| } |
| |
| auto Value = getStringLiteralIfNotInterpolated( |
| AttrLoc, ("'" + ArgumentKindStr + "'").str()); |
| consumeToken(); |
| if (!Value) { |
| AnyArgumentInvalid = true; |
| break; |
| } |
| |
| if (ArgumentKind == IsMessage) { |
| diagnoseDuplicate(Message.empty()); |
| Message = Value.getValue(); |
| } else { |
| ParsedDeclName parsedName = parseDeclName(Value.getValue()); |
| if (!parsedName) { |
| diagnose(AttrLoc, diag::attr_availability_invalid_renamed, AttrName); |
| AnyArgumentInvalid = true; |
| break; |
| } |
| diagnoseDuplicate(Renamed.empty()); |
| Renamed = Value.getValue(); |
| } |
| |
| SyntaxContext->createNodeInPlace(SyntaxKind::AvailabilityLabeledArgument); |
| |
| break; |
| } |
| |
| case IsDeprecated: |
| if (!findAttrValueDelimiter()) { |
| if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { |
| diagnose(Tok, diag::attr_availability_unavailable_deprecated, |
| AttrName); |
| } |
| |
| PlatformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated; |
| break; |
| } |
| LLVM_FALLTHROUGH; |
| |
| case IsIntroduced: |
| case IsObsoleted: { |
| // Items with version arguments. |
| SourceLoc DelimiterLoc; |
| if (findAttrValueDelimiter()) { |
| DelimiterLoc = Tok.getLoc(); |
| consumeToken(); |
| } else { |
| diagnose(Tok, diag::attr_availability_expected_equal, AttrName, |
| ArgumentKindStr); |
| AnyArgumentInvalid = true; |
| if (peekToken().isAny(tok::r_paren, tok::comma)) |
| consumeToken(); |
| break; |
| } |
| |
| auto &VerArg = |
| (ArgumentKind == IsIntroduced) |
| ? Introduced |
| : (ArgumentKind == IsDeprecated) ? Deprecated : Obsoleted; |
| |
| bool VerArgWasEmpty = VerArg.empty(); |
| if (parseVersionTuple( |
| VerArg.Version, VerArg.Range, |
| Diagnostic(diag::attr_availability_expected_version, AttrName))) { |
| AnyArgumentInvalid = true; |
| if (peekToken().isAny(tok::r_paren, tok::comma)) |
| consumeToken(); |
| } |
| VerArg.DelimiterLoc = DelimiterLoc; |
| diagnoseDuplicate(VerArgWasEmpty); |
| |
| SyntaxContext->createNodeInPlace(SyntaxKind::AvailabilityLabeledArgument); |
| |
| break; |
| } |
| |
| case IsUnavailable: |
| if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { |
| diagnose(Tok, diag::attr_availability_unavailable_deprecated, AttrName); |
| } |
| |
| PlatformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; |
| break; |
| |
| case IsInvalid: |
| llvm_unreachable("handled above"); |
| } |
| |
| // Parse the trailing comma |
| if (consumeIf(tok::comma)) { |
| HasUpcomingEntry = true; |
| } else { |
| HasUpcomingEntry = false; |
| } |
| } |
| |
| if (!AnyAnnotations) { |
| diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName, |
| /*isDeclModifier*/ false); |
| } |
| |
| auto PlatformKind = platformFromString(Platform); |
| |
| // Treat 'swift' as a valid version-qualifying token, when |
| // at least some versions were mentioned and no other |
| // platform-agnostic availability spec has been provided. |
| bool SomeVersion = (!Introduced.empty() || |
| !Deprecated.empty() || |
| !Obsoleted.empty()); |
| if (!PlatformKind.hasValue() && |
| (Platform == "swift" || Platform == "_PackageDescription")) { |
| |
| if (PlatformAgnostic == PlatformAgnosticAvailabilityKind::Deprecated) { |
| diagnose(AttrLoc, |
| diag::attr_availability_platform_agnostic_expected_deprecated_version, |
| AttrName, Platform); |
| return nullptr; |
| } |
| if (PlatformAgnostic == PlatformAgnosticAvailabilityKind::Unavailable) { |
| diagnose(AttrLoc, diag::attr_availability_platform_agnostic_infeasible_option, |
| "unavailable", AttrName, Platform); |
| return nullptr; |
| } |
| assert(PlatformAgnostic == PlatformAgnosticAvailabilityKind::None); |
| |
| if (!SomeVersion) { |
| diagnose(AttrLoc, diag::attr_availability_platform_agnostic_expected_option, |
| AttrName, Platform); |
| return nullptr; |
| } |
| |
| PlatformKind = PlatformKind::none; |
| PlatformAgnostic = (Platform == "swift") ? |
| PlatformAgnosticAvailabilityKind::SwiftVersionSpecific : |
| PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific; |
| } |
| |
| |
| if (AnyArgumentInvalid) |
| return nullptr; |
| if (!PlatformKind.hasValue()) { |
| diagnose(AttrLoc, diag::attr_availability_unknown_platform, |
| Platform, AttrName); |
| return nullptr; |
| } |
| |
| // Warn if any version is specified for non-specific platform '*'. |
| if (Platform == "*" && SomeVersion) { |
| auto diag = diagnose(AttrLoc, |
| diag::attr_availability_nonspecific_platform_unexpected_version, |
| AttrName); |
| if (!Introduced.empty()) |
| diag.fixItRemove(SourceRange(Introduced.DelimiterLoc, |
| Introduced.Range.End)); |
| if (!Deprecated.empty()) |
| diag.fixItRemove(SourceRange(Deprecated.DelimiterLoc, |
| Deprecated.Range.End)); |
| if (!Obsoleted.empty()) |
| diag.fixItRemove(SourceRange(Obsoleted.DelimiterLoc, |
| Obsoleted.Range.End)); |
| return nullptr; |
| } |
| |
| auto Attr = new (Context) |
| AvailableAttr(AtLoc, SourceRange(AttrLoc, Tok.getLoc()), |
| PlatformKind.getValue(), |
| Message, Renamed, |
| Introduced.Version, Introduced.Range, |
| Deprecated.Version, Deprecated.Range, |
| Obsoleted.Version, Obsoleted.Range, |
| PlatformAgnostic, |
| /*Implicit=*/false); |
| return makeParserResult(Attr); |
| |
| } |
| |
| bool Parser::parseSpecializeAttributeArguments( |
| swift::tok ClosingBrace, bool &DiscardAttribute, Optional<bool> &Exported, |
| Optional<SpecializeAttr::SpecializationKind> &Kind, |
| swift::TrailingWhereClause *&TrailingWhereClause) { |
| SyntaxParsingContext ContentContext(SyntaxContext, |
| SyntaxKind::SpecializeAttributeSpecList); |
| // Parse optional "exported" and "kind" labeled parameters. |
| while (!Tok.is(tok::kw_where)) { |
| SyntaxParsingContext ArgumentContext(SyntaxContext, |
| SyntaxKind::LabeledSpecializeEntry); |
| if (Tok.is(tok::identifier)) { |
| auto ParamLabel = Tok.getText(); |
| if (ParamLabel != "exported" && ParamLabel != "kind") { |
| diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name, |
| ParamLabel); |
| } |
| consumeToken(); |
| if (!consumeIf(tok::colon)) { |
| diagnose(Tok.getLoc(), diag::attr_specialize_missing_colon, ParamLabel); |
| skipUntil(tok::comma, tok::kw_where); |
| if (Tok.is(ClosingBrace)) |
| break; |
| if (Tok.is(tok::kw_where)) { |
| continue; |
| } |
| if (Tok.is(tok::comma)) { |
| consumeToken(); |
| continue; |
| } |
| DiscardAttribute = true; |
| return false; |
| } |
| if ((ParamLabel == "exported" && Exported.hasValue()) || |
| (ParamLabel == "kind" && Kind.hasValue())) { |
| diagnose(Tok.getLoc(), diag::attr_specialize_parameter_already_defined, |
| ParamLabel); |
| } |
| if (ParamLabel == "exported") { |
| bool isTrue = consumeIf(tok::kw_true); |
| bool isFalse = consumeIf(tok::kw_false); |
| if (!isTrue && !isFalse) { |
| diagnose(Tok.getLoc(), diag::attr_specialize_expected_bool_value); |
| skipUntil(tok::comma, tok::kw_where); |
| if (Tok.is(ClosingBrace)) |
| break; |
| if (Tok.is(tok::kw_where)) { |
| continue; |
| } |
| if (Tok.is(tok::comma)) { |
| consumeToken(); |
| continue; |
| } |
| DiscardAttribute = true; |
| return false; |
| } |
| if (ParamLabel == "exported") { |
| Exported = isTrue ? true : false; |
| } |
| } |
| if (ParamLabel == "kind") { |
| SourceLoc paramValueLoc; |
| if (Tok.is(tok::identifier)) { |
| if (Tok.getText() == "partial") { |
| Kind = SpecializeAttr::SpecializationKind::Partial; |
| } else if (Tok.getText() == "full") { |
| Kind = SpecializeAttr::SpecializationKind::Full; |
| } else { |
| diagnose(Tok.getLoc(), |
| diag::attr_specialize_expected_partial_or_full); |
| } |
| consumeToken(); |
| } else if (consumeIf(tok::kw_true, paramValueLoc) || |
| consumeIf(tok::kw_false, paramValueLoc)) { |
| diagnose(paramValueLoc, |
| diag::attr_specialize_expected_partial_or_full); |
| } |
| } |
| if (!consumeIf(tok::comma)) { |
| diagnose(Tok.getLoc(), diag::attr_specialize_missing_comma); |
| skipUntil(tok::comma, tok::kw_where); |
| if (Tok.is(ClosingBrace)) |
| break; |
| if (Tok.is(tok::kw_where)) { |
| continue; |
| } |
| if (Tok.is(tok::comma)) { |
| consumeToken(); |
| continue; |
| } |
| DiscardAttribute = true; |
| return false; |
| } |
| continue; |
| } |
| diagnose(Tok.getLoc(), |
| diag::attr_specialize_missing_parameter_label_or_where_clause); |
| DiscardAttribute = true; |
| return false; |
| }; |
| |
| // Parse the where clause. |
| if (Tok.is(tok::kw_where)) { |
| SourceLoc whereLoc; |
| SmallVector<RequirementRepr, 4> requirements; |
| bool firstTypeInComplete; |
| parseGenericWhereClause(whereLoc, requirements, firstTypeInComplete, |
| /* AllowLayoutConstraints */ true); |
| TrailingWhereClause = |
| TrailingWhereClause::create(Context, whereLoc, requirements); |
| } |
| return true; |
| } |
| |
| bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, |
| SourceLoc Loc, SpecializeAttr *&Attr) { |
| assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square); |
| |
| SourceLoc lParenLoc = consumeToken(); |
| bool DiscardAttribute = false; |
| StringRef AttrName = "_specialize"; |
| |
| Optional<bool> exported; |
| Optional<SpecializeAttr::SpecializationKind> kind; |
| |
| TrailingWhereClause *trailingWhereClause = nullptr; |
| |
| if (!parseSpecializeAttributeArguments(ClosingBrace, DiscardAttribute, |
| exported, kind, trailingWhereClause)) { |
| return false; |
| } |
| |
| // Parse the closing ')' or ']'. |
| SourceLoc rParenLoc; |
| if (!consumeIf(ClosingBrace, rParenLoc)) { |
| if (ClosingBrace == tok::r_paren) |
| diagnose(lParenLoc, diag::attr_expected_rparen, AttrName, |
| /*DeclModifier=*/false); |
| else if (ClosingBrace == tok::r_square) |
| diagnose(lParenLoc, diag::attr_expected_rparen, AttrName, |
| /*DeclModifier=*/false); |
| return false; |
| } |
| // Not exported by default. |
| if (!exported.hasValue()) |
| exported = false; |
| // Full specialization by default. |
| if (!kind.hasValue()) |
| kind = SpecializeAttr::SpecializationKind::Full; |
| |
| if (DiscardAttribute) { |
| Attr = nullptr; |
| return false; |
| } |
| // Store the attribute. |
| Attr = SpecializeAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc), |
| trailingWhereClause, exported.getValue(), |
| kind.getValue()); |
| return true; |
| } |
| |
| ParserResult<ImplementsAttr> |
| Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { |
| StringRef AttrName = "_implements"; |
| ParserStatus Status; |
| |
| if (Tok.isNot(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| /*DeclModifier=*/false); |
| Status.setIsParseError(); |
| return Status; |
| } |
| |
| SourceLoc lParenLoc = consumeToken(); |
| |
| DeclNameLoc MemberNameLoc; |
| DeclName MemberName; |
| ParserResult<TypeRepr> ProtocolType; |
| { |
| SyntaxParsingContext ContentContext( |
| SyntaxContext, SyntaxKind::ImplementsAttributeArguments); |
| ProtocolType = parseType(); |
| Status |= ProtocolType; |
| |
| if (!(Status.shouldStopParsing() || consumeIf(tok::comma))) { |
| diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName, |
| /*DeclModifier=*/false); |
| Status.setIsParseError(); |
| } |
| |
| if (!Status.shouldStopParsing()) { |
| MemberName = |
| parseUnqualifiedDeclName(/*afterDot=*/false, MemberNameLoc, |
| diag::attr_implements_expected_member_name, |
| /*allowOperators=*/true, |
| /*allowZeroArgCompoundNames=*/true); |
| if (!MemberName) { |
| Status.setIsParseError(); |
| } |
| } |
| } |
| |
| if (Status.isError()) { |
| skipUntil(tok::r_paren); |
| } |
| |
| SourceLoc rParenLoc; |
| if (!consumeIf(tok::r_paren, rParenLoc)) { |
| diagnose(lParenLoc, diag::attr_expected_rparen, AttrName, |
| /*DeclModifier=*/false); |
| Status.setIsParseError(); |
| } |
| |
| if (Status.isError()) { |
| return Status; |
| } |
| |
| return ParserResult<ImplementsAttr>( |
| ImplementsAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc), |
| ProtocolType.get(), MemberName, MemberNameLoc)); |
| } |
| |
| /// SWIFT_ENABLE_TENSORFLOW |
| ParserResult<DifferentiableAttr> |
| Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { |
| StringRef AttrName = "differentiable"; |
| SourceLoc lParenLoc = loc, rParenLoc = loc; |
| bool linear = false; |
| SmallVector<ParsedAutoDiffParameter, 8> params; |
| Optional<DeclNameWithLoc> jvpSpec; |
| Optional<DeclNameWithLoc> vjpSpec; |
| TrailingWhereClause *whereClause = nullptr; |
| |
| // Parse '('. |
| if (consumeIf(tok::l_paren, lParenLoc)) { |
| // Parse @differentiable attribute arguments. |
| if (parseDifferentiableAttributeArguments(linear, params, jvpSpec, vjpSpec, |
| whereClause)) |
| return makeParserError(); |
| // Parse ')'. |
| if (!consumeIf(tok::r_paren, rParenLoc)) { |
| diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, |
| /*DeclModifier=*/false); |
| return makeParserError(); |
| } |
| } |
| |
| return ParserResult<DifferentiableAttr>( |
| DifferentiableAttr::create(Context, /*implicit*/ false, atLoc, |
| SourceRange(loc, rParenLoc), linear, |
| params, jvpSpec, vjpSpec, whereClause)); |
| } |
| |
| bool Parser::parseDifferentiationParametersClause( |
| SmallVectorImpl<ParsedAutoDiffParameter> ¶ms, StringRef attrName) { |
| // Set parse error, skip until ')' and parse it. |
| auto errorAndSkipToEnd = [&](int parenDepth = 1) -> bool { |
| for (int i = 0; i < parenDepth; i++) { |
| skipUntil(tok::r_paren); |
| if (!consumeIf(tok::r_paren)) |
| diagnose(Tok, diag::attr_expected_rparen, attrName, |
| /*DeclModifier=*/false); |
| } |
| return true; |
| }; |
| |
| SyntaxParsingContext DiffParamsClauseContext( |
| SyntaxContext, SyntaxKind::DifferentiationParamsClause); |
| consumeToken(tok::identifier); |
| if (!consumeIf(tok::colon)) { |
| diagnose(Tok, diag::expected_colon_after_label, "wrt"); |
| return errorAndSkipToEnd(); |
| } |
| |
| // Function that parses a parameter into `params`. Returns true if error |
| // occurred. |
| auto parseParam = [&](bool parseTrailingComma = true) -> bool { |
| SyntaxParsingContext DiffParamContext( |
| SyntaxContext, SyntaxKind::DifferentiationParam); |
| SourceLoc paramLoc; |
| switch (Tok.getKind()) { |
| case tok::identifier: { |
| Identifier paramName; |
| if (parseIdentifier(paramName, paramLoc, |
| diag::diff_params_clause_expected_parameter)) |
| return true; |
| params.push_back(ParsedAutoDiffParameter::getNamedParameter( |
| paramLoc, paramName)); |
| break; |
| } |
| case tok::integer_literal: { |
| unsigned paramNum; |
| if (parseUnsignedInteger( |
| paramNum, paramLoc, |
| diag::diff_params_clause_expected_parameter)) |
| return true; |
| |
| params.push_back(ParsedAutoDiffParameter::getOrderedParameter( |
| paramLoc, paramNum)); |
| break; |
| } |
| case tok::kw_self: { |
| paramLoc = consumeToken(tok::kw_self); |
| params.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); |
| break; |
| } |
| default: |
| diagnose(Tok, diag::diff_params_clause_expected_parameter); |
| return true; |
| } |
| if (parseTrailingComma && Tok.isNot(tok::r_paren)) |
| return parseToken(tok::comma, diag::attr_expected_comma, attrName, |
| /*isDeclModifier=*/false); |
| return false; |
| }; |
| |
| // Parse opening '(' of the parameter list. |
| if (Tok.is(tok::l_paren)) { |
| SyntaxParsingContext DiffParamsContext( |
| SyntaxContext, SyntaxKind::DifferentiationParams); |
| consumeToken(tok::l_paren); |
| // Parse first parameter. At least one is required. |
| if (parseParam()) |
| return errorAndSkipToEnd(2); |
| // Parse remaining parameters until ')'. |
| while (Tok.isNot(tok::r_paren)) |
| if (parseParam()) |
| return errorAndSkipToEnd(2); |
| SyntaxContext->collectNodesInPlace(SyntaxKind::DifferentiationParamList); |
| // Parse closing ')' of the parameter list. |
| consumeToken(tok::r_paren); |
| } |
| // If no opening '(' for parameter list, parse a single parameter. |
| else { |
| if (parseParam(/*parseTrailingComma*/ false)) |
| return errorAndSkipToEnd(); |
| } |
| return false; |
| } |
| |
| bool Parser::parseTransposingParametersClause( |
| SmallVectorImpl<ParsedAutoDiffParameter> ¶ms, StringRef attrName) { |
| // Set parse error, skip until ')' and parse it. |
| auto errorAndSkipToEnd = [&](int parenDepth = 1) -> bool { |
| for (int i = 0; i < parenDepth; i++) { |
| skipUntil(tok::r_paren); |
| if (!consumeIf(tok::r_paren)) |
| diagnose(Tok, diag::attr_expected_rparen, attrName, |
| /*DeclModifier=*/false); |
| } |
| return true; |
| }; |
| |
| SyntaxParsingContext DiffParamsClauseContext( |
| SyntaxContext, SyntaxKind::DifferentiationParamsClause); |
| consumeToken(tok::identifier); |
| if (!consumeIf(tok::colon)) { |
| diagnose(Tok, diag::expected_colon_after_label, "wrt"); |
| return errorAndSkipToEnd(); |
| } |
| |
| // Function that parses a parameter into `params`. Returns true if error |
| // occurred. |
| auto parseParam = [&](bool parseTrailingComma = true) -> bool { |
| SyntaxParsingContext DiffParamContext( |
| SyntaxContext, SyntaxKind::DifferentiationParam); |
| SourceLoc paramLoc; |
| switch (Tok.getKind()) { |
| case tok::integer_literal: { |
| unsigned int paramNum; |
| if (parseUnsignedInteger( |
| paramNum, paramLoc, |
| diag::transposing_params_clause_expected_parameter)) |
| return true; |
| |
| params.push_back( |
| ParsedAutoDiffParameter::getOrderedParameter(paramLoc, paramNum)); |
| break; |
| } |
| case tok::kw_self: { |
| paramLoc = consumeToken(tok::kw_self); |
| params.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); |
| break; |
| } |
| default: |
| diagnose(Tok, diag::transposing_params_clause_expected_parameter); |
| return true; |
| } |
| if (parseTrailingComma && Tok.isNot(tok::r_paren)) |
| return parseToken(tok::comma, diag::attr_expected_comma, attrName, |
| /*isDeclModifier=*/false); |
| return false; |
| }; |
| |
| // Parse opening '(' of the parameter list. |
| if (Tok.is(tok::l_paren)) { |
| SyntaxParsingContext DiffParamsContext( |
| SyntaxContext, SyntaxKind::DifferentiationParams); |
| consumeToken(tok::l_paren); |
| // Parse first parameter. At least one is required. |
| if (parseParam()) |
| return errorAndSkipToEnd(2); |
| // Parse remaining parameters until ')'. |
| while (Tok.isNot(tok::r_paren)) |
| if (parseParam()) |
| return errorAndSkipToEnd(2); |
| SyntaxContext->collectNodesInPlace(SyntaxKind::DifferentiationParamList); |
| // Parse closing ')' of the parameter list. |
| consumeToken(tok::r_paren); |
| } |
| // If no opening '(' for parameter list, parse a single parameter. |
| else { |
| if (parseParam(/*parseTrailingComma*/ false)) |
| return errorAndSkipToEnd(); |
| } |
| return false; |
| } |
| |
| bool Parser::parseDifferentiableAttributeArguments( |
| bool &linear, SmallVectorImpl<ParsedAutoDiffParameter> ¶ms, |
| Optional<DeclNameWithLoc> &jvpSpec, Optional<DeclNameWithLoc> &vjpSpec, |
| TrailingWhereClause *&whereClause) { |
| StringRef AttrName = "differentiable"; |
| |
| // Set parse error, skip until ')' and parse it. |
| auto errorAndSkipToEnd = [&](int parenDepth = 1) -> bool { |
| for (int i = 0; i < parenDepth; i++) { |
| skipUntil(tok::r_paren); |
| if (!consumeIf(tok::r_paren)) |
| diagnose(Tok, diag::attr_expected_rparen, AttrName, |
| /*DeclModifier=*/false); |
| } |
| return true; |
| }; |
| |
| // Parse trailing comma, if it exists, and check for errors. |
| auto consumeIfTrailingComma = [&]() -> bool { |
| if (!consumeIf(tok::comma)) return false; |
| // Diagnose trailing comma before 'where' or ')'. |
| if (Tok.is(tok::kw_where) || Tok.is(tok::r_paren)) { |
| diagnose(Tok, diag::unexpected_separator, ","); |
| return true; |
| } |
| // Check that token after comma is 'wrt:' or a function specifier label. |
| if (!Tok.is(tok::identifier) || !(Tok.getText() == "wrt" || |
| Tok.getText() == "jvp" || |
| Tok.getText() == "vjp")) { |
| diagnose(Tok, diag::attr_differentiable_expected_label); |
| return true; |
| } |
| return false; |
| }; |
| |
| // Store starting parser position. |
| auto startingLoc = Tok.getLoc(); |
| SyntaxParsingContext ContentContext( |
| SyntaxContext, SyntaxKind::DifferentiableAttributeArguments); |
| |
| // Parse optional differentiation parameters. |
| // Parse 'linear' label (optional). |
| linear = false; |
| if (Tok.is(tok::identifier) && Tok.getText() == "linear") { |
| linear = true; |
| consumeToken(tok::identifier); |
| // If no trailing comma or 'where' clause, terminate parsing arguments. |
| if (Tok.isNot(tok::comma) && Tok.isNot(tok::kw_where)) |
| return false; |
| if (consumeIfTrailingComma()) |
| return errorAndSkipToEnd(); |
| } |
| |
| // If 'withRespectTo' is used, make the user change it to 'wrt'. |
| if (Tok.is(tok::identifier) && Tok.getText() == "withRespectTo") { |
| SourceRange withRespectToRange(Tok.getLoc(), peekToken().getLoc()); |
| diagnose(Tok, diag::attr_differentiable_use_wrt_not_withrespectto) |
| .highlight(withRespectToRange) |
| .fixItReplace(withRespectToRange, "wrt:"); |
| return errorAndSkipToEnd(); |
| } |
| if (Tok.is(tok::identifier) && Tok.getText() == "wrt") { |
| if (parseDifferentiationParametersClause(params, AttrName)) |
| return true; |
| // If no trailing comma or 'where' clause, terminate parsing arguments. |
| if (Tok.isNot(tok::comma) && Tok.isNot(tok::kw_where)) |
| return false; |
| if (consumeIfTrailingComma()) |
| return errorAndSkipToEnd(); |
| } |
| |
| // Function that parses a label and a function specifier, e.g. 'vjp: foo(_:)'. |
| // Return true on error. |
| auto parseFuncSpec = [&](StringRef label, DeclNameWithLoc &result, |
| bool &terminateParsingArgs) -> bool { |
| // Parse label. |
| if (parseSpecificIdentifier(label, |
| diag::attr_differentiable_missing_label, label) || |
| parseToken(tok::colon, diag::expected_colon_after_label, label)) |
| return true; |
| // Parse the name of the function. |
| SyntaxParsingContext FuncDeclNameContext( |
| SyntaxContext, SyntaxKind::FunctionDeclName); |
| Diagnostic funcDiag(diag::attr_differentiable_expected_function_name.ID, |
| { label }); |
| result.Name = |
| parseUnqualifiedDeclName(/*afterDot=*/false, result.Loc, |
| funcDiag, /*allowOperators=*/true, |
| /*allowZeroArgCompoundNames=*/true); |
| // If no trailing comma or 'where' clause, terminate parsing arguments. |
| if (Tok.isNot(tok::comma) && Tok.isNot(tok::kw_where)) |
| terminateParsingArgs = true; |
| return !result.Name; |
| }; |
| |
| // Store whether to terminate parsing arguments. |
| bool terminateParsingArgs = false; |
| |
| // Parse 'jvp: <func_name>' (optional). |
| if (Tok.is(tok::identifier) && Tok.getText() == "jvp") { |
| SyntaxParsingContext JvpContext( |
| SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); |
| jvpSpec = DeclNameWithLoc(); |
| if (parseFuncSpec("jvp", *jvpSpec, terminateParsingArgs)) |
| return errorAndSkipToEnd(); |
| if (terminateParsingArgs) |
| return false; |
| if (consumeIfTrailingComma()) |
| return errorAndSkipToEnd(); |
| } |
| |
| // Parse 'vjp: <func_name>' (optional). |
| if (Tok.is(tok::identifier) && Tok.getText() == "vjp") { |
| SyntaxParsingContext VjpContext( |
| SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); |
| vjpSpec = DeclNameWithLoc(); |
| if (parseFuncSpec("vjp", *vjpSpec, terminateParsingArgs)) |
| return errorAndSkipToEnd(); |
| if (terminateParsingArgs) |
| return false; |
| // Note: intentionally parse trailing comma here, even though it's the last |
| // function specifier. `consumeIfTrailingComma` will emit an error. |
| if (consumeIfTrailingComma()) |
| return errorAndSkipToEnd(); |
| } |
| |
| // If parser has not advanced and token is not 'where' or ')', emit error. |
| if (Tok.getLoc() == startingLoc && |
| Tok.isNot(tok::kw_where) && Tok.isNot(tok::r_paren)) { |
| diagnose(Tok, diag::attr_differentiable_expected_label); |
| return errorAndSkipToEnd(); |
| } |
| |
| // Parse a trailing 'where' clause if any. |
| if (Tok.is(tok::kw_where)) { |
| SourceLoc whereLoc; |
| SmallVector<RequirementRepr, 4> requirements; |
| bool firstTypeInComplete; |
| parseGenericWhereClause(whereLoc, requirements, firstTypeInComplete, |
| /*AllowLayoutConstraints*/ true); |
| whereClause = TrailingWhereClause::create(Context, whereLoc, requirements); |
| } |
| return false; |
| } |
| |
| /// SWIFT_ENABLE_TENSORFLOW |
| ParserResult<DifferentiatingAttr> |
| Parser::parseDifferentiatingAttribute(SourceLoc atLoc, SourceLoc loc) { |
| StringRef AttrName = "differentiating"; |
| SourceLoc lParenLoc = loc, rParenLoc = loc; |
| DeclNameWithLoc original; |
| bool linear = false; |
| SmallVector<ParsedAutoDiffParameter, 8> params; |
| |
| // Parse trailing comma, if it exists, and check for errors. |
| auto consumeIfTrailingComma = [&]() -> bool { |
| if (!consumeIf(tok::comma)) return false; |
| // Diagnose trailing comma before ')'. |
| if (Tok.is(tok::r_paren)) { |
| diagnose(Tok, diag::unexpected_separator, ","); |
| return true; |
| } |
| // Check that token after comma is 'linear' or 'wrt:'. |
| if (!Tok.is(tok::identifier) || |
| !(Tok.getText() == "linear" || Tok.getText() == "wrt")) { |
| diagnose(Tok, diag::attr_differentiating_expected_label_linear_or_wrt); |
| return true; |
| } |
| return false; |
| }; |
| |
| // Parse '('. |
| if (!consumeIf(tok::l_paren, lParenLoc)) { |
| diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName, |
| /*DeclModifier*/ false); |
| return makeParserError(); |
| } |
| |
| { |
| SyntaxParsingContext ContentContext( |
| SyntaxContext, SyntaxKind::DifferentiatingAttributeArguments); |
| |
| { |
| // Parse the name of the function. |
| SyntaxParsingContext FuncDeclNameContext( |
| SyntaxContext, SyntaxKind::FunctionDeclName); |
| original.Name = parseUnqualifiedDeclName( |
| /*afterDot*/ false, original.Loc, |
| diag::attr_differentiating_expected_original_name, |
| /*allowOperators*/ true, /*allowZeroArgCompoundNames*/ true); |
| |
| if (consumeIfTrailingComma()) |
| return makeParserError(); |
| } |
| |
| // Parse the optional 'linear' differentiation flag. |
| if (Tok.is(tok::identifier) && Tok.getText() == "linear") { |
| linear = true; |
| consumeToken(tok::identifier); |
| if (consumeIfTrailingComma()) |
| return makeParserError(); |
| } |
| |
| // Parse the optional 'wrt' differentiation parameters clause. |
| if (Tok.is(tok::identifier) && Tok.getText() == "wrt" && |
| parseDifferentiationParametersClause(params, AttrName)) |
| return makeParserError(); |
| } |
| |
| // Parse ')'. |
| if (!consumeIf(tok::r_paren, rParenLoc)) { |
| diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, |
| /*DeclModifier*/ false); |
| return makeParserError(); |
| } |
| return ParserResult<DifferentiatingAttr>( |
| DifferentiatingAttr::create(Context, /*implicit*/ false, atLoc, |
| SourceRange(loc, rParenLoc), |
| original, linear, params)); |
| } |
| |
| /// SWIFT_ENABLE_TENSORFLOW |
| /// Helper function that parses 'type-identifier' for `parseQualifiedDeclName`. |
| /// Returns true on error. Sets `baseType` to the parsed type, if present, or to |
| /// `nullptr` if not. A missing base type is not considered an error. |
| static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { |
| baseType = nullptr; |
| |
| if (!P.canParseTypeQualifierForDeclName()) |
| return false; |
| |
| SourceLoc loc = P.Tok.getLoc(); |
| auto result = P.parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true); |
| if (!result.isSuccess()) |
| return true; |
| |
| // Consume the period. |
| if (P.Tok.is(tok::period) || P.Tok.is(tok::period_prefix)) |
| P.consumeToken(); |
| else if (P.startsWithSymbol(P.Tok, '.')) |
| P.consumeStartingCharacterOfCurrentToken(tok::period); |
| |
| P.SyntaxContext->addSyntax(result.get()); |
| auto node = P.SyntaxContext->topNode<TypeSyntax>(); |
| baseType = P.Generator.generate(node, loc); |
| return false; |
| } |
| |
| /// SWIFT_ENABLE_TENSORFLOW |
| /// parseQualifiedDeclName |
| /// |
| /// qualified-decl-name: |
| /// type-identifier? unqualified-decl-name |
| /// type-identifier: |
| /// identifier generic-args? ('.' identifier generic-args?)* |
| /// |
| /// Parses an optional base type, followed by a declaration name. |
| /// Returns true on error (if function decl name could not be parsed). |
| bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, |
| TypeRepr *&baseType, DeclNameWithLoc &original) { |
| if (parseBaseTypeForQualifiedDeclName(P, baseType)) |
| return true; |
| |
| // If base type was parsed and has at least one component, then there was a |
| // dot before the current token. |
| bool afterDot = false; |
| if (baseType) { |
| if (auto ident = dyn_cast<IdentTypeRepr>(baseType)) { |
| auto components = ident->getComponentRange(); |
| afterDot = std::distance(components.begin(), components.end()) > 0; |
| } |
| } |
| original.Name = |
| P.parseUnqualifiedDeclName(afterDot, original.Loc, nameParseError, |
| /*allowOperators*/ true, |
| /*allowZeroArgCompoundNames*/ true); |
| |
| // The base type is optional, but the final unqualified decl name is not. |
| // If name could not be parsed, return true for error. |
| return !original.Name; |
| } |
| |
| ParserResult<TransposingAttr> Parser::parseTransposingAttribute(SourceLoc atLoc, |
| SourceLoc loc) { |
| StringRef AttrName = "transposing"; |
| SourceLoc lParenLoc = loc, rParenLoc = loc; |
| TypeRepr *baseType; |
| DeclNameWithLoc original; |
| SmallVector<ParsedAutoDiffParameter, 8> params; |
| |
| // Parse trailing comma, if it exists, and check for errors. |
| auto consumeIfTrailingComma = [&]() -> bool { |
| if (!consumeIf(tok::comma)) return false; |
| // Diagnose trailing comma before ')'. |
| if (Tok.is(tok::r_paren)) { |
| diagnose(Tok, diag::unexpected_separator, ","); |
| return true; |
| } |
| // Check that token after comma is 'wrt:'. |
| if (!Tok.is(tok::identifier) || !(Tok.getText() == "wrt")) { |
| diagnose(Tok, diag::attr_transposing_expected_label_linear_or_wrt); |
| return true; |
| } |
| return false; |
| }; |
| |
| // Parse '('. |
| if (!consumeIf(tok::l_paren, lParenLoc)) { |
| diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName, |
| /*DeclModifier*/ false); |
| return makeParserError(); |
| } |
| |
| { |
| SyntaxParsingContext ContentContext( |
| SyntaxContext, SyntaxKind::TransposingAttributeArguments); |
| |
| { |
| // Parse the optionally qualified function. |
| if (parseQualifiedDeclName(*this, |
| diag::attr_transposing_expected_original_name, |
| baseType, original)) |
| return makeParserError(); |
| |
| if (consumeIfTrailingComma()) |
| return makeParserError(); |
| } |
| |
| // Parse the optional 'wrt' differentiation parameters clause. |
| if (Tok.is(tok::identifier) && Tok.getText() == "wrt" && |
| parseTransposingParametersClause(params, AttrName)) |
| return makeParserError(); |
| } |
| |
| // Parse ')'. |
| if (!consumeIf(tok::r_paren, rParenLoc)) { |
| diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, |
| /*DeclModifier*/ false); |
| return makeParserError(); |
| } |
| return ParserResult<TransposingAttr>(TransposingAttr::create( |
| Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, |
| original, params)); |
| } |
| |
| ParserResult<QuotedAttr> Parser::parseQuotedAttribute(SourceLoc atLoc, |
| SourceLoc loc) { |
| if (Context.LangOpts.EnableExperimentalQuasiquotes) { |
| return ParserResult<QuotedAttr>(QuotedAttr::create( |
| Context, atLoc, SourceRange(loc, loc), /*Implicit=*/false)); |
| } else { |
| diagnose(atLoc, diag::attr_quoted_enable_experimental_quasiquotes); |
| return makeParserError(); |
| } |
| } |
| |
| void Parser::parseObjCSelector(SmallVector<Identifier, 4> &Names, |
| SmallVector<SourceLoc, 4> &NameLocs, |
| bool &IsNullarySelector) { |
| IsNullarySelector = true; |
| SyntaxParsingContext SelectorContext(SyntaxContext, SyntaxKind::ObjCSelector); |
| while (true) { |
| SyntaxParsingContext SelectorPieceContext(SyntaxContext, |
| SyntaxKind::ObjCSelectorPiece); |
| // Empty selector piece. |
| if (Tok.is(tok::colon)) { |
| Names.push_back(Identifier()); |
| NameLocs.push_back(Tok.getLoc()); |
| IsNullarySelector = false; |
| consumeToken(); |
| continue; |
| } |
| |
| // Name. |
| if (Tok.is(tok::identifier) || Tok.isKeyword()) { |
| Names.push_back(Context.getIdentifier(Tok.getText())); |
| NameLocs.push_back(Tok.getLoc()); |
| consumeToken(); |
| |
| // If we have a colon, consume it. |
| if (Tok.is(tok::colon)) { |
| consumeToken(); |
| IsNullarySelector = false; |
| continue; |
| } |
| |
| // If we see a closing parentheses, we're done. |
| if (Tok.is(tok::r_paren)) { |
| // If we saw more than one identifier, there's a ':' |
| // missing here. Complain and pretend we saw it. |
| if (Names.size() > 1) { |
| diagnose(Tok, diag::attr_objc_missing_colon) |
| .fixItInsertAfter(NameLocs.back(), ":"); |
| IsNullarySelector = false; |
| } |
| |
| break; |
| } |
| |
| // If we see another identifier or keyword, complain about |
| // the missing colon and keep going. |
| if (Tok.is(tok::identifier) || Tok.isKeyword()) { |
| diagnose(Tok, diag::attr_objc_missing_colon) |
| .fixItInsertAfter(NameLocs.back(), ":"); |
| IsNullarySelector = false; |
| continue; |
| } |
| |
| // We don't know what happened. Break out. |
| break; |
| } |
| |
| // We didn't parse anything, don't create a ObjCSelectorPiece |
| SelectorPieceContext.setTransparent(); |
| break; |
| } |
| } |
| |
| bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, |
| DeclAttrKind DK) { |
| // Ok, it is a valid attribute, eat it, and then process it. |
| StringRef AttrName = Tok.getText(); |
| SourceLoc Loc = consumeToken(); |
| |
| bool DiscardAttribute = false; |
| |
| // Diagnose duplicated attributes. |
| const DeclAttribute *DuplicateAttribute = nullptr; |
| if (!DeclAttribute::allowMultipleAttributes(DK)) |
| if ((DuplicateAttribute = Attributes.getAttribute(DK))) { |
| // Delay issuing the diagnostic until we parse the attribute. |
| DiscardAttribute = true; |
| } |
| |
| // If this is a SIL-only attribute, reject it. |
| if ((DeclAttribute::getOptions(DK) & DeclAttribute::SILOnly) != 0 && |
| !isInSILMode()) { |
| diagnose(Loc, diag::only_allowed_in_sil, AttrName); |
| DiscardAttribute = true; |
| } |
| |
| if (Context.LangOpts.Target.isOSBinFormatCOFF()) { |
| if (DK == DAK_WeakLinked) { |
| diagnose(Loc, diag::attr_unsupported_on_target, AttrName, |
| Context.LangOpts.Target.str()); |
| DiscardAttribute = true; |
| } |
| } |
| |
| // Filled in during parsing. If there is a duplicate |
| // diagnostic this can be used for better error presentation. |
| SourceRange AttrRange; |
| |
| switch (DK) { |
| case DAK_Count: |
| llvm_unreachable("DAK_Count should not appear in parsing switch"); |
| |
| case DAK_RawDocComment: |
| case DAK_ObjCBridged: |
| case DAK_RestatedObjCConformance: |
| case DAK_SynthesizedProtocol: |
| case DAK_ClangImporterSynthesizedType: |
| case DAK_Custom: |
| llvm_unreachable("virtual attributes should not be parsed " |
| "by attribute parsing code"); |
| case DAK_SetterAccess: |
| llvm_unreachable("handled by DAK_AccessControl"); |
| |
| #define SIMPLE_DECL_ATTR(_, CLASS, ...) \ |
| case DAK_##CLASS: \ |
| if (!DiscardAttribute) \ |
| Attributes.add(new (Context) CLASS##Attr(AtLoc, Loc)); \ |
| break; |
| #include "swift/AST/Attr.def" |
| |
| case DAK_Effects: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); return false; |
| } |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Loc, diag::effects_attribute_expect_option, AttrName); |
| return false; |
| } |
| |
| EffectsKind kind; |
| if (Tok.getText() == "readonly") |
| kind = EffectsKind::ReadOnly; |
| else if (Tok.getText() == "readnone") |
| kind = EffectsKind::ReadNone; |
| else if (Tok.getText() == "readwrite") |
| kind = EffectsKind::ReadWrite; |
| else if (Tok.getText() == "releasenone") |
| kind = EffectsKind::ReleaseNone; |
| else { |
| diagnose(Loc, diag::effects_attribute_unknown_option, |
| Tok.getText(), AttrName); |
| return false; |
| } |
| AttrRange = SourceRange(Loc, Tok.getRange().getStart()); |
| consumeToken(tok::identifier); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (!DiscardAttribute) |
| Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, kind)); |
| break; |
| } |
| |
| case DAK_Inline: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Loc, diag::optimization_attribute_expect_option, AttrName, |
| "none"); |
| return false; |
| } |
| |
| InlineKind kind; |
| if (Tok.getText() == "never") |
| kind = InlineKind::Never; |
| else if (Tok.getText() == "__always") |
| kind = InlineKind::Always; |
| else { |
| diagnose(Loc, diag::optimization_attribute_unknown_option, |
| Tok.getText(), AttrName); |
| return false; |
| } |
| consumeToken(tok::identifier); |
| AttrRange = SourceRange(Loc, Tok.getRange().getStart()); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (!DiscardAttribute) |
| Attributes.add(new (Context) InlineAttr(AtLoc, AttrRange, kind)); |
| |
| break; |
| } |
| |
| case DAK_Optimize: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Loc, diag::optimization_attribute_expect_option, AttrName, |
| "speed"); |
| return false; |
| } |
| |
| OptimizationMode optMode = OptimizationMode::NotSet; |
| if (Tok.getText() == "none") |
| optMode = OptimizationMode::NoOptimization; |
| else if (Tok.getText() == "speed") |
| optMode = OptimizationMode::ForSpeed; |
| else if (Tok.getText() == "size") |
| optMode = OptimizationMode::ForSize; |
| else { |
| diagnose(Loc, diag::optimization_attribute_unknown_option, |
| Tok.getText(), AttrName); |
| return false; |
| } |
| consumeToken(tok::identifier); |
| AttrRange = SourceRange(Loc, Tok.getRange().getStart()); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (!DiscardAttribute) |
| Attributes.add(new (Context) OptimizeAttr(AtLoc, AttrRange, optMode)); |
| |
| break; |
| } |
| |
| case DAK_ReferenceOwnership: { |
| // Handle weak/unowned/unowned(unsafe). |
| auto Kind = AttrName == "weak" ? ReferenceOwnership::Weak |
| : ReferenceOwnership::Unowned; |
| SourceLoc EndLoc = Loc; |
| |
| if (Kind == ReferenceOwnership::Unowned && Tok.is(tok::l_paren)) { |
| // Parse an optional specifier after unowned. |
| SourceLoc lp = consumeToken(tok::l_paren); |
| if (Tok.is(tok::identifier) && Tok.getText() == "safe") { |
| consumeToken(); |
| } else if (Tok.is(tok::identifier) && Tok.getText() == "unsafe") { |
| consumeToken(); |
| Kind = ReferenceOwnership::Unmanaged; |
| } else { |
| diagnose(Tok, diag::attr_unowned_invalid_specifier); |
| consumeIf(tok::identifier); |
| } |
| |
| SourceLoc rp; |
| parseMatchingToken(tok::r_paren, rp, diag::attr_unowned_expected_rparen, |
| lp); |
| EndLoc = rp; |
| } |
| |
| if (!DiscardAttribute) |
| Attributes.add( |
| new (Context) ReferenceOwnershipAttr(SourceRange(Loc, EndLoc), Kind)); |
| break; |
| } |
| |
| case DAK_AccessControl: { |
| |
| // Diagnose using access control in a local scope, which isn't meaningful. |
| if (CurDeclContext->isLocalContext()) { |
| diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName); |
| } |
| |
| AccessLevel access = llvm::StringSwitch<AccessLevel>(AttrName) |
| .Case("private", AccessLevel::Private) |
| .Case("fileprivate", AccessLevel::FilePrivate) |
| .Case("internal", AccessLevel::Internal) |
| .Case("public", AccessLevel::Public) |
| .Case("open", AccessLevel::Open); |
| |
| if (!consumeIf(tok::l_paren)) { |
| // Normal access control attribute. |
| AttrRange = Loc; |
| DuplicateAttribute = Attributes.getAttribute<AccessControlAttr>(); |
| if (!DuplicateAttribute) |
| Attributes.add(new (Context) AccessControlAttr(AtLoc, Loc, access)); |
| break; |
| } |
| |
| // Parse the subject. |
| if (Tok.isContextualKeyword("set")) { |
| consumeToken(); |
| } else { |
| diagnose(Loc, diag::attr_access_expected_set, AttrName); |
| // Minimal recovery: if there's a single token and then an r_paren, |
| // consume them both. If there's just an r_paren, consume that. |
| if (!consumeIf(tok::r_paren)) { |
| if (Tok.isNot(tok::l_paren) && peekToken().is(tok::r_paren)) { |
| consumeToken(); |
| consumeToken(tok::r_paren); |
| } |
| } |
| return false; |
| } |
| |
| AttrRange = SourceRange(Loc, Tok.getLoc()); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| DuplicateAttribute = Attributes.getAttribute<SetterAccessAttr>(); |
| if (!DuplicateAttribute) { |
| Attributes.add(new (Context) SetterAccessAttr(AtLoc, AttrRange, access)); |
| } |
| |
| break; |
| } |
| |
| case DAK_CDecl: |
| case DAK_SILGenName: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::string_literal)) { |
| diagnose(Loc, diag::attr_expected_string_literal, AttrName); |
| return false; |
| } |
| |
| Optional<StringRef> AsmName = getStringLiteralIfNotInterpolated( |
| Loc, ("'" + AttrName + "'").str()); |
| |
| consumeToken(tok::string_literal); |
| |
| if (AsmName.hasValue()) |
| AttrRange = SourceRange(Loc, Tok.getRange().getStart()); |
| else |
| DiscardAttribute = true; |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| // Diagnose using @_silgen_name in a local scope. These don't |
| // actually work. |
| if (CurDeclContext->isLocalContext()) { |
| // Emit an error, but do not discard the attribute. This enables |
| // better recovery in the parser. |
| diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName); |
| } |
| |
| if (!DiscardAttribute) { |
| if (DK == DAK_SILGenName) |
| Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc, |
| AttrRange, /*Implicit=*/false)); |
| else if (DK == DAK_CDecl) |
| Attributes.add(new (Context) CDeclAttr(AsmName.getValue(), AtLoc, |
| AttrRange, /*Implicit=*/false)); |
| else |
| llvm_unreachable("out of sync with switch"); |
| } |
| |
| break; |
| } |
| |
| case DAK_Alignment: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::integer_literal)) { |
| diagnose(Loc, diag::alignment_must_be_positive_integer); |
| return false; |
| } |
| |
| StringRef alignmentText = Tok.getText(); |
| unsigned alignmentValue; |
| if (alignmentText.getAsInteger(0, alignmentValue)) { |
| diagnose(Loc, diag::alignment_must_be_positive_integer); |
| return false; |
| } |
| |
| consumeToken(tok::integer_literal); |
| |
| auto range = SourceRange(Loc, Tok.getRange().getStart()); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| Attributes.add(new (Context) AlignmentAttr(alignmentValue, AtLoc, range, |
| /*implicit*/ false)); |
| |
| break; |
| } |
| |
| case DAK_SwiftNativeObjCRuntimeBase: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Loc, diag::swift_native_objc_runtime_base_must_be_identifier); |
| return false; |
| } |
| |
| Identifier name; |
| consumeIdentifier(&name); |
| |
| auto range = SourceRange(Loc, Tok.getRange().getStart()); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| Attributes.add(new (Context) SwiftNativeObjCRuntimeBaseAttr(name, |
| AtLoc, range, /*implicit*/ false)); |
| break; |
| } |
| |
| case DAK_Semantics: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::string_literal)) { |
| diagnose(Loc, diag::attr_expected_string_literal, AttrName); |
| return false; |
| } |
| |
| auto Value = getStringLiteralIfNotInterpolated( |
| Loc, ("'" + AttrName + "'").str()); |
| |
| consumeToken(tok::string_literal); |
| |
| if (Value.hasValue()) |
| AttrRange = SourceRange(Loc, Tok.getRange().getStart()); |
| else |
| DiscardAttribute = true; |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| // Diagnose using @_semantics in a local scope. These don't |
| // actually work. |
| if (CurDeclContext->isLocalContext()) { |
| // Emit an error, but do not discard the attribute. This enables |
| // better recovery in the parser. |
| diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName); |
| } |
| |
| if (!DiscardAttribute) |
| Attributes.add(new (Context) SemanticsAttr(Value.getValue(), AtLoc, |
| AttrRange, |
| /*Implicit=*/false)); |
| break; |
| } |
| |
| case DAK_Available: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| // platform: |
| // * |
| // identifier |
| if (!Tok.is(tok::identifier) && |
| !(Tok.isAnyOperator() && Tok.getText() == "*")) { |
| if (Tok.is(tok::code_complete) && CodeCompletion) { |
| CodeCompletion->completeDeclAttrParam(DAK_Available, 0); |
| consumeToken(tok::code_complete); |
| } |
| diagnose(Tok.getLoc(), diag::attr_availability_platform, AttrName) |
| .highlight(SourceRange(Tok.getLoc())); |
| consumeIf(tok::r_paren); |
| return false; |
| } |
| |
| // Delay processing of platform until later, after we have |
| // parsed more of the attribute. |
| StringRef Platform = Tok.getText(); |
| |
| if (Platform != "*" && |
| peekToken().isAny(tok::integer_literal, tok::floating_literal)) { |
| // We have the short form of available: @available(iOS 8.0.1, *) |
| SmallVector<AvailabilitySpec *, 5> Specs; |
| ParserStatus Status = parseAvailabilitySpecList(Specs); |
| |
| if (Status.isError()) |
| return false; |
| |
| AttrRange = SourceRange(Loc, Tok.getLoc()); |
| // For each platform version spec in the spec list, create an |
| // implicit AvailableAttr for the platform with the introduced |
| // version from the spec. For example, if we have |
| // @available(iOS 8.0, OSX 10.10, *): |
| // we will synthesize: |
| // @available(iOS, introduced: 8.0) |
| // @available(OSX, introduced: 10.10) |
| // |
| // Similarly if we have a language version spec or PackageDescription |
| // version in the spec list, create an implicit AvailableAttr |
| // with the specified version as the introduced argument. |
| // For example, if we have |
| // @available(swift 3.1) |
| // we will synthesize |
| // @available(swift, introduced: 3.1) |
| // or, if we have |
| // @available(_PackageDescription 4.2) |
| // we will synthesize |
| // @available(_PackageDescription, introduced: 4.2) |
| |
| for (auto *Spec : Specs) { |
| PlatformKind Platform; |
| llvm::VersionTuple Version; |
| SourceRange VersionRange; |
| PlatformAgnosticAvailabilityKind PlatformAgnostic; |
| |
| if (auto *PlatformVersionSpec = |
| dyn_cast<PlatformVersionConstraintAvailabilitySpec>(Spec)) { |
| Platform = PlatformVersionSpec->getPlatform(); |
| Version = PlatformVersionSpec->getVersion(); |
| VersionRange = PlatformVersionSpec->getVersionSrcRange(); |
| PlatformAgnostic = PlatformAgnosticAvailabilityKind::None; |
| |
| } else if (auto *PlatformAgnosticVersionSpec = |
| dyn_cast<PlatformAgnosticVersionConstraintAvailabilitySpec>(Spec)) { |
| Platform = PlatformKind::none; |
| Version = PlatformAgnosticVersionSpec->getVersion(); |
| VersionRange = PlatformAgnosticVersionSpec->getVersionSrcRange(); |
| PlatformAgnostic = PlatformAgnosticVersionSpec->isLanguageVersionSpecific() ? |
| PlatformAgnosticAvailabilityKind::SwiftVersionSpecific : |
| PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific; |
| |
| } else { |
| continue; |
| } |
| |
| Attributes.add(new (Context) |
| AvailableAttr(AtLoc, AttrRange, |
| Platform, |
| /*Message=*/StringRef(), |
| /*Rename=*/StringRef(), |
| /*Introduced=*/Version, |
| /*IntroducedRange=*/VersionRange, |
| /*Deprecated=*/llvm::VersionTuple(), |
| /*DeprecatedRange=*/SourceRange(), |
| /*Obsoleted=*/llvm::VersionTuple(), |
| /*ObsoletedRange=*/SourceRange(), |
| PlatformAgnostic, |
| /*Implicit=*/false)); |
| } |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Tok.getLoc(), diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| break; |
| } |
| |
| auto AvailabilityAttr = parseExtendedAvailabilitySpecList(AtLoc, Loc, |
| AttrName); |
| DiscardAttribute |= AvailabilityAttr.isParseError(); |
| |
| if (!consumeIf(tok::r_paren)) { |
| if (!DiscardAttribute) { |
| diagnose(Tok.getLoc(), diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| } |
| return false; |
| } |
| |
| if (!DiscardAttribute) { |
| Attributes.add(AvailabilityAttr.get()); |
| } else { |
| return false; |
| } |
| break; |
| } |
| case DAK_PrivateImport: { |
| // Parse the leading '('. |
| if (Tok.isNot(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| SourceLoc LParenLoc = consumeToken(tok::l_paren); |
| Optional<StringRef> filename; |
| { |
| SyntaxParsingContext ContentContext( |
| SyntaxContext, SyntaxKind::NamedAttributeStringArgument); |
| |
| // Parse 'sourceFile'. |
| if (Tok.getText() != "sourceFile") { |
| diagnose(LParenLoc, diag::attr_private_import_expected_sourcefile); |
| return false; |
| } |
| auto ForLoc = consumeToken(); |
| |
| // Parse ':'. |
| if (Tok.getKind() != tok::colon) { |
| diagnose(ForLoc, diag::attr_private_import_expected_colon); |
| return false; |
| } |
| auto ColonLoc = consumeToken(tok::colon); |
| |
| // Parse '"'function-name'"' |
| if (Tok.isNot(tok::string_literal)) { |
| diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name); |
| return false; |
| } |
| filename = getStringLiteralIfNotInterpolated(Loc, "_private"); |
| if (!filename.hasValue()) { |
| diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name); |
| return false; |
| } |
| consumeToken(tok::string_literal); |
| } |
| // Parse the matching ')'. |
| SourceLoc RParenLoc; |
| bool Invalid = parseMatchingToken(tok::r_paren, RParenLoc, |
| diag::attr_private_import_expected_rparen, |
| LParenLoc); |
| if (Invalid) |
| return false; |
| auto *attr = PrivateImportAttr::create(Context, AtLoc, Loc, LParenLoc, |
| *filename, RParenLoc); |
| Attributes.add(attr); |
| |
| break; |
| } |
| case DAK_ObjC: { |
| // Unnamed @objc attribute. |
| if (Tok.isNot(tok::l_paren)) { |
| auto attr = ObjCAttr::createUnnamed(Context, AtLoc, Loc); |
| Attributes.add(attr); |
| break; |
| } |
| |
| // Parse the leading '('. |
| SourceLoc LParenLoc = consumeToken(tok::l_paren); |
| |
| // Parse the names, with trailing colons (if there are present) and populate |
| // the inout parameters |
| SmallVector<Identifier, 4> Names; |
| SmallVector<SourceLoc, 4> NameLocs; |
| bool NullarySelector = true; |
| parseObjCSelector(Names, NameLocs, NullarySelector); |
| |
| // Parse the matching ')'. |
| SourceLoc RParenLoc; |
| bool Invalid = parseMatchingToken(tok::r_paren, RParenLoc, |
| diag::attr_objc_expected_rparen, |
| LParenLoc); |
| |
| ObjCAttr *attr; |
| if (Names.empty()) { |
| // When there are no names, recover as if there were no parentheses. |
| if (!Invalid) |
| diagnose(LParenLoc, diag::attr_objc_empty_name); |
| attr = ObjCAttr::createUnnamed(Context, AtLoc, Loc); |
| } else if (NullarySelector) { |
| // When we didn't see a colon, this is a nullary name. |
| assert(Names.size() == 1 && "Forgot to set sawColon?"); |
| attr = ObjCAttr::createNullary(Context, AtLoc, Loc, LParenLoc, |
| NameLocs.front(), Names.front(), |
| RParenLoc); |
| } else { |
| // When we did see a colon, this is a selector. |
| attr = ObjCAttr::createSelector(Context, AtLoc, Loc, LParenLoc, |
| NameLocs, Names, RParenLoc); |
| } |
| Attributes.add(attr); |
| break; |
| } |
| case DAK_ObjCRuntimeName: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Loc, diag::objc_runtime_name_must_be_identifier); |
| return false; |
| } |
| |
| auto name = Tok.getText(); |
| |
| consumeToken(tok::identifier); |
| |
| auto range = SourceRange(Loc, Tok.getRange().getStart()); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| Attributes.add(new (Context) ObjCRuntimeNameAttr(name, AtLoc, range, |
| /*implicit*/ false)); |
| break; |
| } |
| |
| |
| case DAK_DynamicReplacement: { |
| // Parse the leading '('. |
| if (Tok.isNot(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| SourceLoc LParenLoc = consumeToken(tok::l_paren); |
| DeclName replacedFunction; |
| { |
| SyntaxParsingContext ContentContext( |
| SyntaxContext, SyntaxKind::NamedAttributeStringArgument); |
| |
| // Parse 'for'. |
| if (Tok.getText() != "for") { |
| diagnose(Loc, diag::attr_dynamic_replacement_expected_for); |
| return false; |
| } |
| auto ForLoc = consumeToken(); |
| |
| // Parse ':'. |
| if (Tok.getText() != ":") { |
| diagnose(ForLoc, diag::attr_dynamic_replacement_expected_colon); |
| return false; |
| } |
| consumeToken(tok::colon); |
| { |
| SyntaxParsingContext ContentContext(SyntaxContext, |
| SyntaxKind::DeclName); |
| |
| DeclNameLoc loc; |
| replacedFunction = parseUnqualifiedDeclName( |
| true, loc, diag::attr_dynamic_replacement_expected_function, |
| /*allowOperators*/ true, /*allowZeroArgCompoundNames*/ true, |
| /*allowDeinitAndSubscript*/ true); |
| } |
| } |
| |
| // Parse the matching ')'. |
| SourceLoc RParenLoc; |
| bool Invalid = parseMatchingToken( |
| tok::r_paren, RParenLoc, diag::attr_dynamic_replacement_expected_rparen, |
| LParenLoc); |
| if (Invalid) { |
| return false; |
| } |
| |
| |
| DynamicReplacementAttr *attr = DynamicReplacementAttr::create( |
| Context, AtLoc, Loc, LParenLoc, replacedFunction, RParenLoc); |
| Attributes.add(attr); |
| break; |
| } |
| |
| case DAK_Specialize: { |
| if (Tok.isNot(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| SpecializeAttr *Attr; |
| if (!parseSpecializeAttribute(tok::r_paren, AtLoc, Loc, Attr)) |
| return false; |
| |
| Attributes.add(Attr); |
| break; |
| } |
| |
| case DAK_Implements: { |
| ParserResult<ImplementsAttr> Attr = parseImplementsAttribute(AtLoc, Loc); |
| if (Attr.isNonNull()) { |
| Attributes.add(Attr.get()); |
| } |
| break; |
| } |
| |
| // SWIFT_ENABLE_TENSORFLOW |
| case DAK_Differentiable: { |
| auto Attr = parseDifferentiableAttribute(AtLoc, Loc); |
| if (Attr.isNonNull()) |
| Attributes.add(Attr.get()); |
| break; |
| } |
| |
| // SWIFT_ENABLE_TENSORFLOW |
| case DAK_Differentiating: { |
| auto Attr = parseDifferentiatingAttribute(AtLoc, Loc); |
| if (Attr.isNonNull()) |
| Attributes.add(Attr.get()); |
| break; |
| } |
| |
| // SWIFT_ENABLE_TENSORFLOW |
| case DAK_Transposing: { |
| auto Attr = parseTransposingAttribute(AtLoc, Loc); |
| if (Attr.isNonNull()) |
| Attributes.add(Attr.get()); |
| break; |
| } |
| |
| case DAK_ProjectedValueProperty: { |
| if (!consumeIf(tok::l_paren)) { |
| diagnose(Loc, diag::attr_expected_lparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Loc, diag::projection_value_property_not_identifier); |
| return false; |
| } |
| |
| Identifier name; |
| consumeIdentifier(&name, /*allowDollarIdentifier=*/true); |
| |
| auto range = SourceRange(Loc, Tok.getRange().getStart()); |
| |
| if (!consumeIf(tok::r_paren)) { |
| diagnose(Loc, diag::attr_expected_rparen, AttrName, |
| DeclAttribute::isDeclModifier(DK)); |
| return false; |
| } |
| |
| Attributes.add(new (Context) ProjectedValuePropertyAttr( |
| name, AtLoc, range, /*implicit*/ false)); |
| break; |
| } |
| |
| case DAK_Quoted: { |
| auto Attr = parseQuotedAttribute(AtLoc, Loc); |
| if (Attr.isNonNull()) |
| Attributes.add(Attr.get()); |
| break; |
| } |
| } |
| |
| if (DuplicateAttribute) { |
| diagnose(Loc, diag::duplicate_attribute, DeclAttribute::isDeclModifier(DK)) |
| .highlight(AttrRange); |
| diagnose(DuplicateAttribute->getLocation(), |
| diag::previous_attribute, |
| DeclAttribute::isDeclModifier(DK)) |
| .highlight(DuplicateAttribute->getRange()); |
| } |
| |
| // If this is a decl modifier spelled with an @, emit an error and remove it |
| // with a fixit. |
| if (AtLoc.isValid() && DeclAttribute::isDeclModifier(DK)) |
| diagnose(AtLoc, diag::cskeyword_not_attribute, AttrName).fixItRemove(AtLoc); |
| |
| return false; |
| } |
| |
| bool Parser::parseVersionTuple(llvm::VersionTuple &Version, |
| SourceRange &Range, |
| const Diagnostic &D) { |
| SyntaxParsingContext VersionContext(SyntaxContext, SyntaxKind::VersionTuple); |
| // A version number is either an integer (8), a float (8.1), or a |
| // float followed by a dot and an integer (8.1.0). |
| if (!Tok.isAny(tok::integer_literal, tok::floating_literal)) { |
| diagnose(Tok, D); |
| return true; |
| } |
| |
| SourceLoc StartLoc = Tok.getLoc(); |
| |
| if (Tok.is(tok::integer_literal)) { |
| unsigned major = 0; |
| if (Tok.getText().getAsInteger(10, major)) { |
| // Maybe the literal was in hex. Reject that. |
| diagnose(Tok, D); |
| consumeToken(); |
| return true; |
| } |
| Version = llvm::VersionTuple(major); |
| Range = SourceRange(StartLoc, Tok.getLoc()); |
| consumeToken(); |
| return false; |
| } |
| |
| unsigned major = 0, minor = 0; |
| StringRef majorPart, minorPart; |
| std::tie(majorPart, minorPart) = Tok.getText().split('.'); |
| if (majorPart.getAsInteger(10, major) || minorPart.getAsInteger(10, minor)) { |
| // Reject things like 0.1e5 and hex literals. |
| diagnose(Tok, D); |
| consumeToken(); |
| return true; |
| } |
| |
| Range = SourceRange(StartLoc, Tok.getLoc()); |
| consumeToken(); |
| |
| if (consumeIf(tok::period)) { |
| unsigned micro = 0; |
| if (!Tok.is(tok::integer_literal) || |
| Tok.getText().getAsInteger(10, micro)) { |
| // Reject things like 0.1e5 and hex literals. |
| diagnose(Tok, D); |
| if (Tok.is(tok::integer_literal) || |
| peekToken().isAny(tok::r_paren, tok::comma)) |
| consumeToken(); |
| return true; |
| } |
| |
| Range = SourceRange(StartLoc, Tok.getLoc()); |
| consumeToken(); |
| |
| Version = llvm::VersionTuple(major, minor, micro); |
| } else { |
| Version = llvm::VersionTuple(major, minor); |
| } |
| |
| return false; |
| } |
| |
| /// Check whether the attributes have already established an initializer |
| /// context within the given set of attributes. |
| static PatternBindingInitializer *findAttributeInitContent( |
| DeclAttributes &Attributes) { |
| for (auto custom : Attributes.getAttributes<CustomAttr>()) { |
| if (auto initContext = custom->getInitContext()) |
| return initContext; |
| } |
| |
| return nullptr; |
| } |
| |
| /// \verbatim |
| /// attribute: |
| /// '_silgen_name' '(' identifier ')' |
| /// 'semantics' '(' identifier ')' |
| /// 'infix' '=' numeric_constant |
| /// 'unary' |
| /// 'stdlib' |
| /// 'weak' |
| /// 'inout' |
| /// 'unowned' |
| /// 'unowned' '(' 'safe' ')' |
| /// 'unowned' '(' 'unsafe' ')' |
| /// 'noreturn' |
| /// 'optional' |
| /// 'mutating' |
| /// ( 'private' | 'internal' | 'public' ) |
| /// ( 'private' | 'internal' | 'public' ) '(' 'set' ')' |
| /// 'requires_stored_property_inits' |
| /// \endverbatim |
| /// |
| /// Note that various attributes (like mutating, weak, and unowned) are parsed |
| /// but rejected since they have context-sensitive keywords. |
| /// |
| ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc) { |
| // If this not an identifier, the attribute is malformed. |
| if (Tok.isNot(tok::identifier) && |
| Tok.isNot(tok::kw_in) && |
| Tok.isNot(tok::kw_inout)) { |
| |
| if (Tok.is(tok::code_complete)) { |
| if (CodeCompletion) { |
| // If the next token is not on the same line, this attribute might be |
| // starting new declaration instead of adding attribute to existing |
| // decl. |
| auto isIndependent = peekToken().isAtStartOfLine(); |
| CodeCompletion->completeDeclAttrBeginning(isInSILMode(), isIndependent); |
| } |
| consumeToken(tok::code_complete); |
| return makeParserCodeCompletionStatus(); |
| } else { |
| // Synthesize an r_brace syntax node if the token is absent |
| SyntaxContext->synthesize(tok::identifier, AtLoc.getAdvancedLoc(1)); |
| } |
| |
| diagnose(Tok, diag::expected_attribute_name); |
| return makeParserError(); |
| } |
| |
| // If the attribute follows the new representation, switch |
| // over to the alternate parsing path. |
| DeclAttrKind DK = DeclAttribute::getAttrKindFromString(Tok.getText()); |
| |
| auto checkInvalidAttrName = [&](StringRef invalidName, |
| StringRef correctName, |
| DeclAttrKind kind, |
| Optional<Diag<StringRef, StringRef>> diag = None) { |
| if (DK == DAK_Count && Tok.getText() == invalidName) { |
| DK = kind; |
| |
| if (diag) { |
| diagnose(Tok, *diag, invalidName, correctName) |
| .fixItReplace(Tok.getLoc(), correctName); |
| } |
| } |
| }; |
| |
| // Check if attr is availability, and suggest available instead |
| checkInvalidAttrName("availability", "available", DAK_Available, diag::attr_renamed); |
| |
| // Check if attr is inlineable, and suggest inlinable instead |
| checkInvalidAttrName("inlineable", "inlinable", DAK_Inlinable, diag::attr_name_close_match); |
| |
| // In Swift 5 and above, these become hard errors. In Swift 4.2, emit a |
| // warning for compatibility. Otherwise, don't diagnose at all. |
| if (Context.isSwiftVersionAtLeast(5)) { |
| checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline, diag::attr_renamed); |
| checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable, diag::attr_renamed); |
| } else if (Context.isSwiftVersionAtLeast(4, 2)) { |
| checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline, diag::attr_renamed_warning); |
| checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable, diag::attr_renamed_warning); |
| } else { |
| checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline); |
| checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable); |
| } |
| |
| // Other names of property wrappers... |
| checkInvalidAttrName("propertyDelegate", "propertyWrapper", |
| DAK_PropertyWrapper, diag::attr_renamed_warning); |
| checkInvalidAttrName("_propertyWrapper", "propertyWrapper", |
| DAK_PropertyWrapper, diag::attr_renamed_warning); |
| |
| if (DK == DAK_Count && Tok.getText() == "warn_unused_result") { |
| // The behavior created by @warn_unused_result is now the default. Emit a |
| // Fix-It to remove. |
| SourceLoc attrLoc = consumeToken(); |
| |
| // @warn_unused_result with no arguments. |
| if (Tok.isNot(tok::l_paren)) { |
| diagnose(AtLoc, diag::attr_warn_unused_result_removed) |
| .fixItRemove(SourceRange(AtLoc, attrLoc)); |
| |
| // Recovered. |
| return makeParserSuccess(); |
| } |
| |
| // @warn_unused_result with arguments. |
| SourceLoc lParenLoc = consumeToken(); |
| skipUntil(tok::r_paren); |
| |
| // Parse the closing ')'. |
| SourceLoc rParenLoc; |
| if (Tok.isNot(tok::r_paren)) { |
| parseMatchingToken(tok::r_paren, rParenLoc, |
| diag::attr_warn_unused_result_expected_rparen, |
| lParenLoc); |
| } |
| if (Tok.is(tok::r_paren)) { |
| rParenLoc = consumeToken(); |
| } |
| |
| diagnose(AtLoc, diag::attr_warn_unused_result_removed) |
| .fixItRemove(SourceRange(AtLoc, rParenLoc)); |
| |
| // Recovered. |
| return makeParserSuccess(); |
| } |
| |
| if (DK != DAK_Count && !DeclAttribute::shouldBeRejectedByParser(DK)) { |
| parseNewDeclAttribute(Attributes, AtLoc, DK); |
| return makeParserSuccess(); |
| } |
| |
| if (TypeAttributes::getAttrKindFromString(Tok.getText()) != TAK_Count) |
| diagnose(Tok, diag::type_attribute_applied_to_decl); |
| else if (Tok.isContextualKeyword("unknown")) { |
| diagnose(Tok, diag::unknown_attribute, "unknown"); |
| } else { |
| // Change the context to create a custom attribute syntax. |
| SyntaxContext->setCreateSyntax(SyntaxKind::CustomAttribute); |
| // Parse a custom attribute. |
| auto type = parseType(diag::expected_type); |
| if (type.hasCodeCompletion() || type.isNull()) { |
| if (Tok.is(tok::l_paren)) |
| skipSingle(); |
| |
| return ParserStatus(type); |
| } |
| |
| // Parse the optional arguments. |
| SourceLoc lParenLoc, rParenLoc; |
| SmallVector<Expr *, 2> args; |
| SmallVector<Identifier, 2> argLabels; |
| SmallVector<SourceLoc, 2> argLabelLocs; |
| Expr *trailingClosure = nullptr; |
| bool hasInitializer = false; |
| ParserStatus status; |
| |
| // If we're not in a local context, we'll need a context to parse |
| // initializers into (should we have one). This happens for properties |
| // and global variables in libraries. |
| PatternBindingInitializer *initContext = nullptr; |
| |
| if (Tok.isFollowingLParen()) { |
| if (peekToken().is(tok::code_complete)) { |
| consumeToken(tok::l_paren); |
| if (CodeCompletion) { |
| auto typeE = new (Context) TypeExpr(type.get()); |
| auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); |
| CodeCompletion->completePostfixExprParen(typeE, CCE); |
| } |
| consumeToken(tok::code_complete); |
| skipUntil(tok::r_paren); |
| consumeIf(tok::r_paren); |
| status.setHasCodeCompletion(); |
| } else { |
| // If we have no local context to parse the initial value into, create |
| // one for the PBD we'll eventually create. This allows us to have |
| // reasonable DeclContexts for any closures that may live inside of |
| // initializers. |
| Optional<ParseFunctionBody> initParser; |
| if (!CurDeclContext->isLocalContext()) { |
| initContext = findAttributeInitContent(Attributes); |
| if (!initContext) |
| initContext = |
| new (Context) PatternBindingInitializer(CurDeclContext); |
| |
| initParser.emplace(*this, initContext); |
| } |
| status |= parseExprList(tok::l_paren, tok::r_paren, |
| /*isPostfix=*/false, /*isExprBasic=*/true, |
| lParenLoc, args, argLabels, argLabelLocs, |
| rParenLoc, trailingClosure); |
| assert(!trailingClosure && "Cannot parse a trailing closure here"); |
| hasInitializer = true; |
| } |
| } |
| |
| // Form the attribute. |
| auto attr = CustomAttr::create(Context, AtLoc, type.get(), hasInitializer, |
| initContext, lParenLoc, args, argLabels, |
| argLabelLocs, rParenLoc); |
| Attributes.add(attr); |
| return status; |
| } |
| |
| // Recover by eating @foo(...) when foo is not known. |
| consumeToken(); |
| if (Tok.is(tok::l_paren)) |
| skipSingle(); |
| |
| return makeParserError(); |
| } |
| |
| /// Parses specifier and attributes for types. |
| /// |
| /// specifier: |
| /// 'inout' |
| /// '__shared' |
| /// '__owned' |
| /// |
| /// attribute-list: |
| /// attribute-type (',' attribute-type)? |
| ParserStatus Parser::parseTypeAttributeListSyntax( |
| Optional<ParsedTokenSyntax> &specifier, |
| Optional<ParsedAttributeListSyntax> &attrs) { |
| // Parser a specifier. |
| while (Tok.is(tok::kw_inout) || |
| (Tok.is(tok::identifier) && |
| (Tok.getRawText().equals("__shared") || |
| Tok.getRawText().equals("__owned")))) { |
| if (specifier) { |
| diagnose(Tok, diag::parameter_specifier_repeated) |
| .fixItRemove(Tok.getLoc()); |
| ignoreToken(); |
| } else { |
| specifier = consumeTokenSyntax(); |
| } |
| } |
| |
| ParserStatus status; |
| SmallVector<ParsedSyntax, 2> attrsList; |
| while (Tok.is(tok::at_sign) && status.isSuccess()) { |
| auto attr = parseTypeAttributeSyntax(); |
| status |= attr.getStatus(); |
| if (!attr.isNull()) |
| attrsList.emplace_back(attr.get()); |
| } |
| if (!attrsList.empty()) |
| attrs = ParsedSyntaxRecorder::makeAttributeList(attrsList, *SyntaxContext); |
| |
| return status; |
| } |
| |
| bool Parser::parseTypeAttributeList(ParamDecl::Specifier &Specifier, |
| SourceLoc &SpecifierLoc, |
| TypeAttributes &Attributes) { |
| SourceLoc leadingLoc = leadingTriviaLoc(); |
| Optional<ParsedTokenSyntax> parsedSpecifier; |
| Optional<ParsedAttributeListSyntax> parsedAttrs; |
| auto status = parseTypeAttributeListSyntax(parsedSpecifier, parsedAttrs); |
| |
| if (parsedSpecifier) { |
| SyntaxContext->addSyntax(std::move(*parsedSpecifier)); |
| auto tok = SyntaxContext->topNode<TokenSyntax>(); |
| Specifier = llvm::StringSwitch<ParamDecl::Specifier>(Tok.getText()) |
| .Case("inout", ParamDecl::Specifier::InOut) |
| .Case("__shared", ParamDecl::Specifier::Shared) |
| .Case("__owned", ParamDecl::Specifier::Owned) |
| .Default(ParamDecl::Specifier::Default); |
| SpecifierLoc = Generator.generate(tok, leadingLoc); |
| |
| leadingLoc = leadingLoc.getAdvancedLoc(tok.getTextLength()); |
| } |
| |
| if (parsedAttrs) { |
| SyntaxContext->addSyntax(std::move(*parsedAttrs)); |
| auto syntax = SyntaxContext->topNode<AttributeListSyntax>(); |
| Attributes = Generator.generateTypeAttributes(syntax, leadingLoc); |
| } |
| return status.isError(); |
| } |
| |
| /// Parses an attribute for types. |
| /// |
| /// attribute-type: |
| /// '@' identifier |
| /// '@' 'convention' '(' identifier ')' |
| /// '@' 'convention' '(' 'witness_method' ':' identifier ')' |
| /// '@' 'opened' '(' string-literal ')' |
| /// '@' '_opaqureResultTypeOf' '(' string-literal ',' integer-literal ')' |
| ParsedSyntaxResult<ParsedAttributeSyntax> Parser::parseTypeAttributeSyntax() { |
| ParsedAttributeSyntaxBuilder builder(*SyntaxContext); |
| ParserStatus status; |
| |
| // Parse '@'. |
| auto atLoc = Tok.getLoc(); |
| builder.useAtSignToken(consumeTokenSyntax(tok::at_sign)); |
| |
| // Parse attribute name. |
| if (Tok.isNot(tok::identifier, tok::kw_in, tok::kw_inout)) { |
| diagnose(Tok, diag::expected_attribute_name); |
| if (Tok.is(tok::code_complete)) { |
| // TODO: Implement type attribute completion. |
| } |
| return makeParsedError(builder.build()); |
| } |
| StringRef attrName = Tok.getText(); |
| builder.useAttributeName(consumeTokenSyntax()); |
| |
| TypeAttrKind attr = TypeAttributes::getAttrKindFromString(attrName); |
| switch (attr) { |
| case TAK_out: |
| case TAK_in: |
| case TAK_owned: |
| case TAK_unowned_inner_pointer: |
| case TAK_guaranteed: |
| case TAK_autoreleased: |
| case TAK_callee_owned: |
| case TAK_callee_guaranteed: |
| case TAK_objc_metatype: |
| case TAK_sil_weak: |
| case TAK_sil_unowned: |
| if (!isInSILMode()) { |
| diagnose(atLoc, diag::only_allowed_in_sil, attrName); |
| status.setIsParseError(); |
| } |
| break; |
| |
| case TAK_Count: { |
| auto declAttrID = DeclAttribute::getAttrKindFromString(attrName); |
| if (declAttrID == DAK_Count) { |
| // Not a decl or type attribute. |
| diagnose(Tok, diag::unknown_attribute, attrName); |
| } else { |
| // Otherwise this is a valid decl attribute so they should have put it on |
| // the decl instead of the type. |
| diagnose(Tok, diag::decl_attribute_applied_to_type); |
| } |
| // Recover by eating @foo(...) when foo is not known. |
| if (Tok.is(tok::l_paren) && getEndOfPreviousLoc() == Tok.getLoc()) { |
| BacktrackingScope backtrack(*this); |
| auto lParen = consumeTokenSyntax(tok::l_paren); |
| ignoreUntil(tok::r_paren); |
| auto rParen = consumeTokenSyntaxIf(tok::r_paren); |
| |
| // If we found '->', or 'throws' after paren, it's likely a parameter |
| // of function type. |
| if (Tok.isNot(tok::arrow, tok::kw_throws, tok::kw_rethrows, |
| tok::kw_throw)) { |
| backtrack.cancelBacktrack(); |
| builder.useLeftParen(std::move(lParen)); |
| if (rParen) |
| builder.useRightParen(std::move(*rParen)); |
| } |
| } |
| status.setIsParseError(); |
| break; |
| } |
| |
| // SWIFT_ENABLE_TENSORFLOW |
| case TAK_differentiable: |
| status |= [&]() -> ParserStatus { |
| // Check if there is a 'linear' argument. |
| // If next tokens are not `'(' identifier`, break early. |
| if (!Tok.is(tok::l_paren) || !peekToken().is(tok::identifier)) |
| return makeParserSuccess(); |
| |
| Parser::BacktrackingScope backtrack(*this); |
| SourceLoc lParenLoc = Tok.getLoc(); |
| auto lParen = consumeTokenSyntax(tok::l_paren); |
| |
| // Determine if we have '@differentiable(linear) (T) -> U' |
| // or '@differentiable (linear) -> U'. |
| if (Tok.getText() == "linear") { |
| auto linearIdentifier = consumeTokenSyntax(tok::identifier); |
| if (Tok.is(tok::r_paren) && |
| peekToken().isAny(tok::l_paren, tok::at_sign, tok::identifier)) { |
| // It is being used as an attribute argument, so cancel backtrack |
| // as function is linear differentiable. |
| backtrack.cancelBacktrack(); |
| builder.useLeftParen(std::move(lParen)); |
| builder.useArgument(std::move(linearIdentifier)); |
| SourceLoc rParenLoc; |
| auto rParen = parseMatchingTokenSyntax( |
| tok::r_paren, diag::differentiable_attribute_expected_rparen, lParenLoc); |
| if (rParen.isError()) |
| return makeParserError(); |
| builder.useRightParen(rParen.get()); |
| } else if (Tok.is(tok::l_paren)) { |
| // Handle invalid '@differentiable(linear (T) -> U' |
| diagnose(Tok, diag::differentiable_attribute_expected_rparen); |
| backtrack.cancelBacktrack(); |
| builder.useLeftParen(std::move(lParen)); |
| builder.useArgument(std::move(linearIdentifier)); |
| return makeParserError(); |
| } |
| } else if (Tok.is(tok::identifier)) { |
| // No 'linear' arg or param type, but now checking if the token is being |
| // passed in as an invalid argument to '@differentiable'. |
| auto possibleArg = Tok.getText(); |
| auto t = Tok; // get ref to the argument for clearer diagnostics. |
| auto argIdentifier = consumeTokenSyntax(tok::identifier); |
| // Check if there is an invalid argument getting passed into |
| // '@differentiable'. |
| if (Tok.is(tok::r_paren) && peekToken().is(tok::l_paren)) { |
| // Handling '@differentiable(wrong) (...'. |
| diagnose(t, diag::unexpected_argument_differentiable, possibleArg); |
| auto rParen = consumeTokenSyntax(tok::r_paren); |
| backtrack.cancelBacktrack(); |
| builder.useLeftParen(std::move(lParen)); |
| builder.useArgument(std::move(argIdentifier)); |
| builder.useRightParen(std::move(rParen)); |
| return makeParserError(); |
| } |
| } |
| return makeParserSuccess(); |
| }(); |
| break; |
| |
| case TAK_convention: |
| status |= [&]() -> ParserStatus { |
| // Parse '('. |
| if (!Tok.is(tok::l_paren) || Tok.isAtStartOfLine()) { |
| diagnose(Tok, diag::convention_attribute_expected_lparen); |
| return makeParserError(); |
| } |
| SourceLoc LParenLoc = Tok.getLoc(); |
| builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); |
| |
| // Parse convention name. |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Tok, diag::convention_attribute_expected_name); |
| return makeParserError(); |
| } |
| auto conventionName = Tok.getText(); |
| auto convention = consumeTokenSyntax(tok::identifier); |
| |
| // Parse convention name. |
| if (conventionName == "witness_method") { |
| ParsedNamedAttributeStringArgumentSyntaxBuilder argBuilder( |
| *SyntaxContext); |
| argBuilder.useNameTok(std::move(convention)); |
| |
| // Parse ':'. |
| if (Tok.isNot(tok::colon)) { |
| diagnose(Tok, |
| diag::convention_attribute_witness_method_expected_colon); |
| builder.useArgument(argBuilder.build()); |
| return makeParserError(); |
| } |
| argBuilder.useColon(consumeTokenSyntax(tok::colon)); |
| |
| // Parse protocol name. |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Tok, |
| diag::convention_attribute_witness_method_expected_protocol); |
| builder.useArgument(argBuilder.build()); |
| return makeParserError(); |
| } |
| auto name = ParsedSyntaxRecorder::makeDeclName( |
| consumeTokenSyntax(tok::identifier), None, *SyntaxContext); |
| argBuilder.useStringOrDeclname(std::move(name)); |
| builder.useArgument(argBuilder.build()); |
| } else { |
| builder.useArgument(std::move(convention)); |
| } |
| |
| // Parse ')'. |
| auto RParen = parseMatchingTokenSyntax( |
| tok::r_paren, diag::convention_attribute_expected_rparen, LParenLoc); |
| if (RParen.isError()) |
| return makeParserError(); |
| builder.useRightParen(RParen.get()); |
| |
| return makeParserSuccess(); |
| }(); |
| break; |
| |
| case TAK_opened: |
| status |= [&]() -> ParserStatus { |
| if (!isInSILMode()) { |
| diagnose(atLoc, diag::only_allowed_in_sil, "opened"); |
| return makeParserError(); |
| } |
| |
| // Parse '('. |
| if (!Tok.is(tok::l_paren) || Tok.isAtStartOfLine()) { |
| diagnose(Tok, diag::opened_attribute_expected_lparen); |
| return makeParserError(); |
| } |
| SourceLoc LParenLoc = Tok.getLoc(); |
| builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); |
| |
| if (!Tok.is(tok::string_literal)) { |
| diagnose(Tok, diag::opened_attribute_id_value); |
| return makeParserError(); |
| } |
| builder.useArgument(consumeTokenSyntax(tok::string_literal)); |
| |
| // Parse ')'. |
| auto RParen = parseMatchingTokenSyntax( |
| tok::r_paren, diag::opened_attribute_expected_rparen, LParenLoc); |
| if (RParen.isError()) |
| return makeParserError(); |
| builder.useRightParen(RParen.get()); |
| |
| return makeParserSuccess(); |
| }(); |
| break; |
| |
| case TAK__opaqueReturnTypeOf: |
| status |= [&]() -> ParserStatus { |
| // Parse '('. |
| if (!Tok.is(tok::l_paren) || Tok.isAtStartOfLine()) { |
| diagnose(Tok, diag::attr_expected_lparen, "_opaqueReturnTypeOf", false); |
| return makeParserError(); |
| } |
| SourceLoc LParenLoc = Tok.getLoc(); |
| builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); |
| |
| ParsedOpaqueReturnTypeOfAttributeArgumentsSyntaxBuilder argBuilder( |
| *SyntaxContext); |
| |
| // Parse the mangled decl name and index. |
| if (!Tok.is(tok::string_literal)) { |
| diagnose(Tok, diag::opened_attribute_id_value); |
| return makeParserError(); |
| } |
| argBuilder.useMangledName(consumeTokenSyntax(tok::string_literal)); |
| |
| // Parse ','. |
| if (!Tok.is(tok::comma)) { |
| diagnose(Tok, diag::attr_expected_comma, "_opaqueReturnTypeOf", false); |
| builder.useArgument(builder.build()); |
| return makeParserError(); |
| } |
| argBuilder.useComma(consumeTokenSyntax(tok::comma)); |
| |
| // Parse index number. |
| if (!Tok.is(tok::integer_literal)) { |
| diagnose(Tok, diag::attr_expected_string_literal, |
| "_opaqueReturnTypeOf"); |
| builder.useArgument(builder.build()); |
| return makeParserError(); |
| } |
| argBuilder.useIndex(consumeTokenSyntax(tok::integer_literal)); |
| |
| builder.useArgument(argBuilder.build()); |
| |
| // Parse ')'. |
| auto RParen = parseMatchingTokenSyntax( |
| tok::r_paren, diag::expected_rparen_expr_list, LParenLoc); |
| if (RParen.isError()) |
| return makeParserError(); |
| builder.useRightParen(RParen.get()); |
| |
| return makeParserSuccess(); |
| }(); |
| break; |
| |
| default: |
| break; |
| } |
| return makeParsedResult(builder.build(), status); |
| } |
| |
| bool Parser::canParseTypeAttribute() { |
| if (!Tok.isAny(tok::identifier, tok::kw_in, tok::kw_inout)) |
| return false; |
| |
| TypeAttrKind attr = TypeAttributes::getAttrKindFromString(Tok.getText()); |
| consumeToken(); |
| |
| switch (attr) { |
| case TAK_Count: |
| return false; |
| case TAK_convention: { |
| if (!consumeIf(tok::l_paren)) |
| return false; |
| if (!Tok.is(tok::identifier)) |
| return false; |
| auto name = Tok.getText(); |
| consumeToken(tok::identifier); |
| if (name == "witness_method") { |
| consumeToken(); |
| if (!consumeIf(tok::colon)) |
| return false; |
| if (!consumeIf(tok::identifier)) |
| return false; |
| } |
| if (!consumeIf(tok::r_paren)) |
| return false; |
| return true; |
| } |
| case TAK_opened: |
| return (consumeIf(tok::l_paren) && // '(' |
| consumeIf(tok::string_literal) && // UUID |
| consumeIf(tok::r_paren)); // ')' |
| case TAK__opaqueReturnTypeOf: |
| return (consumeIf(tok::l_paren) && // '(' |
| consumeIf(tok::string_literal) && // Mangled name |
| consumeIf(tok::comma) && // ',' |
| consumeIf(tok::integer_literal) && // Index |
| consumeIf(tok::r_paren)); // ')' |
| default: |
| return true; |
| } |
| } |
| |
| /// \verbatim |
| /// attribute-list: |
| /// /*empty*/ |
| /// attribute-list-clause attribute-list |
| /// attribute-list-clause: |
| /// '@' attribute |
| /// \endverbatim |
| ParserStatus Parser::parseDeclAttributeList(DeclAttributes &Attributes) { |
| if (Tok.isNot(tok::at_sign)) |
| return makeParserSuccess(); |
| |
| ParserStatus Status; |
| SyntaxParsingContext AttrListCtx(SyntaxContext, SyntaxKind::AttributeList); |
| do { |
| SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute); |
| SourceLoc AtLoc = consumeToken(); |
| Status |= parseDeclAttribute(Attributes, AtLoc); |
| } while (Tok.is(tok::at_sign)); |
| return Status; |
| } |
| |
| /// \verbatim |
| /// modifier-list |
| /// /* empty */ |
| // modifier modifier-list |
| // modifier |
| // 'private' |
| // 'private' '(' 'set' ')' |
| // 'fileprivate' |
| // 'fileprivate' '(' 'set' )' |
| // 'internal' |
| // 'internal' '(' 'set' ')' |
| // 'public' |
| // 'open' |
| // 'weak' |
| // 'unowned' |
| // 'unowned' '(' 'safe' ')' |
| // 'unowned' '(' 'unsafe' ')' |
| // 'optional' |
| // 'required' |
| // 'lazy' |
| // 'final' |
| // 'dynamic' |
| // 'prefix' |
| // 'postfix' |
| // 'infix' |
| // 'override' |
| // 'mutating |
| // 'nonmutating' |
| // '__consuming' |
| // 'convenience' |
| bool Parser::parseDeclModifierList(DeclAttributes &Attributes, |
| SourceLoc &StaticLoc, |
| StaticSpellingKind &StaticSpelling) { |
| SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::ModifierList); |
| bool isError = false; |
| bool hasModifier = false; |
| while (true) { |
| switch (Tok.getKind()) { |
| |
| case tok::kw_private: |
| case tok::kw_fileprivate: |
| case tok::kw_internal: |
| case tok::kw_public: { |
| SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); |
| // We still model these specifiers as attributes. |
| isError |= |
| parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_AccessControl); |
| hasModifier = true; |
| continue; |
| } |
| |
| // Context sensitive keywords. |
| case tok::identifier: { |
| if (Tok.isEscapedIdentifier()) |
| break; |
| |
| DeclAttrKind Kind = llvm::StringSwitch<DeclAttrKind>(Tok.getText()) |
| #define CONTEXTUAL_CASE(KW, CLASS) .Case(#KW, DAK_##CLASS) |
| #define CONTEXTUAL_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS) |
| #define CONTEXTUAL_DECL_ATTR_ALIAS(KW, CLASS) CONTEXTUAL_CASE(KW, CLASS) |
| #define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS) |
| #include <swift/AST/Attr.def> |
| #undef CONTEXTUAL_CASE |
| .Default(DAK_Count); |
| |
| if (Kind == DAK_Count) |
| break; |
| |
| SyntaxParsingContext ModContext(SyntaxContext, |
| SyntaxKind::DeclModifier); |
| isError |= parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, Kind); |
| hasModifier = true; |
| continue; |
| } |
| |
| case tok::kw_static: { |
| // 'static' is not handled as an attribute in AST. |
| if (StaticLoc.isValid()) { |
| diagnose(Tok, diag::decl_already_static, |
| StaticSpellingKind::KeywordStatic) |
| .highlight(StaticLoc) |
| .fixItRemove(Tok.getLoc()); |
| } else { |
| StaticLoc = Tok.getLoc(); |
| StaticSpelling = StaticSpellingKind::KeywordStatic; |
| } |
| SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); |
| consumeToken(tok::kw_static); |
| hasModifier = true; |
| continue; |
| } |
| |
| case tok::kw_class: { |
| // If 'class' is a modifier on another decl kind, like var or func, |
| // then treat it as a modifier. |
| { |
| BacktrackingScope Scope(*this); |
| consumeToken(tok::kw_class); |
| // When followed by an 'override' or CC token inside a class, |
| // treat 'class' as a modifier; in the case of a following CC |
| // token, we cannot be sure there is no intention to override |
| // or witness something static. |
| if (isStartOfDecl() || (isa<ClassDecl>(CurDeclContext) && |
| (Tok.is(tok::code_complete) || |
| Tok.getRawText().equals("override")))) { |
| /* We're OK */ |
| } else { |
| // This 'class' is a real ClassDecl introducer. |
| break; |
| } |
| } |
| if (StaticLoc.isValid()) { |
| diagnose(Tok, diag::decl_already_static, |
| StaticSpellingKind::KeywordClass) |
| .highlight(StaticLoc) |
| .fixItRemove(Tok.getLoc()); |
| } else { |
| StaticLoc = Tok.getLoc(); |
| StaticSpelling = StaticSpellingKind::KeywordClass; |
| } |
| SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); |
| consumeToken(tok::kw_class); |
| hasModifier = true; |
| continue; |
| } |
| |
| case tok::unknown: |
| // Eat an invalid token in decl modifier context. Error tokens are |
| // diagnosed by the lexer, so we don't need to emit another diagnostic. |
| consumeToken(tok::unknown); |
| hasModifier = true; |
| continue; |
| |
| default: |
| break; |
| } |
| |
| // If we don't have any modifiers, don't bother to construct an empty list. |
| if (!hasModifier) |
| ListContext.setTransparent(); |
| |
| // If we 'break' out of the switch, modifier list has ended. |
| return isError; |
| } |
| } |
| |
| static bool isStartOfOperatorDecl(const Token &Tok, const Token &Tok2) { |
| return Tok.isContextualKeyword("operator") && |
| (Tok2.isContextualKeyword("prefix") || |
| Tok2.isContextualKeyword("postfix") || |
| Tok2.isContextualKeyword("infix")); |
| } |
| |
| /// Diagnose issues with fixity attributes, if any. |
| static void diagnoseOperatorFixityAttributes(Parser &P, |
| DeclAttributes &Attrs, |
| const Decl *D) { |
| auto isFixityAttr = [](DeclAttribute *attr){ |
| DeclAttrKind kind = attr->getKind(); |
| return attr->isValid() && (kind == DAK_Prefix || |
| kind == DAK_Infix || |
| kind == DAK_Postfix); |
| }; |
| |
| SmallVector<DeclAttribute *, 3> fixityAttrs; |
| std::copy_if(Attrs.begin(), Attrs.end(), |
| std::back_inserter(fixityAttrs), isFixityAttr); |
| std::reverse(fixityAttrs.begin(), fixityAttrs.end()); |
| |
| for (auto it = fixityAttrs.begin(); it != fixityAttrs.end(); ++it) { |
| if (it != fixityAttrs.begin()) { |
| auto *attr = *it; |
| P.diagnose(attr->getLocation(), diag::mutually_exclusive_attrs, |
| attr->getAttrName(), fixityAttrs.front()->getAttrName(), |
| attr->isDeclModifier()) |
| .fixItRemove(attr->getRange()); |
| attr->setInvalid(); |
| } |
| } |
| |
| // Operator declarations must specify a fixity. |
| if (auto *OD = dyn_cast<OperatorDecl>(D)) { |
| if (fixityAttrs.empty()) { |
| P.diagnose(OD->getOperatorLoc(), diag::operator_decl_no_fixity); |
| } |
| } |
| // Infix operator is only allowed on operator declarations, not on func. |
| else if (isa<FuncDecl>(D)) { |
| if (auto *attr = Attrs.getAttribute<InfixAttr>()) { |
| P.diagnose(attr->getLocation(), diag::invalid_infix_on_func) |
| .fixItRemove(attr->getLocation()); |
| attr->setInvalid(); |
| } |
| } else { |
| llvm_unreachable("unexpected decl kind?"); |
| } |
| } |
| |
| static unsigned skipUntilMatchingRBrace(Parser &P, |
| bool &HasPoundDirective, |
| bool &HasOperatorDeclarations, |
| bool &HasNestedClassDeclarations, |
| SyntaxParsingContext *&SyntaxContext) { |
| HasPoundDirective = false; |
| HasOperatorDeclarations = false; |
| HasNestedClassDeclarations = false; |
| |
| bool isRootCtx = SyntaxContext->isRoot(); |
| SyntaxParsingContext BlockItemListContext(SyntaxContext, |
| SyntaxKind::CodeBlockItemList); |
| if (isRootCtx) { |
| BlockItemListContext.setTransparent(); |
| } |
| SyntaxParsingContext BlockItemContext(SyntaxContext, |
| SyntaxKind::CodeBlockItem); |
| SyntaxParsingContext BodyContext(SyntaxContext, SyntaxKind::TokenList); |
| unsigned OpenBraces = 1; |
| |
| bool LastTokenWasFunc = false; |
| |
| while (OpenBraces != 0 && P.Tok.isNot(tok::eof)) { |
| // Detect 'func' followed by an operator identifier. |
| if (LastTokenWasFunc) { |
| LastTokenWasFunc = false; |
| HasOperatorDeclarations |= P.Tok.isAnyOperator(); |
| } else { |
| LastTokenWasFunc = P.Tok.is(tok::kw_func); |
| } |
| |
| HasNestedClassDeclarations |= P.Tok.is(tok::kw_class); |
| |
| HasPoundDirective |= P.Tok.isAny(tok::pound_sourceLocation, tok::pound_line, |
| tok::pound_if, tok::pound_else, tok::pound_endif, tok::pound_elseif); |
| if (P.consumeIf(tok::l_brace)) { |
| OpenBraces++; |
| continue; |
| } |
| if (OpenBraces == 1 && P.Tok.is(tok::r_brace)) |
| break; |
| if (P.consumeIf(tok::r_brace)) { |
| OpenBraces--; |
| continue; |
| } |
| P.consumeToken(); |
| } |
| return OpenBraces; |
| } |
| |
| bool swift::isKeywordPossibleDeclStart(const Token &Tok) { |
| switch (Tok.getKind()) { |
| case tok::at_sign: |
| case tok::kw_associatedtype: |
| case tok::kw_case: |
| case tok::kw_class: |
| case tok::kw_deinit: |
| case tok::kw_enum: |
| case tok::kw_extension: |
| case tok::kw_fileprivate: |
| case tok::kw_func: |
| case tok::kw_import: |
| case tok::kw_init: |
| case tok::kw_internal: |
| case tok::kw_let: |
| case tok::kw_operator: |
| case tok::kw_precedencegroup: |
| case tok::kw_private: |
| case tok::kw_protocol: |
| case tok::kw_public: |
| case tok::kw_static: |
| case tok::kw_struct: |
| case tok::kw_subscript: |
| case tok::kw_typealias: |
| case tok::kw_var: |
| case tok::pound_if: |
| case tok::pound_warning: |
| case tok::pound_error: |
| case tok::identifier: |
| case tok::pound_sourceLocation: |
| return true; |
| case tok::pound_line: |
| // #line at the start of the line is a directive, but it's deprecated. |
| // #line within a line is an expression. |
| return Tok.isAtStartOfLine(); |
| |
| case tok::kw_try: |
| // 'try' is not a valid way to start a decl, but we special-case 'try let' |
| // and 'try var' for better recovery. |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /// Given a current token of 'unowned', check to see if it is followed by a |
| /// "(safe)" or "(unsafe)" specifier. |
| static bool isParenthesizedUnowned(Parser &P) { |
| assert(P.Tok.getText() == "unowned" && P.peekToken().is(tok::l_paren) && |
| "Invariant violated"); |
| |
| // Look ahead to parse the parenthesized expression. |
| Parser::BacktrackingScope Backtrack(P); |
| P.consumeToken(tok::identifier); |
| P.consumeToken(tok::l_paren); |
| return P.Tok.is(tok::identifier) && P.peekToken().is(tok::r_paren) && |
| (P.Tok.getText() == "safe" || P.Tok.getText() == "unsafe"); |
| } |
| |
| |
| bool Parser::isStartOfDecl() { |
| // If this is obviously not the start of a decl, then we're done. |
| if (!isKeywordPossibleDeclStart(Tok)) return false; |
| |
| // When 'init' appears inside another 'init', it's likely the user wants to |
| // invoke an initializer but forgets to prefix it with 'self.' or 'super.' |
| // Otherwise, expect 'init' to be the start of a declaration (and complain |
| // when the expectation is not fulfilled). |
| if (Tok.is(tok::kw_init)) { |
| return !isa<ConstructorDecl>(CurDeclContext); |
| } |
| |
| // Similarly, when 'case' appears inside a function, it's probably a switch |
| // case, not an enum case declaration. |
| if (Tok.is(tok::kw_case)) { |
| return !isa<AbstractFunctionDecl>(CurDeclContext); |
| } |
| |
| // The protocol keyword needs more checking to reject "protocol<Int>". |
| if (Tok.is(tok::kw_protocol)) { |
| const Token &Tok2 = peekToken(); |
| return !Tok2.isAnyOperator() || !Tok2.getText().equals("<"); |
| } |
| |
| // The 'try' case is only for simple local recovery, so we only bother to |
| // check 'let' and 'var' right now. |
| if (Tok.is(tok::kw_try)) |
| return peekToken().isAny(tok::kw_let, tok::kw_var); |
| |
| // Look through attribute list, because it may be an *type* attribute list. |
| if (Tok.is(tok::at_sign)) { |
| BacktrackingScope backtrack(*this); |
| while (consumeIf(tok::at_sign)) { |
| // If not identifier or code complete token, consider '@' as an incomplete |
| // attribute. |
| if (Tok.isNot(tok::identifier, tok::code_complete)) |
| continue; |
| consumeToken(); |
| // Eat paren after attribute name; e.g. @foo(x) |
| if (consumeIf(tok::l_paren)) { |
| while (Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) { |
| if (consumeIf(tok::r_paren)) break; |
| skipSingle(); |
| } |
| } |
| } |
| // If this attribute is the last element in the block, |
| // consider it is a start of incomplete decl. |
| if (Tok.isAny(tok::r_brace, tok::eof, tok::pound_endif)) |
| return true; |
| |
| return isStartOfDecl(); |
| } |
| |
| // Otherwise, the only hard case left is the identifier case. |
| if (Tok.isNot(tok::identifier)) return true; |
| |
| // If this is an operator declaration, handle it. |
| const Token &Tok2 = peekToken(); |
| if (isStartOfOperatorDecl(Tok, Tok2)) |
| return true; |
| |
| // If this can't possibly be a contextual keyword, then this identifier is |
| // not interesting. Bail out. |
| if (!Tok.isContextualDeclKeyword()) |
| return false; |
| |
| // If it might be, we do some more digging. |
| |
| // If this is 'unowned', check to see if it is valid. |
| if (Tok.getText() == "unowned" && Tok2.is(tok::l_paren) && |
| isParenthesizedUnowned(*this)) { |
| Parser::BacktrackingScope Backtrack(*this); |
| consumeToken(tok::identifier); |
| consumeToken(tok::l_paren); |
| consumeToken(tok::identifier); |
| consumeToken(tok::r_paren); |
| return isStartOfDecl(); |
| } |
| |
| // If the next token is obviously not the start of a decl, bail early. |
| if (!isKeywordPossibleDeclStart(Tok2)) |
| return false; |
| |
| // Otherwise, do a recursive parse. |
| Parser::BacktrackingScope Backtrack(*this); |
| consumeToken(tok::identifier); |
| return isStartOfDecl(); |
| } |
| |
| void Parser::consumeDecl(ParserPosition BeginParserPosition, |
| ParseDeclOptions Flags, |
| bool IsTopLevel) { |
| SyntaxParsingContext Discarding(SyntaxContext); |
| Discarding.setDiscard(); |
| SourceLoc CurrentLoc = Tok.getLoc(); |
| |
| SourceLoc EndLoc = PreviousLoc; |
| backtrackToPosition(BeginParserPosition); |
| SourceLoc BeginLoc = Tok.getLoc(); |
| |
| State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, Flags.toRaw(), |
| CurDeclContext, {BeginLoc, EndLoc}, |
| BeginParserPosition.PreviousLoc); |
| |
| while (SourceMgr.isBeforeInBuffer(Tok.getLoc(), CurrentLoc)) |
| consumeToken(); |
| |
| if (IsTopLevel) { |
| // Skip the rest of the file to prevent the parser from constructing the |
| // AST for it. Forward references are not allowed at the top level. |
| while (Tok.isNot(tok::eof)) |
| consumeToken(); |
| } |
| } |
| |
| void Parser::setLocalDiscriminator(ValueDecl *D) { |
| // If we're not in a local context, this is unnecessary. |
| if (!CurLocalContext || !D->getDeclContext()->isLocalContext()) |
| return; |
| |
| if (auto TD = dyn_cast<TypeDecl>(D)) |
| if (!getScopeInfo().isInactiveConfigBlock()) |
| SF.LocalTypeDecls.insert(TD); |
| |
| Identifier name = D->getBaseName().getIdentifier(); |
| unsigned discriminator = CurLocalContext->claimNextNamedDiscriminator(name); |
| D->setLocalDiscriminator(discriminator); |
| } |
| |
| void Parser::setLocalDiscriminatorToParamList(ParameterList *PL) { |
| for (auto P : *PL) { |
| if (!P->hasName() || P->isImplicit()) |
| continue; |
| setLocalDiscriminator(P); |
| } |
| } |
| |
| void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition, |
| ParseDeclOptions Flags) { |
| auto CurLoc = Tok.getLoc(); |
| backtrackToPosition(BeginParserPosition); |
| SourceLoc BeginLoc = Tok.getLoc(); |
| SourceLoc EndLoc = CurLoc; |
| State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, |
| Flags.toRaw(), |
| CurDeclContext, {BeginLoc, EndLoc}, |
| BeginParserPosition.PreviousLoc); |
| |
| while (Tok.isNot(tok::eof)) |
| consumeToken(); |
| } |
| |
| /// Parse a single syntactic declaration and return a list of decl |
| /// ASTs. This can return multiple results for var decls that bind to multiple |
| /// values, structs that define a struct decl and a constructor, etc. |
| /// |
| /// \verbatim |
| /// decl: |
| /// decl-typealias |
| /// decl-extension |
| /// decl-let |
| /// decl-var |
| /// decl-class |
| /// decl-func |
| /// decl-enum |
| /// decl-struct |
| /// decl-import |
| /// decl-operator |
| /// \endverbatim |
| ParserResult<Decl> |
| Parser::parseDecl(ParseDeclOptions Flags, |
| bool IsAtStartOfLineOrPreviousHadSemi, |
| llvm::function_ref<void(Decl*)> Handler) { |
| ParserPosition BeginParserPosition; |
| if (isCodeCompletionFirstPass()) |
| BeginParserPosition = getParserPosition(); |
| |
| if (Tok.is(tok::pound_if)) { |
| auto IfConfigResult = parseIfConfig( |
| [&](SmallVectorImpl<ASTNode> &Decls, bool IsActive) { |
| Optional<Scope> scope; |
| if (!IsActive) |
| scope.emplace(this, getScopeInfo().getCurrentScope()->getKind(), |
| /*inactiveConfigBlock=*/true); |
| |
| ParserStatus Status; |
| bool PreviousHadSemi = true; |
| SyntaxParsingContext DeclListCtx(SyntaxContext, |
| SyntaxKind::MemberDeclList); |
| while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif, |
| tok::eof)) { |
| if (Tok.is(tok::r_brace)) { |
| diagnose(Tok.getLoc(), |
| diag::unexpected_rbrace_in_conditional_compilation_block); |
| // If we see '}', following declarations don't look like belong to |
| // the current decl context; skip them. |
| skipUntilConditionalBlockClose(); |
| break; |
| } |
| Status |= parseDeclItem(PreviousHadSemi, Flags, |
| [&](Decl *D) {Decls.emplace_back(D);}); |
| } |
| }); |
| if (IfConfigResult.hasCodeCompletion() && isCodeCompletionFirstPass()) { |
| consumeDecl(BeginParserPosition, Flags, |
| CurDeclContext->isModuleScopeContext()); |
| return makeParserError(); |
| } |
| |
| if (auto ICD = IfConfigResult.getPtrOrNull()) { |
| // The IfConfigDecl is ahead of its members in source order. |
| Handler(ICD); |
| // Copy the active members into the entries list. |
| for (auto activeMember : ICD->getActiveClauseElements()) { |
| auto *D = activeMember.get<Decl*>(); |
| if (isa<IfConfigDecl>(D)) |
| // Don't hoist nested '#if'. |
| continue; |
| Handler(D); |
| } |
| } |
| return IfConfigResult; |
| } |
| if (Tok.isAny(tok::pound_warning, tok::pound_error)) { |
| auto Result = parseDeclPoundDiagnostic(); |
| if (Result.isNonNull()) |
| Handler(Result.get()); |
| return Result; |
| } |
| |
| SyntaxParsingContext DeclParsingContext(SyntaxContext, |
| SyntaxContextKind::Decl); |
| SourceLoc leadingLoc = leadingTriviaLoc(); |
| |
| // Note that we're parsing a declaration. |
| StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(), |
| StructureMarkerKind::Declaration); |
| |
| // Parse attributes. |
| SourceLoc AttrsLoc = Tok.getLoc(); |
| DeclAttributes Attributes; |
| if (Tok.hasComment()) |
| Attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange())); |
| ParserStatus AttrStatus = parseDeclAttributeList(Attributes); |
| |
| // Parse modifiers. |
| // Keep track of where and whether we see a contextual keyword on the decl. |
| SourceLoc StaticLoc; |
| StaticSpellingKind StaticSpelling = StaticSpellingKind::None; |
| parseDeclModifierList(Attributes, StaticLoc, StaticSpelling); |
| |
| if (!Attributes.isEmpty()) |
| Generator.addDeclAttributes(Attributes, AttrsLoc); |
| |
| // We emit diagnostics for 'try let ...' in parseDeclVar(). |
| SourceLoc tryLoc; |
| if (Tok.is(tok::kw_try) && peekToken().isAny(tok::kw_let, tok::kw_var)) |
| tryLoc = consumeToken(tok::kw_try); |
| |
| ParserResult<Decl> DeclResult; |
| |
| // Save the original token, in case code-completion needs it. |
| auto OrigTok = Tok; |
| bool MayNeedOverrideCompletion = false; |
| |
| auto parseLetOrVar = [&](bool HasLetOrVarKeyword) { |
| // Collect all modifiers into a modifier list. |
| DeclParsingContext.setCreateSyntax(SyntaxKind::VariableDecl); |
| llvm::SmallVector<Decl *, 4> Entries; |
| DeclResult = parseDeclVar(Flags, Attributes, Entries, StaticLoc, |
| StaticSpelling, tryLoc, HasLetOrVarKeyword); |
| StaticLoc = SourceLoc(); // we handled static if present. |
| MayNeedOverrideCompletion = true; |
| if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass()) |
| return; |
| std::for_each(Entries.begin(), Entries.end(), Handler); |
| if (auto *D = DeclResult.getPtrOrNull()) |
| markWasHandled(D); |
| }; |
| |
| auto parseFunc = [&](bool HasFuncKeyword) { |
| // Collect all modifiers into a modifier list. |
| DeclParsingContext.setCreateSyntax(SyntaxKind::FunctionDecl); |
| DeclResult = parseDeclFunc(StaticLoc, StaticSpelling, Flags, Attributes, |
| HasFuncKeyword); |
| StaticLoc = SourceLoc(); // we handled static if present. |
| MayNeedOverrideCompletion = true; |
| }; |
| |
| switch (Tok.getKind()) { |
| case tok::kw_import: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::ImportDecl); |
| DeclResult = parseDeclImport(Flags, Attributes); |
| break; |
| case tok::kw_extension: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::ExtensionDecl); |
| DeclResult = parseDeclExtension(Flags, Attributes); |
| break; |
| case tok::kw_let: |
| case tok::kw_var: { |
| parseLetOrVar(/*HasLetOrVarKeyword=*/true); |
| break; |
| } |
| case tok::kw_typealias: |
| DeclResult = parseDeclTypeAlias(Flags, Attributes, leadingLoc); |
| MayNeedOverrideCompletion = true; |
| break; |
| case tok::kw_associatedtype: |
| DeclResult = parseDeclAssociatedType(Flags, Attributes, leadingLoc); |
| break; |
| case tok::kw_enum: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::EnumDecl); |
| DeclResult = parseDeclEnum(Flags, Attributes); |
| break; |
| case tok::kw_case: { |
| llvm::SmallVector<Decl *, 4> Entries; |
| DeclParsingContext.setCreateSyntax(SyntaxKind::EnumCaseDecl); |
| DeclResult = parseDeclEnumCase(Flags, Attributes, Entries); |
| if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass()) |
| break; |
| std::for_each(Entries.begin(), Entries.end(), Handler); |
| if (auto *D = DeclResult.getPtrOrNull()) |
| markWasHandled(D); |
| break; |
| } |
| case tok::kw_class: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::ClassDecl); |
| DeclResult = parseDeclClass(Flags, Attributes); |
| break; |
| case tok::kw_struct: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::StructDecl); |
| DeclResult = parseDeclStruct(Flags, Attributes); |
| break; |
| case tok::kw_init: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::InitializerDecl); |
| DeclResult = parseDeclInit(Flags, Attributes); |
| break; |
| case tok::kw_deinit: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::DeinitializerDecl); |
| DeclResult = parseDeclDeinit(Flags, Attributes); |
| break; |
| case tok::kw_operator: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::OperatorDecl); |
| DeclResult = parseDeclOperator(Flags, Attributes); |
| break; |
| case tok::kw_precedencegroup: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::PrecedenceGroupDecl); |
| DeclResult = parseDeclPrecedenceGroup(Flags, Attributes); |
| break; |
| case tok::kw_protocol: |
| DeclParsingContext.setCreateSyntax(SyntaxKind::ProtocolDecl); |
| DeclResult = parseDeclProtocol(Flags, Attributes); |
| break; |
| case tok::kw_func: |
| parseFunc(/*HasFuncKeyword=*/true); |
| break; |
| case tok::kw_subscript: { |
| DeclParsingContext.setCreateSyntax(SyntaxKind::SubscriptDecl); |
| llvm::SmallVector<Decl *, 4> Entries; |
| DeclResult = parseDeclSubscript(StaticLoc, StaticSpelling, Flags, |
| Attributes, Entries); |
| StaticLoc = SourceLoc(); // we handled static if present. |
| if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass()) |
| break; |
| std::for_each(Entries.begin(), Entries.end(), Handler); |
| MayNeedOverrideCompletion = true; |
| if (auto *D = DeclResult.getPtrOrNull()) |
| markWasHandled(D); |
| break; |
| } |
| |
| case tok::code_complete: |
| MayNeedOverrideCompletion = true; |
| DeclResult = makeParserError(); |
| // Handled below. |
| break; |
| case tok::pound: |
| if (Tok.isAtStartOfLine() && |
| peekToken().is(tok::code_complete) && |
| Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) { |
| consumeToken(); |
| if (CodeCompletion) |
| CodeCompletion->completeAfterPoundDirective(); |
| consumeToken(tok::code_complete); |
| DeclResult = makeParserCodeCompletionResult<Decl>(); |
| break; |
| } |
| LLVM_FALLTHROUGH; |
| |
| case tok::pound_if: |
| case tok::pound_sourceLocation: |
| case tok::pound_line: |
| case tok::pound_warning: |
| case tok::pound_error: |
| // We see some attributes right before these pounds. |
| // TODO: Emit dedicated errors for them. |
| LLVM_FALLTHROUGH; |
| |
| // Obvious nonsense. |
| default: |
| |
| if (Flags.contains(PD_HasContainerType) && |
| IsAtStartOfLineOrPreviousHadSemi) { |
| |
| // Emit diagnostics if we meet an identifier/operator where a declaration |
| // is expected, perhaps the user forgot the 'func' or 'var' keyword. |
| // |
| // Must not confuse it with trailing closure syntax, so we only |
| // recover in contexts where there can be no statements. |
| |
| const bool IsProbablyVarDecl = |
| Tok.isIdentifierOrUnderscore() && |
| peekToken().isAny(tok::colon, tok::equal, tok::comma); |
| |
| const bool IsProbablyTupleDecl = |
| Tok.is(tok::l_paren) && peekToken().isIdentifierOrUnderscore(); |
| |
| if (IsProbablyVarDecl || IsProbablyTupleDecl) { |
| |
| DescriptiveDeclKind DescriptiveKind; |
| |
| switch (StaticSpelling) { |
| case StaticSpellingKind::None: |
| DescriptiveKind = DescriptiveDeclKind::Property; |
| break; |
| case StaticSpellingKind::KeywordStatic: |
| DescriptiveKind = DescriptiveDeclKind::StaticProperty; |
| break; |
| case StaticSpellingKind::KeywordClass: |
| llvm_unreachable("kw_class is only parsed as a modifier if it's " |
| "followed by a keyword"); |
| } |
| |
| diagnose(Tok.getLoc(), diag::expected_keyword_in_decl, "var", |
| DescriptiveKind) |
| .fixItInsert(Tok.getLoc(), "var "); |
| parseLetOrVar(/*HasLetOrVarKeyword=*/false); |
| break; |
| } |
| |
| const bool IsProbablyFuncDecl = |
| Tok.isIdentifierOrUnderscore() || Tok.isAnyOperator(); |
| |
| if (IsProbablyFuncDecl) { |
| |
| DescriptiveDeclKind DescriptiveKind; |
| |
| if (Tok.isAnyOperator()) { |
| DescriptiveKind = DescriptiveDeclKind::OperatorFunction; |
| } else { |
| switch (StaticSpelling) { |
| case StaticSpellingKind::None: |
| DescriptiveKind = DescriptiveDeclKind::Method; |
| break; |
| case StaticSpellingKind::KeywordStatic: |
| DescriptiveKind = DescriptiveDeclKind::StaticMethod; |
| break; |
| case StaticSpellingKind::KeywordClass: |
| llvm_unreachable("kw_class is only parsed as a modifier if it's " |
| "followed by a keyword"); |
| } |
| } |
| |
| diagnose(Tok.getLoc(), diag::expected_keyword_in_decl, "func", |
| DescriptiveKind) |
| .fixItInsert(Tok.getLoc(), "func "); |
| parseFunc(/*HasFuncKeyword=*/false); |
| break; |
| } |
| } |
| |
| diagnose(Tok, diag::expected_decl); |
| |
| if (CurDeclContext) { |
| if (auto nominal = dyn_cast<NominalTypeDecl>(CurDeclContext)) { |
| diagnose(nominal->getLoc(), diag::note_in_decl_extension, false, |
| nominal->getName()); |
| } else if (auto extension = dyn_cast<ExtensionDecl>(CurDeclContext)) { |
| if (auto repr = extension->getExtendedTypeRepr()) { |
| if (auto idRepr = dyn_cast<IdentTypeRepr>(repr)) { |
| diagnose(extension->getLoc(), diag::note_in_decl_extension, true, |
| idRepr->getComponentRange().front()->getIdentifier()); |
| } |
| } |
| } |
| } |
| } |
| |
| if (DeclResult.isParseError() && Tok.is(tok::code_complete)) { |
| if (MayNeedOverrideCompletion && CodeCompletion) { |
| // If we need to complete an override, collect the keywords already |
| // specified so that we do not duplicate them in code completion |
| // strings. |
| SmallVector<StringRef, 3> Keywords; |
| SourceLoc introducerLoc; |
| switch (OrigTok.getKind()) { |
| case tok::kw_func: |
| case tok::kw_subscript: |
| case tok::kw_var: |
| case tok::kw_let: |
| case tok::kw_typealias: |
| Keywords.push_back(OrigTok.getText()); |
| introducerLoc = OrigTok.getLoc(); |
| break; |
| default: |
| // Other tokens are already accounted for. |
| break; |
| } |
| if (StaticSpelling == StaticSpellingKind::KeywordStatic) { |
| Keywords.push_back(getTokenText(tok::kw_static)); |
| } else if (StaticSpelling == StaticSpellingKind::KeywordClass) { |
| Keywords.push_back(getTokenText(tok::kw_class)); |
| } |
| for (auto attr : Attributes) { |
| Keywords.push_back(attr->getAttrName()); |
| } |
| CodeCompletion->completeNominalMemberBeginning(Keywords, |
| introducerLoc); |
| } |
| |
| DeclResult = makeParserCodeCompletionStatus(); |
| consumeToken(tok::code_complete); |
| } |
| |
| if (AttrStatus.hasCodeCompletion()) { |
| if (CodeCompletion) { |
| Optional<DeclKind> DK; |
| if (DeclResult.isNonNull()) |
| DK = DeclResult.get()->getKind(); |
| CodeCompletion->setAttrTargetDeclKind(DK); |
| } else { |
| delayParseFromBeginningToHere(BeginParserPosition, Flags); |
| return makeParserError(); |
| } |
| } |
| |
| if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass() && |
| !CurDeclContext->isModuleScopeContext() && |
| !isa<TopLevelCodeDecl>(CurDeclContext) && |
| !isa<AbstractClosureExpr>(CurDeclContext)) { |
| // Only consume non-toplevel decls. |
| consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false); |
| |
| return makeParserError(); |
| } |
| |
| if (auto SF = CurDeclContext->getParentSourceFile()) { |
| if (!getScopeInfo().isInactiveConfigBlock()) { |
| for (auto Attr : Attributes) { |
| if (isa<ObjCAttr>(Attr)) |
| SF->AttrsRequiringFoundation.insert(Attr); |
| } |
| } |
| } |
| |
| if (DeclResult.isNonNull()) { |
| Decl *D = DeclResult.get(); |
| if (!declWasHandledAlready(D)) { |
| Handler(D); |
| if (auto FD = dyn_cast<FuncDecl>(D)) { |
| if (auto attr = D->getAttrs().getAttribute<QuotedAttr>()) { |
| // TODO(TF-718): Properly mangle names for quote decls. |
| auto originalName = FD->getBaseName().userFacingName(); |
| SmallString<16> buf; |
| buf.append("_quoted"); |
| buf.push_back(clang::toUppercase(originalName[0])); |
| buf.append(originalName.begin() + 1, originalName.end()); |
| auto id = Context.getIdentifier(StringRef(buf.data(), buf.size())); |
| SmallVector<Identifier, 4> pieces; |
| auto name = DeclName(Context, id, pieces); |
| |
| // TODO(TF-716): Should this perhaps be a let? |
| // TODO(TF-717): Figure out the overriding story for quote decls. |
| auto kind = CurDeclContext->isTypeContext() |
| ? StaticSpellingKind::KeywordClass |
| : StaticSpellingKind::None; |
| auto params = |
| ParameterList::create(Context, SourceLoc(), {}, SourceLoc()); |
| auto ret = new (Context) |
| SimpleIdentTypeRepr(SourceLoc(), Context.getIdentifier("Tree")); |
| auto quoteDecl = FuncDecl::create( |
| Context, SourceLoc(), kind, SourceLoc(), name, SourceLoc(), |
| /*Throws=*/false, SourceLoc(), |
| /*GenericParams=*/nullptr, params, TypeLoc(ret), CurDeclContext); |
| quoteDecl->setImplicit(true); |
| auto expr = DeclQuoteExpr::create(Context, FD); |
| auto stmt = |
| new (Context) ReturnStmt(SourceLoc(), expr, /*Implicit=*/true); |
| auto body = BraceStmt::create(Context, SourceLoc(), {stmt}, |
| SourceLoc(), /*Implicit=*/true); |
| quoteDecl->setBody(body); |
| |
| attr->setQuoteDecl(quoteDecl); |
| Handler(quoteDecl); |
| } |
| } |
| } |
| } |
| |
| if (!DeclResult.isParseError()) { |
| // If we parsed 'class' or 'static', but didn't handle it above, complain |
| // about it. |
| if (StaticLoc.isValid()) |
| diagnose(DeclResult.get()->getLoc(), diag::decl_not_static, |
| StaticSpelling) |
| .fixItRemove(SourceRange(StaticLoc)); |
| } |
| |
| return DeclResult; |
| } |
| |
| /// Determine the declaration parsing options to use when parsing the members |
| /// of the given context. |
| static Parser::ParseDeclOptions getMemberParseDeclOptions( |
| IterableDeclContext *idc) { |
| using ParseDeclOptions = Parser::ParseDeclOptions; |
| |
| auto decl = idc->getDecl(); |
| switch (decl->getKind()) { |
| case DeclKind::Extension: |
| return ParseDeclOptions( |
| Parser::PD_HasContainerType | Parser::PD_InExtension); |
| case DeclKind::Enum: |
| return ParseDeclOptions( |
| Parser::PD_HasContainerType | Parser::PD_AllowEnumElement | |
| Parser::PD_InEnum); |
| |
| case DeclKind::Protocol: |
| return ParseDeclOptions( |
| Parser::PD_HasContainerType | Parser::PD_DisallowInit | |
| Parser::PD_InProtocol); |
| |
| case DeclKind::Class: |
| return ParseDeclOptions( |
| Parser::PD_HasContainerType | Parser::PD_AllowDestructor | |
| Parser::PD_InClass); |
| |
| case DeclKind::Struct: |
| return ParseDeclOptions(Parser::PD_HasContainerType | Parser::PD_InStruct); |
| |
| default: |
| llvm_unreachable("Bad iterable decl context kinds."); |
| } |
| } |
| |
| static ScopeKind getMemberParseScopeKind(IterableDeclContext *idc) { |
| auto decl = idc->getDecl(); |
| switch (decl->getKind()) { |
| case DeclKind::Extension: return ScopeKind::Extension; |
| case DeclKind::Enum: return ScopeKind::EnumBody; |
| case DeclKind::Protocol: return ScopeKind::ProtocolBody; |
| case DeclKind::Class: return ScopeKind::ClassBody; |
| case DeclKind::Struct: return ScopeKind::StructBody; |
| |
| default: |
| llvm_unreachable("Bad iterable decl context kinds."); |
| } |
| } |
| |
| std::vector<Decl *> Parser::parseDeclListDelayed(IterableDeclContext *IDC) { |
| Decl *D = const_cast<Decl*>(IDC->getDecl()); |
| DeclContext *DC = cast<DeclContext>(D); |
| SourceRange BodyRange; |
| if (auto ext = dyn_cast<ExtensionDecl>(IDC)) { |
| BodyRange = ext->getBraces(); |
| } else { |
| auto *ntd = cast<NominalTypeDecl>(IDC); |
| BodyRange = ntd->getBraces(); |
| } |
| |
| if (BodyRange.isInvalid()) { |
| assert(D->isImplicit()); |
| return { }; |
| } |
| |
| auto BeginParserPosition = getParserPosition({BodyRange.Start,BodyRange.End}); |
| auto EndLexerState = L->getStateForEndOfTokenLoc(BodyRange.End); |
| |
| // ParserPositionRAII needs a primed parser to restore to. |
| if (Tok.is(tok::NUM_TOKENS)) |
| consumeTokenWithoutFeedingReceiver(); |
| |
| // Ensure that we restore the parser state at exit. |
| ParserPositionRAII PPR(*this); |
| |
| // Create a lexer that cannot go past the end state. |
| Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); |
| |
| // Temporarily swap out the parser's current lexer with our new one. |
| llvm::SaveAndRestore<Lexer *> T(L, &LocalLex); |
| |
| // Rewind to the start of the member list, which is a '{' in well-formed |
| // code. |
| restoreParserPosition(BeginParserPosition); |
| |
| // If there is no left brace, then return an empty list of declarations; |
| // we will have already diagnosed this. |
| if (!Tok.is(tok::l_brace)) |
| return { }; |
| |
| // Re-enter the lexical scope. The top-level scope is needed because |
| // delayed parsing of members happens with a fresh parser, where there is |
| // no context. |
| Scope TopLevelScope(this, ScopeKind::TopLevel); |
| |
| Scope S(this, getMemberParseScopeKind(IDC)); |
| ContextChange CC(*this, DC); |
| SourceLoc LBLoc = consumeToken(tok::l_brace); |
| (void)LBLoc; |
| assert(LBLoc == BodyRange.Start); |
| SourceLoc RBLoc; |
| Diag<> Id; |
| switch (D->getKind()) { |
| case DeclKind::Extension: Id = diag::expected_rbrace_extension; break; |
| case DeclKind::Enum: Id = diag::expected_rbrace_enum; break; |
| case DeclKind::Protocol: Id = diag::expected_rbrace_protocol; break; |
| case DeclKind::Class: Id = diag::expected_rbrace_class; break; |
| case DeclKind::Struct: Id = diag::expected_rbrace_struct; break; |
| default: |
| llvm_unreachable("Bad iterable decl context kinds."); |
| } |
| bool hadError = false; |
| ParseDeclOptions Options = getMemberParseDeclOptions(IDC); |
| return parseDeclList(LBLoc, RBLoc, Id, Options, IDC, hadError); |
| } |
| |
| void Parser::parseDeclDelayed() { |
| auto DelayedState = State->takeDelayedDeclState(); |
| assert(DelayedState.get() && "should have delayed state"); |
| |
| auto BeginParserPosition = getParserPosition(DelayedState->BodyPos); |
| auto EndLexerState = L->getStateForEndOfTokenLoc(DelayedState->BodyEnd); |
| |
| // ParserPositionRAII needs a primed parser to restore to. |
| if (Tok.is(tok::NUM_TOKENS)) |
| consumeTokenWithoutFeedingReceiver(); |
| |
| // Ensure that we restore the parser state at exit. |
| ParserPositionRAII PPR(*this); |
| |
| // Create a lexer that cannot go past the end state. |
| Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); |
| |
| // Temporarily swap out the parser's current lexer with our new one. |
| llvm::SaveAndRestore<Lexer *> T(L, &LocalLex); |
| |
| // Rewind to the beginning of the decl. |
| restoreParserPosition(BeginParserPosition); |
| |
| // Re-enter the lexical scope. |
| Scope S(this, DelayedState->takeScope()); |
| ContextChange CC(*this, DelayedState->ParentContext); |
| |
| parseDecl(ParseDeclOptions(DelayedState->Flags), |
| /*IsAtStartOfLineOrPreviousHadSemi=*/true, |
| [&](Decl *D) { |
| if (auto *parent = DelayedState->ParentContext) { |
| if (auto *NTD = dyn_cast<NominalTypeDecl>(parent)) { |
| NTD->addMember(D); |
| } else if (auto *ED = dyn_cast<ExtensionDecl>(parent)) { |
| ED->addMember(D); |
| } else if (auto *SF = dyn_cast<SourceFile>(parent)) { |
| SF->Decls.push_back(D); |
| } |
| } |
| }); |
| } |
| |
| /// Parse an 'import' declaration, doing no token skipping on error. |
| /// |
| /// \verbatim |
| /// decl-import: |
| /// 'import' attribute-list import-kind? import-path |
| /// import-kind: |
| /// 'typealias' |
| /// 'struct' |
| /// 'class' |
| /// 'enum' |
| /// 'protocol' |
| /// 'var' |
| /// 'func' |
| /// import-path: |
| /// any-identifier ('.' any-identifier)* |
| /// \endverbatim |
| ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags, |
| DeclAttributes &Attributes) { |
| SourceLoc ImportLoc = consumeToken(tok::kw_import); |
| DebuggerContextChange DCC (*this); |
| |
| if (!CodeCompletion && !DCC.movedToTopLevel() && !(Flags & PD_AllowTopLevel)) { |
| diagnose(ImportLoc, diag::decl_inner_scope); |
| return nullptr; |
| } |
| |
| ImportKind Kind = ImportKind::Module; |
| SourceLoc KindLoc; |
| if (Tok.isKeyword()) { |
| switch (Tok.getKind()) { |
| case tok::kw_typealias: |
| Kind = ImportKind::Type; |
| break; |
| case tok::kw_struct: |
| Kind = ImportKind::Struct; |
| break; |
| case tok::kw_class: |
| Kind = ImportKind::Class; |
| break; |
| case tok::kw_enum: |
| Kind = ImportKind::Enum; |
| break; |
| case tok::kw_protocol: |
| Kind = ImportKind::Protocol; |
| break; |
| case tok::kw_var: |
| case tok::kw_let: |
| Kind = ImportKind::Var; |
| break; |
| case tok::kw_func: |
| Kind = ImportKind::Func; |
| break; |
| default: |
| diagnose(Tok, diag::expected_identifier_in_decl, "import"); |
| diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText()); |
| diagnose(Tok, diag::backticks_to_escape); |
| return nullptr; |
| } |
| KindLoc = consumeToken(); |
| } |
| |
| std::vector<std::pair<Identifier, SourceLoc>> ImportPath; |
| bool HasNext; |
| do { |
| SyntaxParsingContext AccessCompCtx(SyntaxContext, |
| SyntaxKind::AccessPathComponent); |
| if (Tok.is(tok::code_complete)) { |
| consumeToken(); |
| if (CodeCompletion) { |
| CodeCompletion->completeImportDecl(ImportPath); |
| } |
| return makeParserCodeCompletionStatus(); |
| } |
| ImportPath.push_back(std::make_pair(Identifier(), Tok.getLoc())); |
| if (parseAnyIdentifier(ImportPath.back().first, |
| diag::expected_identifier_in_decl, "import")) |
| return nullptr; |
| HasNext = consumeIf(tok::period); |
| } while (HasNext); |
| |
| // Collect all access path components to an access path. |
| SyntaxContext->collectNodesInPlace(SyntaxKind::AccessPath); |
| |
| if (Tok.is(tok::code_complete)) { |
| // We omit the code completion token if it immediately follows the module |
| // identifiers. |
| auto BufferId = SourceMgr.getCodeCompletionBufferID(); |
| auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().second, |
| BufferId) + ImportPath.back().first.str().size(); |
| auto CCTokenOffset = SourceMgr.getLocOffsetInBuffer(SourceMgr. |
| getCodeCompletionLoc(), BufferId); |
| if (IdEndOffset == CCTokenOffset) { |
| consumeToken(); |
| } |
| } |
| |
| if (Kind != ImportKind::Module && ImportPath.size() == 1) { |
| diagnose(ImportPath.front().second, diag::decl_expected_module_name); |
| return nullptr; |
| } |
| |
| auto *ID = ImportDecl::create(Context, CurDeclContext, ImportLoc, Kind, |
| KindLoc, ImportPath); |
| ID->getAttrs() = Attributes; |
| return DCC.fixupParserResult(ID); |
| } |
| |
| /// Parse an inheritance clause. |
| /// |
| /// \verbatim |
| /// inheritance: |
| /// ':' inherited (',' inherited)* |
| /// |
| /// inherited: |
| /// 'class' |
| /// type-identifier |
| /// \endverbatim |
| |
| ParsedSyntaxResult<ParsedTypeInheritanceClauseSyntax> |
| Parser::parseTypeInheritanceClauseSyntax(bool allowClassRequirement, |
| bool allowAnyObject) { |
| ParsedTypeInheritanceClauseSyntaxBuilder builder(*SyntaxContext); |
| ParserStatus status; |
| |
| builder.useColon(consumeTokenSyntax(tok::colon)); |
| |
| SourceLoc startLoc = Tok.getLoc(); |
| SourceLoc classRequirementLoc, prevCommaLoc; |
| bool hasNext = true; |
| do { |
| ParsedInheritedTypeSyntaxBuilder elemBuilder(*SyntaxContext); |
| |
| // Parse the 'class' keyword for a class requirement. |
| if (Tok.is(tok::kw_class)) { |
| auto classLoc = Tok.getLoc(); |
| auto classTok = consumeTokenSyntax(tok::kw_class); |
| auto restriction = ParsedSyntaxRecorder::makeClassRestrictionType( |
| std::move(classTok), *SyntaxContext); |
| elemBuilder.useTypeName(std::move(restriction)); |
| |
| if (!allowClassRequirement) { |
| // If we aren't allowed to have a class requirement here, complain. |
| diagnose(classLoc, diag::unexpected_class_constraint); |
| |
| // Note that it makes no sense to suggest fixing 'struct S : class' to |
| // 'struct S : AnyObject' for example; in that case we just complain |
| // about 'class' being invalid here. |
| if (allowAnyObject) { |
| diagnose(classLoc, diag::suggest_anyobject) |
| .fixItReplace(classLoc, "AnyObject"); |
| } |
| |
| } else if (classRequirementLoc.isValid()) { |
| // If we already saw a class requirement, complain. |
| diagnose(Tok.getLoc(), diag::redundant_class_requirement) |
| .highlight(classRequirementLoc) |
| .fixItRemove(SourceRange(prevCommaLoc, classLoc)); |
| |
| } else if (prevCommaLoc.isValid()) { |
| // If the class requirement was not the first requirement, complain. |
| diagnose(classLoc, diag::late_class_requirement) |
| .fixItInsert(startLoc, "class, ") |
| .fixItRemove(SourceRange(prevCommaLoc, classLoc)); |
| } |
| |
| // Record the location of the 'class' keyword. |
| if (!classRequirementLoc.isValid()) |
| classRequirementLoc = classLoc; |
| } else { |
| // Parse inherited type. |
| auto inheritedType = parseTypeSyntax(); |
| status |= inheritedType.getStatus(); |
| if (!inheritedType.isNull()) |
| elemBuilder.useTypeName(inheritedType.get()); |
| else |
| elemBuilder.useTypeName( |
| ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); |
| } |
| |
| // Parse ','. |
| if (Tok.is(tok::comma)) { |
| prevCommaLoc = Tok.getLoc(); |
| elemBuilder.useTrailingComma(consumeTokenSyntax(tok::comma)); |
| } else { |
| hasNext = false; |
| } |
| |
| builder.addInheritedTypeCollectionMember(elemBuilder.build()); |
| } while (hasNext); |
| |
| return makeParsedResult(builder.build(), status); |
| } |
| |
| ParserStatus Parser::parseInheritance(MutableArrayRef<TypeLoc> &Inherited, |
| bool allowClassRequirement, |
| bool allowAnyObject) { |
| auto leadingLoc = leadingTriviaLoc(); |
| auto parsed = parseTypeInheritanceClauseSyntax(allowClassRequirement, |
| allowAnyObject); |
| SyntaxContext->addSyntax(parsed.get()); |
| auto clause = SyntaxContext->topNode<TypeInheritanceClauseSyntax>(); |
| Inherited = Generator.generate(clause, leadingLoc, allowClassRequirement); |
| return parsed.getStatus(); |
| } |
| |
| static ParsedSyntaxResult<ParsedTokenSyntax> |
| parseIdentifierDeclNameSyntax(Parser &P, StringRef DeclKindName, |
| llvm::function_ref<bool(const Token &)> canRecover) { |
| if (P.Tok.is(tok::identifier)) { |
| auto text = P.Tok.getText(); |
| auto loc = P.Tok.getLoc(); |
| |
| auto tok = P.consumeIdentifierSyntax(); |
| if (P.Tok.isIdentifierOrUnderscore() && !P.Tok.isContextualDeclKeyword()) |
| P.diagnoseConsecutiveIDs(text, loc, DeclKindName); |
| |
| // Return success anyway |
| return makeParsedResult(std::move(tok)); |
| } |
| |
| P.checkForInputIncomplete(); |
| |
| if (P.Tok.is(tok::integer_literal) || P.Tok.is(tok::floating_literal) || |
| (P.Tok.is(tok::unknown) && isdigit(P.Tok.getText()[0]))) { |
| // Using numbers for identifiers is a common error for beginners, so it's |
| // worth handling this in a special way. |
| P.diagnose(P.Tok, diag::number_cant_start_decl_name, DeclKindName); |
| |
| // Pretend this works as an identifier, which shouldn't be observable since |
| // actual uses of it will hit random other errors, e.g. `1()` won't be |
| // callable. |
| P.Tok.setKind(tok::identifier); |
| return makeParsedResult(P.consumeTokenSyntax()); |
| } |
| |
| if (P.Tok.isKeyword()) { |
| P.diagnose(P.Tok, diag::keyword_cant_be_identifier, P.Tok.getText()); |
| P.diagnose(P.Tok, diag::backticks_to_escape) |
| .fixItReplace(P.Tok.getLoc(), "`" + P.Tok.getText().str() + "`"); |
| |
| // Recover if the next token is one of the expected tokens. |
| if (canRecover(P.peekToken())) { |
| P.Tok.setKind(tok::identifier); |
| return makeParsedResult(P.consumeTokenSyntax()); |
| } |
| return makeParserError(); |
| } |
| |
| P.diagnose(P.Tok, diag::expected_identifier_in_decl, DeclKindName); |
| return makeParserError(); |
| } |
| |
| static ParserStatus |
| parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, |
| StringRef DeclKindName, |
| llvm::function_ref<bool(const Token &)> canRecover) { |
| auto leadingLoc = P.leadingTriviaLoc(); |
| auto parsed = parseIdentifierDeclNameSyntax(P, DeclKindName, canRecover); |
| if (!parsed.isNull()) { |
| P.SyntaxContext->addSyntax(parsed.get()); |
| auto syntax = P.SyntaxContext->topNode<TokenSyntax>(); |
| Loc = P.Generator.generateIdentifierDeclName(syntax, leadingLoc, Result); |
| } |
| return parsed.getStatus(); |
| } |
| |
| /// Add a fix-it to remove the space in consecutive identifiers. |
| /// Add a camel-cased option if it is different than the first option. |
| void Parser::diagnoseConsecutiveIDs(StringRef First, SourceLoc FirstLoc, |
| StringRef DeclKindName) { |
| assert(Tok.isAny(tok::identifier, tok::kw__)); |
| |
| diagnose(Tok, diag::repeated_identifier, DeclKindName); |
| auto Second = Tok.getText(); |
| auto SecondLoc = Tok.getLoc(); |
| ignoreToken(); |
| |
| SourceRange FixRange(FirstLoc, SecondLoc); |
| // Provide two fix-its: a direct concatenation of the two identifiers |
| // and a camel-cased version. |
| // |
| auto DirectConcatenation = First.str() + Second.str(); |
| diagnose(SecondLoc, diag::join_identifiers) |
| .fixItReplace(FixRange, DirectConcatenation); |
| |
| SmallString<8> CapitalizedScratch; |
| auto Capitalized = camel_case::toSentencecase(Second, |
| CapitalizedScratch); |
| if (Capitalized != Second) { |
| auto CamelCaseConcatenation = First.str() + Capitalized.str(); |
| diagnose(SecondLoc, diag::join_identifiers_camel_case) |
| .fixItReplace(FixRange, CamelCaseConcatenation); |
| } |
| } |
| |
| /// Parse a Decl item in decl list. |
| ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi, |
| Parser::ParseDeclOptions Options, |
| llvm::function_ref<void(Decl*)> handler) { |
| if (Tok.is(tok::semi)) { |
| // Consume ';' without preceding decl. |
| diagnose(Tok, diag::unexpected_separator, ";") |
| .fixItRemove(Tok.getLoc()); |
| consumeToken(); |
| // Return success because we already recovered. |
| return makeParserSuccess(); |
| } |
| |
| // If the previous declaration didn't have a semicolon and this new |
| // declaration doesn't start a line, complain. |
| const bool IsAtStartOfLineOrPreviousHadSemi = |
| PreviousHadSemi || Tok.isAtStartOfLine() || Tok.is(tok::unknown); |
| if (!IsAtStartOfLineOrPreviousHadSemi) { |
| auto endOfPrevious = getEndOfPreviousLoc(); |
| diagnose(endOfPrevious, diag::declaration_same_line_without_semi) |
| .fixItInsert(endOfPrevious, ";"); |
| } |
| |
| if (Tok.isAny(tok::pound_sourceLocation, tok::pound_line)) { |
| auto LineDirectiveStatus = parseLineDirective(Tok.is(tok::pound_line)); |
| if (LineDirectiveStatus.isError()) |
| skipUntilDeclRBrace(tok::semi, tok::pound_endif); |
| return LineDirectiveStatus; |
| } |
| |
| ParserResult<Decl> Result; |
| SyntaxParsingContext DeclContext(SyntaxContext, |
| SyntaxKind::MemberDeclListItem); |
| if (loadCurrentSyntaxNodeFromCache()) { |
| return ParserStatus(); |
| } |
| Result = parseDecl(Options, IsAtStartOfLineOrPreviousHadSemi, handler); |
| if (Result.isParseError()) |
| skipUntilDeclRBrace(tok::semi, tok::pound_endif); |
| SourceLoc SemiLoc; |
| PreviousHadSemi = consumeIf(tok::semi, SemiLoc); |
| if (PreviousHadSemi && Result.isNonNull()) |
| Result.get()->TrailingSemiLoc = SemiLoc; |
| return Result; |
| } |
| |
| bool Parser::parseMemberDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, |
| SourceLoc PosBeforeLB, |
| Diag<> ErrorDiag, |
| IterableDeclContext *IDC) { |
| bool HasOperatorDeclarations; |
| bool HasNestedClassDeclarations; |
| |
| if (canDelayMemberDeclParsing(HasOperatorDeclarations, |
| HasNestedClassDeclarations)) { |
| if (HasOperatorDeclarations) |
| IDC->setMaybeHasOperatorDeclarations(); |
| if (HasNestedClassDeclarations) |
| IDC->setMaybeHasNestedClassDeclarations(); |
| |
| if (delayParsingDeclList(LBLoc, RBLoc, IDC)) |
| return true; |
| } else { |
| // When forced to eagerly parse, do so and cache the results in the |
| // evaluator. |
| bool hadError = false; |
| ParseDeclOptions Options = getMemberParseDeclOptions(IDC); |
| auto members = parseDeclList( |
| LBLoc, RBLoc, ErrorDiag, Options, IDC, hadError); |
| IDC->setMaybeHasOperatorDeclarations(); |
| IDC->setMaybeHasNestedClassDeclarations(); |
| Context.evaluator.cacheOutput( |
| ParseMembersRequest{IDC}, |
| Context.AllocateCopy(llvm::makeArrayRef(members))); |
| |
| if (hadError) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Parse the members in a struct/class/enum/protocol/extension. |
| /// |
| /// \verbatim |
| /// decl* '}' |
| /// \endverbatim |
| std::vector<Decl *> Parser::parseDeclList( |
| SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, |
| ParseDeclOptions Options, IterableDeclContext *IDC, |
| bool &hadError) { |
| std::vector<Decl *> decls; |
| ParserStatus Status; |
| bool PreviousHadSemi = true; |
| { |
| SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::MemberDeclList); |
| while (Tok.isNot(tok::r_brace)) { |
| Status |= parseDeclItem(PreviousHadSemi, Options, |
| [&](Decl *D) { decls.push_back(D); }); |
| if (Tok.isAny(tok::eof, tok::pound_endif, tok::pound_else, |
| tok::pound_elseif)) { |
| IsInputIncomplete = true; |
| break; |
| } |
| } |
| } |
| if (parseMatchingToken(tok::r_brace, RBLoc, ErrorDiag, LBLoc)) { |
| // Synthesize an r_brace syntax node if the token is absent |
| SyntaxContext->synthesize(tok::r_brace, RBLoc); |
| } |
| |
| // Increase counter. |
| if (auto *stat = Context.Stats) { |
| stat->getFrontendCounters().NumIterableDeclContextParsed ++; |
| } |
| // If we found the closing brace, then the caller should not care if there |
| // were errors while parsing inner decls, because we recovered. |
| if (RBLoc.isInvalid()) |
| hadError = true; |
| return decls; |
| } |
| |
| bool Parser::canDelayMemberDeclParsing(bool &HasOperatorDeclarations, |
| bool &HasNestedClassDeclarations) { |
| // If explicitly disabled, respect the flag. |
| if (!DelayBodyParsing) |
| return false; |
| // Recovering parser status later for #sourceLocation is not-trivial and |
| // it may not worth it. |
| if (InPoundLineEnvironment) |
| return false; |
| |
| // Skip until the matching right curly bracket; if we find a pound directive, |
| // we can't lazily parse. |
| BacktrackingScope BackTrack(*this); |
| bool HasPoundDirective; |
| skipUntilMatchingRBrace(*this, |
| HasPoundDirective, |
| HasOperatorDeclarations, |
| HasNestedClassDeclarations, |
| SyntaxContext); |
| if (!HasPoundDirective) |
| BackTrack.cancelBacktrack(); |
| return !BackTrack.willBacktrack(); |
| } |
| |
| bool Parser::delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, |
| IterableDeclContext *IDC) { |
| bool error = false; |
| |
| if (Tok.is(tok::r_brace)) { |
| RBLoc = consumeToken(); |
| } else { |
| RBLoc = Tok.getLoc(); |
| error = true; |
| } |
| |
| State->delayDeclList(IDC); |
| return error; |
| } |
| |
| /// Parse an 'extension' declaration. |
| /// |
| /// \verbatim |
| /// extension: |
| /// 'extension' attribute-list type inheritance? where-clause? |
| /// '{' decl* '}' |
| /// \endverbatim |
| ParserResult<ExtensionDecl> |
| Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { |
| SourceLoc ExtensionLoc = consumeToken(tok::kw_extension); |
| |
| DebuggerContextChange DCC (*this); |
| |
| // Parse the type being extended. |
| ParserStatus status; |
| ParserResult<TypeRepr> extendedType = parseType(diag::extension_type_expected); |
| status |= extendedType; |
| |
| // Parse optional inheritance clause. |
| MutableArrayRef<TypeLoc> Inherited; |
| if (Tok.is(tok::colon)) |
| status |= parseInheritance(Inherited, |
| /*allowClassRequirement=*/false, |
| /*allowAnyObject=*/false); |
| |
| // Parse the optional where-clause. |
| TrailingWhereClause *trailingWhereClause = nullptr; |
| if (Tok.is(tok::kw_where)) { |
| SourceLoc whereLoc; |
| SmallVector<RequirementRepr, 4> requirements; |
| bool firstTypeInComplete; |
| auto whereStatus = parseGenericWhereClause(whereLoc, requirements, |
| firstTypeInComplete); |
| if (whereStatus.isSuccess()) { |
| trailingWhereClause = TrailingWhereClause::create(Context, whereLoc, |
| requirements); |
| } else if (whereStatus.hasCodeCompletion()) { |
| if (CodeCompletion && firstTypeInComplete) { |
| CodeCompletion->completeGenericParams(extendedType.getPtrOrNull()); |
| } else |
| return makeParserCodeCompletionResult<ExtensionDecl>(); |
| } |
| } |
| |
| ExtensionDecl *ext = ExtensionDecl::create(Context, ExtensionLoc, |
| extendedType.getPtrOrNull(), |
| Inherited, |
| CurDeclContext, |
| trailingWhereClause); |
| ext->getAttrs() = Attributes; |
| |
| SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock); |
| SourceLoc LBLoc, RBLoc; |
| |
| auto PosBeforeLB = Tok.getLoc(); |
| if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_extension)) { |
| LBLoc = PreviousLoc; |
| RBLoc = LBLoc; |
| status.setIsParseError(); |
| } else { |
| ContextChange CC(*this, ext); |
| Scope S(this, ScopeKind::Extension); |
| |
| if (parseMemberDeclList(LBLoc, RBLoc, PosBeforeLB, |
| diag::expected_rbrace_extension, |
| ext)) |
| status.setIsParseError(); |
| |
| // Don't propagate the code completion bit from members: we cannot help |
| // code completion inside a member decl, and our callers cannot do |
| // anything about it either. But propagate the error bit. |
| } |
| ext->setBraces({LBLoc, RBLoc}); |
| if (!DCC.movedToTopLevel() && !(Flags & PD_AllowTopLevel)) { |
| diagnose(ExtensionLoc, diag::decl_inner_scope); |
| status.setIsParseError(); |
| |
| // Tell the type checker not to touch this extension. |
| ext->setInvalid(); |
| } |
| |
| return DCC.fixupParserResult(status, ext); |
| } |
| |
| ParserResult<PoundDiagnosticDecl> Parser::parseDeclPoundDiagnostic() { |
| bool isError = Tok.is(tok::pound_error); |
| SyntaxParsingContext LocalContext(SyntaxContext, |
| isError ? SyntaxKind::PoundErrorDecl : SyntaxKind::PoundWarningDecl); |
| SourceLoc startLoc = |
| consumeToken(isError ? tok::pound_error : tok::pound_warning); |
| |
| SourceLoc lParenLoc = Tok.getLoc(); |
| bool hadLParen = consumeIf(tok::l_paren); |
| |
| if (!Tok.is(tok::string_literal)) { |
| // Catch #warning(oops, forgot the quotes) |
| SourceLoc wordsStartLoc = Tok.getLoc(); |
| |
| skipUntilTokenOrEndOfLine(tok::r_paren); |
| |
| SourceLoc wordsEndLoc = getEndOfPreviousLoc(); |
| |
| auto diag = diagnose(wordsStartLoc, |
| diag::pound_diagnostic_expected_string, isError); |
| if (wordsEndLoc != wordsStartLoc) { |
| diag.fixItInsert(wordsStartLoc, hadLParen ? "\"" : "(\"") |
| .fixItInsert(wordsEndLoc, Tok.is(tok::r_paren) ? "\"" : "\")"); |
| } |
| |
| // Consume the right paren to finish the decl, if it's there. |
| consumeIf(tok::r_paren); |
| |
| return makeParserError(); |
| } |
| |
| auto string = parseExprStringLiteral(); |
| if (string.isNull()) |
| return makeParserError(); |
| |
| auto messageExpr = string.get(); |
| |
| SourceLoc rParenLoc = Tok.getLoc(); |
| bool hadRParen = consumeIf(tok::r_paren); |
| |
| if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) { |
| diagnose(Tok.getLoc(), |
| diag::extra_tokens_pound_diagnostic_directive, isError); |
| return makeParserError(); |
| } |
| |
| if (!hadLParen && !hadRParen) { |
| // Catch if the user forgot parentheses around the string, e.g. |
| // #warning "foo" |
| diagnose(lParenLoc, diag::pound_diagnostic_expected_parens, isError) |
| .highlight(messageExpr->getSourceRange()) |
| .fixItInsert(messageExpr->getStartLoc(), "(") |
| .fixItInsertAfter(messageExpr->getEndLoc(), ")"); |
| return makeParserError(); |
| } else if (hadRParen && !hadLParen) { |
| // Catch if the user forgot a left paren before the string, e.g. |
| // #warning "foo") |
| diagnose(messageExpr->getStartLoc(), diag::pound_diagnostic_expected, |
| "(", isError) |
| .fixItInsert(messageExpr->getStartLoc(), "("); |
| return makeParserError(); |
| } else if (hadLParen && !hadRParen) { |
| // Catch if the user forgot a right paren after the string, e.g. |
| // #warning("foo" |
| diagnose(messageExpr->getEndLoc(), diag::pound_diagnostic_expected, |
| ")", isError) |
| .fixItInsertAfter(messageExpr->getEndLoc(), ")"); |
| return makeParserError(); |
| } |
| |
| if (messageExpr->getKind() == ExprKind::InterpolatedStringLiteral) { |
| diagnose(messageExpr->getStartLoc(), diag::pound_diagnostic_interpolation, |
| isError) |
| .highlight(messageExpr->getSourceRange()); |
| return makeParserError(); |
| } |
| |
| ParserStatus Status; |
| return makeParserResult(Status, |
| new (Context) PoundDiagnosticDecl(CurDeclContext, isError, |
| startLoc, rParenLoc, |
| cast<StringLiteralExpr>(messageExpr))); |
| } |
| |
| ParserStatus Parser::parseLineDirective(bool isLine) { |
| SyntaxParsingContext PoundSourceLocation(SyntaxContext, |
| SyntaxKind::PoundSourceLocation); |
| SourceLoc Loc = consumeToken(); |
| if (isLine) { |
| diagnose(Loc, diag::line_directive_style_deprecated) |
| .fixItReplace(Loc, "#sourceLocation"); |
| } |
| bool WasInPoundLineEnvironment = InPoundLineEnvironment; |
| if (WasInPoundLineEnvironment) { |
| SourceMgr.closeVirtualFile(Loc); |
| InPoundLineEnvironment = false; |
| } |
| |
| |
| unsigned StartLine = 0; |
| Optional<StringRef> Filename; |
| if (!isLine) { |
| // #sourceLocation() |
| // #sourceLocation(file: "foo", line: 42) |
| if (parseToken(tok::l_paren, diag::sourceLocation_expected, "(")) |
| return makeParserError(); |
| |
| // Handle the "reset" form. |
| if (consumeIf(tok::r_paren)) { |
| if (!WasInPoundLineEnvironment) { |
| diagnose(Tok, diag::unexpected_line_directive); |
| return makeParserError(); |
| } |
| return makeParserSuccess(); |
| } |
| |
| { |
| SyntaxParsingContext Args(SyntaxContext, |
| SyntaxKind::PoundSourceLocationArgs); |
| |
| if (parseSpecificIdentifier("file", diag::sourceLocation_expected, |
| "file:") || |
| parseToken(tok::colon, diag::sourceLocation_expected, ":")) |
| return makeParserError(); |
| |
| if (Tok.isNot(tok::string_literal)) { |
| diagnose(Tok, diag::expected_line_directive_name); |
| return makeParserError(); |
| } |
| |
| Filename = |
| getStringLiteralIfNotInterpolated(Loc, "'#sourceLocation'"); |
| if (!Filename.hasValue()) |
| return makeParserError(); |
| consumeToken(tok::string_literal); |
| |
| if (parseToken(tok::comma, diag::sourceLocation_expected, ",") || |
| parseSpecificIdentifier("line", diag::sourceLocation_expected, |
| "line:") || |
| parseToken(tok::colon, diag::sourceLocation_expected, ":")) |
| return makeParserError(); |
| |
| if (Tok.isNot(tok::integer_literal)) { |
| diagnose(Tok, diag::expected_line_directive_number); |
| return makeParserError(); |
| } |
| if (Tok.getText().getAsInteger(0, StartLine)) { |
| diagnose(Tok, diag::expected_line_directive_number); |
| return makeParserError(); |
| } |
| if (StartLine == 0) { |
| diagnose(Tok, diag::line_directive_line_zero); |
| return makeParserError(); |
| } |
| consumeToken(tok::integer_literal); |
| } |
| |
| if (Tok.isNot(tok::r_paren)) { |
| diagnose(Tok, diag::sourceLocation_expected, ")"); |
| return makeParserError(); |
| } |
| } else { // Legacy #line syntax. |
| |
| // #line\n returns to the main buffer. |
| if (Tok.isAtStartOfLine()) { |
| if (!WasInPoundLineEnvironment) { |
| diagnose(Tok, diag::unexpected_line_directive); |
| return makeParserError(); |
| } |
| return makeParserSuccess(); |
| } |
| |
| // #line 42 "file.swift"\n |
| if (Tok.isNot(tok::integer_literal)) { |
| diagnose(Tok, diag::expected_line_directive_number); |
| return makeParserError(); |
| } |
| if (Tok.getText().getAsInteger(0, StartLine)) { |
| diagnose(Tok, diag::expected_line_directive_number); |
| return makeParserError(); |
| } |
| if (StartLine == 0) { |
| diagnose(Tok, diag::line_directive_line_zero); |
| return makeParserError(); |
| } |
| consumeToken(tok::integer_literal); |
| |
| if (Tok.isNot(tok::string_literal)) { |
| diagnose(Tok, diag::expected_line_directive_name); |
| return makeParserError(); |
| } |
| |
| Filename = getStringLiteralIfNotInterpolated(Loc, "'#line'"); |
| if (!Filename.hasValue()) |
| return makeParserError(); |
| } |
| |
| const char *LastTokTextEnd = Tok.getText().end(); |
| |
| // Skip over trailing whitespace and a single \n to the start of the next |
| // line. |
| while (*LastTokTextEnd == ' ' || *LastTokTextEnd == '\t') |
| ++LastTokTextEnd; |
| SourceLoc nextLineStartLoc = Lexer::getSourceLoc(LastTokTextEnd); |
| |
| if (*LastTokTextEnd == '\n') |
| nextLineStartLoc = nextLineStartLoc.getAdvancedLoc(1); |
| else { |
| diagnose(Tok.getLoc(), diag::extra_tokens_line_directive); |
| return makeParserError(); |
| } |
| |
| int LineOffset = StartLine - SourceMgr.getLineNumber(nextLineStartLoc); |
| |
| // Create a new virtual file for the region started by the #line marker. |
| bool isNewFile = SourceMgr.openVirtualFile(nextLineStartLoc, |
| Filename.getValue(), LineOffset); |
| assert(isNewFile);(void)isNewFile; |
| |
| // Lexing of next token must be deferred until after virtual file setup. |
| consumeToken(isLine ? tok::string_literal : tok::r_paren); |
| |
| InPoundLineEnvironment = true; |
| return makeParserSuccess(); |
| } |
| |
| /// Parse a typealias decl. |
| /// |
| /// decl-typealias: |
| /// 'typealias' identifier generic-params? '=' type |
| /// generic-where-clause? |
| ParsedSyntaxResult<ParsedDeclSyntax> |
| Parser::parseDeclTypeAliasSyntax(Parser::ParseDeclOptions Flags, |
| Optional<ParsedAttributeListSyntax> attrs, |
| Optional<ParsedModifierListSyntax> modifiers) { |
| ParserPosition startPosition = getParserPosition(); |
| llvm::Optional<SyntaxParsingContext> TmpCtxt; |
| TmpCtxt.emplace(SyntaxContext); |
| TmpCtxt->setBackTracking(); |
| |
| auto typealiasKeyword = consumeTokenSyntax(tok::kw_typealias); |
| |
| ParserStatus status; |
| |
| auto applyIntroducer = [&](ParsedTypealiasDeclSyntaxBuilder &builder) { |
| if (attrs) |
| builder.useAttributes(std::move(*attrs)); |
| if (modifiers) |
| builder.useModifiers(std::move(*modifiers)); |
| builder.useTypealiasKeyword(std::move(typealiasKeyword)); |
| }; |
| |
| // Parse the name. |
| auto name = |
| parseIdentifierDeclNameSyntax(*this, "typealias", [](const Token &next) { |
| return next.isAny(tok::colon, tok::equal); |
| }); |
| if (name.isNull()) { |
| TmpCtxt->setTransparent(); |
| TmpCtxt.reset(); |
| ParsedTypealiasDeclSyntaxBuilder builder(*SyntaxContext); |
| applyIntroducer(builder); |
| return makeParsedError(builder.build()); |
| } |
| |
| // Parse optional generic parameters. |
| Optional<ParsedGenericParameterClauseSyntax> genericParams; |
| if (startsWithLess(Tok)) { |
| auto result = parseGenericParameterClauseSyntax(); |
| status |= result.getStatus(); |
| if (!result.isNull()) |
| genericParams = result.get(); |
| } |
| |
| if (Flags.contains(PD_InProtocol) && !genericParams && !Tok.is(tok::equal)) { |
| // If we're in a protocol and don't see an '=' this looks like leftover |
| // Swift 2 code intending to be an associatedtype. |
| TmpCtxt.reset(); |
| backtrackToPosition(startPosition); |
| return parseDeclAssociatedTypeSyntax(Flags, std::move(attrs), |
| std::move(modifiers)); |
| } |
| |
| TmpCtxt->setTransparent(); |
| TmpCtxt.reset(); |
| |
| ParsedTypealiasDeclSyntaxBuilder builder(*SyntaxContext); |
| applyIntroducer(builder); |
| builder.useIdentifier(name.get()); |
| if (genericParams) |
| builder.useGenericParameterClause(std::move(*genericParams)); |
| |
| // Parse underlying type clause. |
| if (Tok.isAny(tok::equal, tok::colon)) { |
| ParsedTypeInitializerClauseSyntaxBuilder initBuilder(*SyntaxContext); |
| |
| // Parse '='. |
| if (Tok.is(tok::colon)) { |
| // It is a common mistake to write "typealias A : Int" instead of = Int. |
| // Recognize this and produce a fixit. |
| diagnose(Tok, diag::expected_equal_in_typealias) |
| .fixItReplace(Tok.getLoc(), " = "); |
| ignoreToken(tok::colon); |
| } else { |
| initBuilder.useEqual(consumeTokenSyntax()); |
| } |
| |
| // Parse the underlying type. |
| auto underlyingType = parseTypeSyntax(diag::expected_type_in_typealias); |
| status |= underlyingType.getStatus(); |
| if (!underlyingType.isNull()) { |
| initBuilder.useValue(underlyingType.get()); |
| } else { |
| initBuilder.useValue( |
| ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); |
| } |
| builder.useInitializer(initBuilder.build()); |
| } else { |
| diagnose(Tok, diag::expected_equal_in_typealias); |
| status.setIsParseError(); |
| } |
| |
| // Parse optional where clause. |
| if (Tok.is(tok::kw_where)) { |
| bool FirstTypeInComplete = false; |
| auto whereClause = parseGenericWhereClauseSyntax(FirstTypeInComplete); |
| status |= whereClause.getStatus(); |
| builder.useGenericWhereClause(whereClause.get()); |
| } |
| |
| return makeParsedResult(builder.build(), status); |
| } |
| |
| ParserResult<TypeDecl> |
| Parser::parseDeclTypeAlias(Parser::ParseDeclOptions Flags, |
| DeclAttributes &Attributes, SourceLoc leadingLoc) { |
| auto modifiers = SyntaxContext->popIf<ParsedModifierListSyntax>(); |
| auto attrs = SyntaxContext->popIf<ParsedAttributeListSyntax>(); |
| |
| auto parsed = |
| parseDeclTypeAliasSyntax(Flags, std::move(attrs), std::move(modifiers)); |
| assert(!parsed.isNull()); |
| |
| SyntaxContext->addSyntax(parsed.get()); |
| auto syntax = SyntaxContext->topNode<DeclSyntax>(); |
| TypeDecl *result = |
| cast_or_null<TypeDecl>(Generator.generate(syntax, leadingLoc)); |
| return makeParserResult(parsed.getStatus(), result); |
| } |
| |
| /// Parse an associatedtype decl. |
| /// |
| /// decl-associatedtype: |
| /// 'associatedtype' identifier type-inheritance-clause? |
| /// ('=' type)? where-clause? |
| ParsedSyntaxResult<ParsedDeclSyntax> |
| Parser::parseDeclAssociatedTypeSyntax(ParseDeclOptions flags, |
| Optional<ParsedAttributeListSyntax> attrs, |
| Optional<ParsedModifierListSyntax> modifiers) { |
| ParsedAssociatedtypeDeclSyntaxBuilder builder(*SyntaxContext); |
| ParserStatus status; |
| |
| if (attrs) |
| builder.useAttributes(std::move(*attrs)); |
| if (modifiers) |
| builder.useModifiers(std::move(*modifiers)); |
| |
| // Parse 'associatedtype' keyword. |
| // Look for 'typealias' here and diagnose a fixit because parseDeclTypeAlias |
| // can ask us to fix up leftover Swift 2 code intending to be an |
| // associatedtype. |
| auto keywordLoc = Tok.getLoc(); |
| if (Tok.is(tok::kw_typealias)) { |
| diagnose(Tok.getLoc(), diag::typealias_inside_protocol_without_type) |
| .fixItReplace(Tok.getLoc(), "associatedtype"); |
| ignoreToken(tok::kw_typealias); |
| } else { |
| builder.useAssociatedtypeKeyword( |
| consumeTokenSyntax(tok::kw_associatedtype)); |
| } |
| |
| // Parse the name. |
| auto name = parseIdentifierDeclNameSyntax( |
| *this, "associatedtype", |
| [&](const Token &next) { return next.isAny(tok::colon, tok::equal); }); |
| if (name.isNull()) |
| return makeParsedResult(builder.build(), name.getStatus()); |
| assert(name.isSuccess()); |
| builder.useIdentifier(name.get()); |
| |
| // Diagnose generic parameters. |
| if (startsWithLess(Tok)) { |
| auto loc = Tok.getLoc(); |
| ignoreToken(); |
| if (ignoreUntilGreaterInTypeList()) |
| ignoreToken(); |
| |
| diagnose(loc, diag::associated_type_generic_parameter_list) |
| .fixItRemove({loc, PreviousLoc}); |
| } |
| |
| // Parse optional inheritance clause. |
| if (Tok.is(tok::colon)) { |
| auto inheritance = parseTypeInheritanceClauseSyntax( |
| /*allowClassRequirement=*/false, /*allowAnyObject=*/true); |
| status |= inheritance.getStatus(); |
| if (!inheritance.isNull()) |
| builder.useInheritanceClause(inheritance.get()); |
| } |
| |
| // Parse optional default type. |
| if (Tok.is(tok::equal)) { |
| ParsedTypeInitializerClauseSyntaxBuilder initBuilder(*SyntaxContext); |
| initBuilder.useEqual(consumeTokenSyntax(tok::equal)); |
| |
| // Parse type. |
| auto type = parseTypeSyntax(diag::expected_type_in_associatedtype); |
| status |= type.getStatus(); |
| if (!type.isNull()) |
| initBuilder.useValue(type.get()); |
| else |
| initBuilder.useValue( |
| ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); |
| |
| builder.useInitializer(initBuilder.build()); |
| } |
| |
| // Parse optional 'where' clause. |
| if (Tok.is(tok::kw_where)) { |
| bool firstTypeInComplete = false; |
| auto where = parseGenericWhereClauseSyntax(firstTypeInComplete); |
| status |= where.getStatus(); |
| if (!where.isNull()) |
| builder.useGenericWhereClause(where.get()); |
| } |
| |
| // Diagnose if it's not in protocol decl. |
| // TODO: Move this to ASTGen. |
| if (!flags.contains(PD_InProtocol)) { |
| diagnose(keywordLoc, diag::associatedtype_outside_protocol) |
| .fixItReplace(keywordLoc, "typealias"); |
| status.setIsParseError(); |
| } |
| |
| return makeParsedResult(builder.build(), status); |
| } |
| |
| ParserResult<TypeDecl> |
| Parser::parseDeclAssociatedType(Parser::ParseDeclOptions Flags, |
| DeclAttributes &Attributes, |
| SourceLoc leadingLoc) { |
| auto modifiers = SyntaxContext->popIf<ParsedModifierListSyntax>(); |
| auto attrs = SyntaxContext->popIf<ParsedAttributeListSyntax>(); |
| |
| auto parsed = parseDeclAssociatedTypeSyntax(Flags, std::move(attrs), |
| std::move(modifiers)); |
| assert(!parsed.isNull()); |
| |
| SyntaxContext->addSyntax(parsed.get()); |
| auto syntax = SyntaxContext->topNode<AssociatedtypeDeclSyntax>(); |
| auto result = Generator.generate(syntax, leadingLoc); |
| return makeParserResult(parsed.getStatus(), result); |
| } |
| |
| /// This function creates an accessor function (with no body) for a computed |
| /// property or subscript. |
| static AccessorDecl *createAccessorFunc(SourceLoc DeclLoc, |
| ParameterList *param, |
| GenericParamList *GenericParams, |
| ParameterList *Indices, |
| SourceLoc StaticLoc, |
| Parser::ParseDeclOptions Flags, |
| AccessorKind Kind, |
| AbstractStorageDecl *storage, |
| Parser *P, SourceLoc AccessorKeywordLoc) { |
| // First task, set up the value argument list. This is the "newValue" name |
| // (for setters) followed by the index list (for subscripts). For |
| // non-subscript getters, this degenerates down to "()". |
| // |
| // We put the 'newValue' argument before the subscript index list as a |
| // micro-optimization for Objective-C thunk generation. |
| ParameterList *ValueArg; |
| { |
| SmallVector<ParamDecl*, 2> ValueArgElements; |
| SourceLoc StartLoc, EndLoc; |
| if (param) { |
| assert(param->size() == 1 && |
| "Should only have a single parameter in the list"); |
| ValueArgElements.push_back(param->get(0)); |
| StartLoc = param->getStartLoc(); |
| EndLoc = param->getEndLoc(); |
| } |
| |
| if (Indices) { |
| // Create parameter declarations corresponding to each of the |
| // parameter declarations from the subscript declaration. |
| for (ParamDecl *storageParam : *Indices) { |
| // Clone the parameter. Do not clone the parameter type; |
| // this will be filled in by the type-checker. |
| auto accessorParam = |
| new (P->Context) ParamDecl(storageParam->getSpecifierLoc(), |
| storageParam->getArgumentNameLoc(), |
| storageParam->getArgumentName(), |
| storageParam->getNameLoc(), |
| storageParam->getName(), |
| P->CurDeclContext); |
| accessorParam->setVariadic(storageParam->isVariadic()); |
| accessorParam->setAutoClosure(storageParam->isAutoClosure()); |
| |
| // The cloned parameter is implicit. |
| accessorParam->setImplicit(); |
| |
| // It has no default arguments; these will be always be taken |
| // from the subscript declaration. |
| accessorParam->setDefaultArgumentKind(DefaultArgumentKind::None); |
| |
| ValueArgElements.push_back(accessorParam); |
| } |
| |
| if (StartLoc.isInvalid()) { |
| StartLoc = Indices->getStartLoc(); |
| EndLoc = Indices->getEndLoc(); |
| } |
| } |
| |
| ValueArg = ParameterList::create(P->Context, StartLoc, ValueArgElements, |
| EndLoc); |
| } |
| |
| // The typechecker will always fill this in. |
| TypeLoc ReturnType; |
| |
| // Start the function. |
| auto *D = AccessorDecl::create(P->Context, |
| /*FIXME FuncLoc=*/DeclLoc, |
| AccessorKeywordLoc, |
| Kind, storage, |
| StaticLoc, StaticSpellingKind::None, |
| /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), |
| (GenericParams |
| ? GenericParams->clone(P->CurDeclContext) |
| : nullptr), |
| ValueArg, ReturnType, |
| P->CurDeclContext); |
| |
| return D; |
| } |
| |
| static ParamDecl *createSetterAccessorArgument(SourceLoc nameLoc, |
| Identifier name, |
| AccessorKind accessorKind, |
| Parser &P) { |
| // Add the parameter. If no name was specified, the name defaults to |
| // 'value'. |
| bool isNameImplicit = name.empty(); |
| if (isNameImplicit) { |
| const char *implName = |
| accessorKind == AccessorKind::DidSet ? "oldValue" : "newValue"; |
| name = P.Context.getIdentifier(implName); |
| } |
| |
| auto result = new (P.Context) |
| ParamDecl(SourceLoc(), SourceLoc(), |
| Identifier(), nameLoc, name, P.CurDeclContext); |
| |
| if (isNameImplicit) |
| result->setImplicit(); |
| |
| return result; |
| } |
| |
| /// Parse a "(value)" specifier for "set" or "willSet" if present. Create a |
| /// parameter list to represent the spelled argument or return null if none is |
| /// present. |
| static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc, |
| Parser &P, |
| AccessorKind Kind) { |
| // 'set' and 'willSet' have a (value) parameter, 'didSet' takes an (oldValue) |
| // parameter and 'get' and always takes a () parameter. |
| if (Kind != AccessorKind::Set && Kind != AccessorKind::WillSet && |
| Kind != AccessorKind::DidSet) |
| return nullptr; |
| |
| SourceLoc StartLoc, NameLoc, EndLoc; |
| Identifier Name; |
| |
| // If the SpecifierLoc is invalid, then the caller just wants us to synthesize |
| // the default, not actually try to parse something. |
| if (SpecifierLoc.isValid() && P.Tok.is(tok::l_paren)) { |
| SyntaxParsingContext ParamCtx(P.SyntaxContext, SyntaxKind::AccessorParameter); |
| StartLoc = P.consumeToken(tok::l_paren); |
| if (P.Tok.isNot(tok::identifier)) { |
| P.diagnose(P.Tok, diag::expected_accessor_parameter_name, |
| Kind == AccessorKind::Set ? 0 : |
| Kind == AccessorKind::WillSet ? 1 : 2); |
| P.skipUntil(tok::r_paren, tok::l_brace); |
| if (P.Tok.is(tok::r_paren)) |
| EndLoc = P.consumeToken(); |
| else |
| EndLoc = StartLoc; |
| } else { |
| // We have a name. |
| NameLoc = P.consumeIdentifier(&Name); |
| |
| auto DiagID = |
| Kind == AccessorKind::Set ? diag::expected_rparen_set_name : |
| Kind == AccessorKind::WillSet ? diag::expected_rparen_willSet_name : |
| diag::expected_rparen_didSet_name; |
| |
| // Look for the closing ')'. |
| P.parseMatchingToken(tok::r_paren, EndLoc, DiagID, StartLoc); |
| } |
| } |
| |
| if (Name.empty()) NameLoc = SpecifierLoc; |
| auto param = createSetterAccessorArgument(NameLoc, Name, Kind, P); |
| return ParameterList::create(P.Context, StartLoc, param, EndLoc); |
| } |
| |
| static unsigned skipBracedBlock(Parser &P, |
| SyntaxParsingContext *&SyntaxContext) { |
| SyntaxParsingContext CodeBlockContext(SyntaxContext, SyntaxKind::CodeBlock); |
| P.consumeToken(tok::l_brace); |
| |
| // We don't care if a skipped function body contained any of these, so |
| // just ignore them. |
| bool HasPoundDirectives; |
| bool HasOperatorDeclarations; |
| bool HasNestedClassDeclarations; |
| |
| unsigned OpenBraces = skipUntilMatchingRBrace(P, |
| HasPoundDirectives, |
| HasOperatorDeclarations, |
| HasNestedClassDeclarations, |
| SyntaxContext); |
| if (P.consumeIf(tok::r_brace)) |
| OpenBraces--; |
| return OpenBraces; |
| } |
| |
| /// Returns a descriptive name for the given accessor/addressor kind. |
| static StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, |
| bool article) { |
| switch (accessorKind) { |
| case AccessorKind::Get: |
| return article ? "a getter" : "getter"; |
| case AccessorKind::Set: |
| return article ? "a setter" : "setter"; |
| case AccessorKind::Address: |
| return article ? "an addressor" : "addressor"; |
| case AccessorKind::MutableAddress: |
| return article ? "a mutable addressor" : "mutable addressor"; |
| case AccessorKind::Read: |
| return article ? "a 'read' accessor" : "'read' accessor"; |
| case AccessorKind::Modify: |
| return article ? "a 'modify' accessor" : "'modify' accessor"; |
| case AccessorKind::WillSet: |
| return "'willSet'"; |
| case AccessorKind::DidSet: |
| return "'didSet'"; |
| } |
| llvm_unreachable("bad accessor kind"); |
| } |
| |
| static StringRef getAccessorNameForDiagnostic(AccessorDecl *accessor, |
| bool article) { |
| return getAccessorNameForDiagnostic(accessor->getAccessorKind(), |
| article); |
| } |
| |
| static void diagnoseRedundantAccessors(Parser &P, SourceLoc loc, |
| AccessorKind accessorKind, |
| bool isSubscript, |
| AccessorDecl *previous) { |
| assert(accessorKind == previous->getAccessorKind()); |
| |
| P.diagnose(loc, diag::duplicate_accessor, |
| unsigned(isSubscript), |
| getAccessorNameForDiagnostic(previous, /*article*/ true)); |
| P.diagnose(previous->getLoc(), diag::previous_accessor, |
| getAccessorNameForDiagnostic(previous, /*article*/ false), |
| /*already*/ true); |
| } |
| |
| static bool isAllowedInLimitedSyntax(AccessorKind kind) { |
| switch (kind) { |
| case AccessorKind::Get: |
| case AccessorKind::Set: |
| return true; |
| |
| case AccessorKind::Address: |
| case AccessorKind::MutableAddress: |
| case AccessorKind::WillSet: |
| case AccessorKind::DidSet: |
| case AccessorKind::Read: |
| case AccessorKind::Modify: |
| return false; |
| } |
| llvm_unreachable("bad accessor kind"); |
| } |
| |
| struct Parser::ParsedAccessors { |
| SourceLoc LBLoc, RBLoc; |
| SmallVector<AccessorDecl*, 16> Accessors; |
| |
| #define ACCESSOR(ID) AccessorDecl *ID = nullptr; |
| #include "swift/AST/AccessorKinds.def" |
| |
| void record(Parser &P, AbstractStorageDecl *storage, bool invalid); |
| void classify(Parser &P, AbstractStorageDecl *storage, bool invalid); |
| |
| /// Add an accessor. If there's an existing accessor of this kind, |
| /// return it. The new accessor is still remembered but will be |
| /// ignored. |
| AccessorDecl *add(AccessorDecl *accessor); |
| |
| /// Find the first accessor that's not an observing accessor. |
| AccessorDecl *findFirstNonObserver() { |
| for (auto accessor : Accessors) { |
| if (!accessor->isObservingAccessor()) |
| return accessor; |
| } |
| return nullptr; |
| } |
| |
| /// Find the first accessor that can be used to perform mutation. |
| AccessorDecl *findFirstMutator() const { |
| if (Set) return Set; |
| if (Modify) return Modify; |
| if (MutableAddress) return MutableAddress; |
| return nullptr; |
| } |
| }; |
| |
| static bool parseAccessorIntroducer(Parser &P, |
| DeclAttributes &Attributes, |
| AccessorKind &Kind, |
| SourceLoc &Loc) { |
| assert(Attributes.isEmpty()); |
| P.parseDeclAttributeList(Attributes); |
| |
| // Parse the contextual keywords for 'mutating' and 'nonmutating' before |
| // get and set. |
| { |
| SyntaxParsingContext ModifierCtx(P.SyntaxContext, SyntaxKind::DeclModifier); |
| |
| if (P.Tok.isContextualKeyword("mutating")) { |
| P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Mutating); |
| } else if (P.Tok.isContextualKeyword("nonmutating")) { |
| P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_NonMutating); |
| } else if (P.Tok.isContextualKeyword("__consuming")) { |
| P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Consuming); |
| } else { |
| ModifierCtx.setTransparent(); |
| } |
| } |
| |
| if (!P.Tok.is(tok::identifier) || P.Tok.isEscapedIdentifier()) { |
| return true; |
| } |
| #define SUPPRESS_ARTIFICIAL_ACCESSORS 1 |
| #define ACCESSOR_KEYWORD(KEYWORD) |
| #define SINGLETON_ACCESSOR(ID, KEYWORD) \ |
| else if (P.Tok.getRawText() == #KEYWORD) { \ |
| Kind = AccessorKind::ID; \ |
| } |
| #include "swift/AST/AccessorKinds.def" |
| else { |
| return true; |
| } |
| P.Tok.setKind(tok::contextual_keyword); |
| Loc = P.consumeToken(); |
| return false; |
| } |
| |
| ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, |
| GenericParamList *GenericParams, |
| ParameterList *Indices, |
| ParsedAccessors &accessors, |
| AbstractStorageDecl *storage, |
| SourceLoc StaticLoc) { |
| assert(Tok.is(tok::l_brace)); |
| |
| // Properties in protocols use a very limited syntax. |
| // SIL mode and module interfaces use the same syntax. |
| // Otherwise, we have a normal var or subscript declaration and we need |
| // parse the full complement of specifiers, along with their bodies. |
| bool parsingLimitedSyntax = Flags.contains(PD_InProtocol) || |
| SF.Kind == SourceFileKind::SIL; |
| |
| SyntaxParsingContext AccessorListCtx(SyntaxContext, |
| SyntaxKind::AccessorBlock); |
| |
| // If the body is completely empty, preserve it. This is at best a getter with |
| // an implicit fallthrough off the end. |
| if (peekToken().is(tok::r_brace)) { |
| accessors.LBLoc = consumeToken(tok::l_brace); |
| // Give syntax node an empty accessor list. |
| SourceLoc listLoc = accessors.LBLoc.getAdvancedLoc(1); |
| SyntaxContext->addSyntax( |
| ParsedSyntaxRecorder::makeBlankAccessorList(listLoc, *SyntaxContext)); |
| accessors.RBLoc = consumeToken(tok::r_brace); |
| |
| // In the limited syntax, fall out and let the caller handle it. |
| if (parsingLimitedSyntax) |
| return makeParserSuccess(); |
| |
| diagnose(accessors.RBLoc, diag::computed_property_no_accessors, |
| /*subscript*/ Indices != nullptr); |
| return makeParserError(); |
| } |
| |
| auto parseImplicitGetter = [&]() { |
| assert(Tok.is(tok::l_brace)); |
| accessors.LBLoc = Tok.getLoc(); |
| auto getter = |
| createAccessorFunc(Tok.getLoc(), /*ValueNamePattern*/ nullptr, |
| GenericParams, Indices, StaticLoc, Flags, |
| AccessorKind::Get, storage, this, |
| /*AccessorKeywordLoc*/ SourceLoc()); |
| accessors.add(getter); |
| parseAbstractFunctionBody(getter); |
| accessors.RBLoc = getter->getEndLoc(); |
| }; |
| |
| // Prepare backtracking for implicit getter. |
| Optional<BacktrackingScope> backtrack; |
| backtrack.emplace(*this); |
| |
| bool Invalid = false; |
| bool accessorHasCodeCompletion = false; |
| bool IsFirstAccessor = true; |
| accessors.LBLoc = consumeToken(tok::l_brace); |
| while (!Tok.isAny(tok::r_brace, tok::eof)) { |
| Optional<SyntaxParsingContext> AccessorCtx; |
| AccessorCtx.emplace(SyntaxContext, SyntaxKind::AccessorDecl); |
| |
| // Parse introducer if possible. |
| DeclAttributes Attributes; |
| AccessorKind Kind = AccessorKind::Get; |
| SourceLoc Loc; |
| bool NotAccessor = parseAccessorIntroducer( |
| *this, Attributes, Kind, Loc); |
| if (NotAccessor) { |
| AccessorCtx->setTransparent(); |
| AccessorCtx.reset(); |
| |
| if (Tok.is(tok::code_complete)) { |
| if (CodeCompletion) { |
| CodeCompletionExpr *CCE = nullptr; |
| if (IsFirstAccessor && !parsingLimitedSyntax) { |
| // If CC token is the first token after '{', it might be implicit |
| // getter. Set up dummy accessor as the decl context to populate |
| // 'self' decl. |
| |
| // FIXME: if there is already code inside the body, we should fall |
| // through to parseImplicitGetter and handle the completion there so |
| // that we can differentiate a single-expression body from the first |
| // expression in a multi-statement body. |
| auto getter = createAccessorFunc( |
| accessors.LBLoc, /*ValueNamePattern*/ nullptr, GenericParams, |
| Indices, StaticLoc, Flags, AccessorKind::Get, |
| storage, this, /*AccessorKeywordLoc*/ SourceLoc()); |
| CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); |
| getter->setBodyParsed(BraceStmt::create(Context, Tok.getLoc(), |
| ASTNode(CCE), Tok.getLoc(), |
| /*implicit*/ true)); |
| accessors.add(getter); |
| CodeCompletion->setParsedDecl(getter); |
| } else { |
| CodeCompletion->setParsedDecl(storage); |
| } |
| CodeCompletion->completeAccessorBeginning(CCE); |
| } |
| consumeToken(tok::code_complete); |
| accessorHasCodeCompletion = true; |
| break; |
| } |
| |
| // parsingLimitedSyntax mode cannot have a body. |
| if (parsingLimitedSyntax) { |
| diagnose(Tok, diag::expected_getset_in_protocol); |
| Invalid = true; |
| break; |
| } |
| |
| // Cannot have an implicit getter after other accessor. |
| if (!IsFirstAccessor) { |
| diagnose(Tok, diag::expected_accessor_kw); |
| skipUntil(tok::r_brace); |
| // Don't signal an error since we recovered. |
| break; |
| } |
| |
| // This is an implicit getter. Cancel accessor contexts, backtrack to '{' |
| // position. |
| backtrack.reset(); |
| AccessorListCtx.setTransparent(); |
| parseImplicitGetter(); |
| return makeParserSuccess(); |
| } |
| IsFirstAccessor = false; |
| |
| // For now, immediately reject illegal accessors in protocols just to |
| // avoid having to deal with them everywhere. |
| if (parsingLimitedSyntax && !isAllowedInLimitedSyntax(Kind)) { |
| diagnose(Loc, diag::expected_getset_in_protocol); |
| continue; |
| } |
| |
| // 'set' and 'willSet' can have an optional name. This isn't valid in a |
| // protocol, but we parse and then reject it for better QoI. |
| // |
| // set-name ::= '(' identifier ')' |
| if (parsingLimitedSyntax && Tok.is(tok::l_paren)) { |
| diagnose(Loc, diag::protocol_setter_name); |
| } |
| auto *ValueNamePattern = parseOptionalAccessorArgument(Loc, *this, Kind); |
| |
| // Set up a function declaration. |
| auto accessor = createAccessorFunc(Loc, ValueNamePattern, GenericParams, |
| Indices, StaticLoc, Flags, |
| Kind, storage, this, Loc); |
| accessor->getAttrs() = Attributes; |
| |
| // Collect this accessor and detect conflicts. |
| if (auto existingAccessor = accessors.add(accessor)) { |
| diagnoseRedundantAccessors(*this, Loc, Kind, |
| /*subscript*/Indices != nullptr, |
| existingAccessor); |
| } |
| |
| // There's no body in the limited syntax. |
| if (parsingLimitedSyntax) |
| continue; |
| |
| // It's okay not to have a body if there's an external asm name. |
| if (!Tok.is(tok::l_brace)) { |
| // Accessors don't need bodies in module interfaces |
| if (SF.Kind == SourceFileKind::Interface) |
| continue; |
| // _silgen_name'd accessors don't need bodies. |
| if (!Attributes.hasAttribute<SILGenNameAttr>()) { |
| diagnose(Tok, diag::expected_lbrace_accessor, |
| getAccessorNameForDiagnostic(accessor, /*article*/ false)); |
| Invalid = true; |
| break; |
| } |
| continue; |
| } |
| |
| parseAbstractFunctionBody(accessor); |
| } |
| backtrack->cancelBacktrack(); |
| backtrack.reset(); |
| // Collect all explicit accessors to a list. |
| AccessorListCtx.collectNodesInPlace(SyntaxKind::AccessorList); |
| // Parse the final '}'. |
| if (Invalid) |
| skipUntil(tok::r_brace); |
| |
| parseMatchingToken(tok::r_brace, accessors.RBLoc, |
| diag::expected_rbrace_in_getset, accessors.LBLoc); |
| if (accessorHasCodeCompletion) |
| return makeParserCodeCompletionStatus(); |
| return Invalid ? makeParserError() : makeParserSuccess(); |
| } |
| |
| /// Parse the brace-enclosed getter and setter for a variable. |
| ParserResult<VarDecl> |
| Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags, |
| SourceLoc StaticLoc, |
| StaticSpellingKind StaticSpelling, |
| SourceLoc VarLoc, bool hasInitializer, |
| const DeclAttributes &Attributes, |
| SmallVectorImpl<Decl *> &Decls) { |
| bool Invalid = false; |
| |
| // The grammar syntactically requires a simple identifier for the variable |
| // name. Complain if that isn't what we got. But for recovery purposes, |
| // make an effort to look through other things anyway. |
| VarDecl *PrimaryVar = nullptr; |
| bool primaryVarIsWellFormed = true; |
| { |
| Pattern *cur = pattern; |
| TypedPattern *previousTyped = nullptr; |
| while (true) { |
| if (auto typed = dyn_cast<TypedPattern>(cur)) { |
| if (previousTyped) primaryVarIsWellFormed = false; |
| previousTyped = typed; |
| cur = typed->getSubPattern(); |
| } else if (auto paren = dyn_cast<ParenPattern>(cur)) { |
| primaryVarIsWellFormed = false; |
| cur = paren->getSubPattern(); |
| } else if (auto var = dyn_cast<VarPattern>(cur)) { |
| primaryVarIsWellFormed = false; |
| cur = var->getSubPattern(); |
| } else { |
| break; |
| } |
| } |
| |
| if (auto named = dyn_cast<NamedPattern>(cur)) { |
| PrimaryVar = named->getDecl(); |
| } |
| } |
| |
| if (!PrimaryVar || !primaryVarIsWellFormed) { |
| diagnose(pattern->getLoc(), diag::getset_nontrivial_pattern); |
| Invalid = true; |
| } |
| |
| // Create a fake VarDecl and PBD so that we don't have to weaken the |
| // formation rule that an AccessorDecl always has a VarDecl. |
| VarDecl *storage = PrimaryVar; |
| if (!storage) { |
| storage = new (Context) VarDecl(StaticLoc.isValid(), |
| VarDecl::Introducer::Var, |
| /*is capture list*/ false, |
| VarLoc, Identifier(), |
| CurDeclContext); |
| storage->setImplicit(true); |
| storage->setInvalid(true); |
| |
| Pattern *pattern = |
| TypedPattern::createImplicit(Context, new (Context) NamedPattern(storage), |
| ErrorType::get(Context)); |
| PatternBindingEntry entry(pattern, /*EqualLoc*/ SourceLoc(), |
| /*Init*/ nullptr, /*InitContext*/ nullptr); |
| auto binding = PatternBindingDecl::create(Context, StaticLoc, |
| StaticSpelling, |
| VarLoc, entry, CurDeclContext); |
| binding->setInvalid(true); |
| storage->setParentPatternBinding(binding); |
| |
| Decls.push_back(binding); |
| Decls.push_back(storage); |
| } |
| |
| // Parse getter and setter. |
| ParsedAccessors accessors; |
| auto AccessorStatus = parseGetSet(Flags, /*GenericParams=*/nullptr, |
| /*Indices=*/nullptr, accessors, |
| storage, StaticLoc); |
| if (AccessorStatus.hasCodeCompletion()) |
| return makeParserCodeCompletionStatus(); |
| if (AccessorStatus.isError()) |
| Invalid = true; |
| |
| // If we have an invalid case, bail out now. |
| if (!PrimaryVar) |
| return nullptr; |
| |
| TypeLoc TyLoc; |
| if (auto *TP = dyn_cast<TypedPattern>(pattern)) { |
| TyLoc = TP->getTypeLoc(); |
| } |
| |
| if (!TyLoc.hasLocation()) { |
| if (accessors.Get || accessors.Set || accessors.Address || |
| accessors.MutableAddress) { |
| SourceLoc locAfterPattern = pattern->getLoc().getAdvancedLoc( |
| pattern->getBoundName().getLength()); |
| diagnose(pattern->getLoc(), diag::computed_property_missing_type) |
| .fixItInsert(locAfterPattern, ": <# Type #>"); |
| Invalid = true; |
| } |
| } |
| |
| // Reject accessors on 'let's after parsing them (for better recovery). |
| if (PrimaryVar->isLet() && !Attributes.hasAttribute<HasStorageAttr>()) { |
| Diag<> DiagID; |
| if (accessors.WillSet || accessors.DidSet) |
| DiagID = diag::let_cannot_be_observing_property; |
| else if (accessors.Address || accessors.MutableAddress) |
| DiagID = diag::let_cannot_be_addressed_property; |
| else |
| DiagID = diag::let_cannot_be_computed_property; |
| |
| diagnose(accessors.LBLoc, DiagID).fixItReplace(VarLoc, "var"); |
| Invalid = true; |
| } |
| |
| accessors.record(*this, PrimaryVar, Invalid); |
| |
| return makeParserResult(PrimaryVar); |
| } |
| |
| /// Add the given accessor to the collection of parsed accessors. If |
| /// it's the first accessor of its kind, remember it for that purpose |
| /// and return null; otherwise, return the existing accessor. |
| AccessorDecl *Parser::ParsedAccessors::add(AccessorDecl *accessor) { |
| Accessors.push_back(accessor); |
| |
| switch (accessor->getAccessorKind()) { |
| #define ACCESSOR(ID) \ |
| case AccessorKind::ID: \ |
| if (ID) { \ |
| return ID; \ |
| } else { \ |
| ID = accessor; \ |
| return nullptr; \ |
| } |
| #include "swift/AST/AccessorKinds.def" |
| } |
| llvm_unreachable("bad accessor kind"); |
| } |
| |
| /// Record a bunch of parsed accessors into the given abstract storage decl. |
| void Parser::ParsedAccessors::record(Parser &P, AbstractStorageDecl *storage, |
| bool invalid) { |
| classify(P, storage, invalid); |
| storage->setAccessors(LBLoc, Accessors, RBLoc); |
| } |
| |
| static void flagInvalidAccessor(AccessorDecl *func) { |
| if (func) { |
| func->setInvalid(); |
| } |
| } |
| |
| static void diagnoseConflictingAccessors(Parser &P, AccessorDecl *first, |
| AccessorDecl *&second) { |
| if (!second) return; |
| P.diagnose(second->getLoc(), diag::conflicting_accessor, |
| isa<SubscriptDecl>(first->getStorage()), |
| getAccessorNameForDiagnostic(second, /*article*/ true), |
| getAccessorNameForDiagnostic(first, /*article*/ true)); |
| P.diagnose(first->getLoc(), diag::previous_accessor, |
| getAccessorNameForDiagnostic(first, /*article*/ false), |
| /*already*/ false); |
| flagInvalidAccessor(second); |
| } |
| |
| template <class... DiagArgs> |
| static void diagnoseAndIgnoreObservers(Parser &P, |
| Parser::ParsedAccessors &accessors, |
| Diag<unsigned, DiagArgs...> diagnostic, |
| typename std::enable_if<true, DiagArgs>::type... args) { |
| if (auto &accessor = accessors.WillSet) { |
| P.diagnose(accessor->getLoc(), diagnostic, /*willSet*/ 0, args...); |
| flagInvalidAccessor(accessor); |
| } |
| if (auto &accessor = accessors.DidSet) { |
| P.diagnose(accessor->getLoc(), diagnostic, /*didSet*/ 1, args...); |
| flagInvalidAccessor(accessor); |
| } |
| } |
| |
| void Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage, |
| bool invalid) { |
| // If there was a problem parsing accessors, mark all parsed accessors |
| // as invalid to avoid tripping up later invariants. |
| // We also want to avoid diagnose missing accessors if something |
| // was invalid. |
| if (invalid) { |
| for (auto accessor : Accessors) { |
| flagInvalidAccessor(accessor); |
| } |
| } |
| |
| // The observing accessors have very specific restrictions. |
| // Prefer to ignore them. |
| if (WillSet || DidSet) { |
| // For now, we don't support the observing accessors on subscripts. |
| if (isa<SubscriptDecl>(storage)) { |
| diagnoseAndIgnoreObservers(P, *this, |
| diag::observing_accessor_in_subscript); |
| |
| // The observing accessors cannot be combined with other accessors. |
| } else if (auto nonObserver = findFirstNonObserver()) { |
| diagnoseAndIgnoreObservers(P, *this, |
| diag::observing_accessor_conflicts_with_accessor, |
| getAccessorNameForDiagnostic(nonObserver, /*article*/ true)); |
| } |
| } |
| |
| // Okay, observers are out of the way. |
| |
| // 'get', 'read', and a non-mutable addressor are all exclusive. |
| if (Get) { |
| diagnoseConflictingAccessors(P, Get, Read); |
| diagnoseConflictingAccessors(P, Get, Address); |
| } else if (Read) { |
| diagnoseConflictingAccessors(P, Read, Address); |
| } else if (Address) { |
| // Nothing can go wrong. |
| |
| // If there's a writing accessor of any sort, there must also be a |
| // reading accessor. |
| } else if (auto mutator = findFirstMutator()) { |
| if (!invalid) { |
| P.diagnose(mutator->getLoc(), |
| // Don't mention the more advanced accessors if the user |
| // only provided a setter without a getter. |
| (MutableAddress || Modify) |
| ? diag::missing_reading_accessor |
| : diag::missing_getter, |
| isa<SubscriptDecl>(storage), |
| getAccessorNameForDiagnostic(mutator, /*article*/ true)); |
| } |
| |
| // Subscripts always have to have some sort of accessor; they can't be |
| // purely stored. |
| } else if (isa<SubscriptDecl>(storage)) { |
| if (!invalid) { |
| P.diagnose(LBLoc, diag::subscript_without_get); |
| } |
| } |
| |
| // A mutable addressor is exclusive with 'set' and 'modify', but |
| // 'set' and 'modify' can appear together. |
| if (Set) { |
| diagnoseConflictingAccessors(P, Set, MutableAddress); |
| } else if (Modify) { |
| diagnoseConflictingAccessors(P, Modify, MutableAddress); |
| } |
| } |
| |
| |
| /// Parse a 'var' or 'let' declaration, doing no token skipping on error. |
| ParserResult<PatternBindingDecl> |
| Parser::parseDeclVar(ParseDeclOptions Flags, |
| DeclAttributes &Attributes, |
| SmallVectorImpl<Decl *> &Decls, |
| SourceLoc StaticLoc, |
| StaticSpellingKind StaticSpelling, |
| SourceLoc TryLoc, |
| bool HasLetOrVarKeyword) { |
| assert(StaticLoc.isInvalid() || StaticSpelling != StaticSpellingKind::None); |
| |
| if (StaticLoc.isValid()) { |
| if (!Flags.contains(PD_HasContainerType)) { |
| diagnose(Tok, diag::static_var_decl_global_scope, StaticSpelling) |
| .fixItRemove(StaticLoc); |
| StaticLoc = SourceLoc(); |
| StaticSpelling = StaticSpellingKind::None; |
| } else if (Flags.contains(PD_InStruct) || Flags.contains(PD_InEnum) || |
| Flags.contains(PD_InProtocol)) { |
| if (StaticSpelling == StaticSpellingKind::KeywordClass) |
| diagnose(Tok, diag::class_var_not_in_class, |
| Flags.contains(PD_InProtocol)) |
| .fixItReplace(StaticLoc, "static"); |
| } |
| } |
| |
| bool isLet = HasLetOrVarKeyword && Tok.is(tok::kw_let); |
| assert(!HasLetOrVarKeyword || Tok.getKind() == tok::kw_let || |
| Tok.getKind() == tok::kw_var); |
| |
| SourceLoc VarLoc = HasLetOrVarKeyword ? consumeToken() : Tok.getLoc(); |
| |
| // If this is a var in the top-level of script/repl source file, wrap the |
| // PatternBindingDecl in a TopLevelCodeDecl, since it represents executable |
| // code. The VarDecl and any accessor decls (for computed properties) go in |
| // CurDeclContext. |
| // |
| TopLevelCodeDecl *topLevelDecl = nullptr; |
| if (allowTopLevelCode() && CurDeclContext->isModuleScopeContext()) { |
| // The body of topLevelDecl will get set later. |
| topLevelDecl = new (Context) TopLevelCodeDecl(CurDeclContext); |
| } |
| |
| bool HasAccessors = false; // Syntactically has accessor {}'s. |
| ParserStatus Status; |
| |
| unsigned NumDeclsInResult = Decls.size(); |
| |
| // In var/let decl with multiple patterns, accumulate them all in this list |
| // so we can build our singular PatternBindingDecl at the end. |
| SmallVector<PatternBindingEntry, 4> PBDEntries; |
| auto BaseContext = CurDeclContext; |
| |
| // No matter what error path we take, make sure the |
| // PatternBindingDecl/TopLevel code block are added. |
| auto makeResult = |
| [&](ParserStatus Status) -> ParserResult<PatternBindingDecl> { |
| |
| // If we didn't parse any patterns, don't create the pattern binding decl. |
| if (PBDEntries.empty()) |
| return Status; |
| |
| // Now that we've parsed all of our patterns, initializers and accessors, we |
| // can finally create our PatternBindingDecl to represent the |
| // pattern/initializer pairs. |
| auto PBD = PatternBindingDecl::create(Context, StaticLoc, StaticSpelling, |
| VarLoc, PBDEntries, BaseContext); |
| |
| // Wire up any initializer contexts we needed. |
| for (unsigned i : indices(PBDEntries)) { |
| if (auto initContext = PBDEntries[i].getInitContext()) |
| cast<PatternBindingInitializer>(initContext)->setBinding(PBD, i); |
| } |
| |
| // If we're setting up a TopLevelCodeDecl, configure it by setting up the |
| // body that holds PBD and we're done. The TopLevelCodeDecl is already set |
| // up in Decls to be returned to caller. |
| if (topLevelDecl) { |
| PBD->setDeclContext(topLevelDecl); |
| auto range = PBD->getSourceRange(); |
| topLevelDecl->setBody(BraceStmt::create(Context, range.Start, |
| ASTNode(PBD), range.End, true)); |
| Decls.insert(Decls.begin()+NumDeclsInResult, topLevelDecl); |
| return makeParserResult(Status, PBD); |
| } |
| |
| // Otherwise return the PBD in "Decls" to the caller. We add it at a |
| // specific spot to get it in before any accessors, which SILGen seems to |
| // want. |
| Decls.insert(Decls.begin()+NumDeclsInResult, PBD); |
| |
| // Always return the result for PBD. |
| return makeParserResult(Status, PBD); |
| }; |
| SyntaxParsingContext PBListCtx(SyntaxContext, SyntaxKind::PatternBindingList); |
| bool HasNext; |
| do { |
| SyntaxParsingContext PatternBindingCtx(SyntaxContext, |
| SyntaxKind::PatternBinding); |
| Pattern *pattern; |
| { |
| // In our recursive parse, remember that we're in a var/let pattern. |
| llvm::SaveAndRestore<decltype(InVarOrLetPattern)> |
| T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar); |
| |
| auto patternRes = parseTypedPattern(); |
| if (patternRes.hasCodeCompletion()) |
| return makeResult(makeParserCodeCompletionStatus()); |
| if (patternRes.isNull()) |
| return makeResult(makeParserError()); |
| |
| pattern = patternRes.get(); |
| } |
| |
| bool hasOpaqueReturnTy = false; |
| if (auto typedPattern = dyn_cast<TypedPattern>(pattern)) { |
| hasOpaqueReturnTy = |
| isa<OpaqueReturnTypeRepr>(typedPattern->getTypeRepr()); |
| } |
| auto sf = CurDeclContext->getParentSourceFile(); |
| |
| // Configure all vars with attributes, 'static' and parent pattern. |
| pattern->forEachVariable([&](VarDecl *VD) { |
| VD->setStatic(StaticLoc.isValid()); |
| VD->getAttrs() = Attributes; |
| setLocalDiscriminator(VD); |
| Decls.push_back(VD); |
| if (hasOpaqueReturnTy && sf) { |
| sf->addUnvalidatedDeclWithOpaqueResultType(VD); |
| } |
| }); |
| |
| // Check whether we have already established an initializer context. |
| PatternBindingInitializer *initContext = |
| findAttributeInitContent(Attributes); |
| |
| // Remember this pattern/init pair for our ultimate PatternBindingDecl. The |
| // Initializer will be added later when/if it is parsed. |
| PBDEntries.push_back({pattern, /*EqualLoc*/ SourceLoc(), /*Init*/ nullptr, |
| initContext}); |
| |
| Expr *PatternInit = nullptr; |
| |
| // Parse an initializer if present. |
| if (Tok.is(tok::equal)) { |
| SyntaxParsingContext InitCtx(SyntaxContext, SyntaxKind::InitializerClause); |
| // If we're not in a local context, we'll need a context to parse initializers |
| // into (should we have one). This happens for properties and global |
| // variables in libraries. |
| |
| // Record the variables that we're trying to initialize. This allows us |
| // to cleanly reject "var x = x" when "x" isn't bound to an enclosing |
| // decl (even though names aren't injected into scope when the initializer |
| // is parsed). |
| SmallVector<VarDecl *, 4> Vars; |
| Vars.append(DisabledVars.begin(), DisabledVars.end()); |
| pattern->collectVariables(Vars); |
| |
| llvm::SaveAndRestore<decltype(DisabledVars)> |
| RestoreCurVars(DisabledVars, Vars); |
| |
| llvm::SaveAndRestore<decltype(DisabledVarReason)> |
| RestoreReason(DisabledVarReason, diag::var_init_self_referential); |
| |
| // If we have no local context to parse the initial value into, create one |
| // for the PBD we'll eventually create. This allows us to have reasonable |
| // DeclContexts for any closures that may live inside of initializers. |
| if (!CurDeclContext->isLocalContext() && !topLevelDecl && !initContext) |
| initContext = new (Context) PatternBindingInitializer(CurDeclContext); |
| |
| // If we're using a local context (either a TopLevelCodeDecl or a |
| // PatternBindingContext) install it now so that CurDeclContext is set |
| // right when parsing the initializer. |
| Optional<ParseFunctionBody> initParser; |
| Optional<ContextChange> topLevelParser; |
| if (topLevelDecl) |
| topLevelParser.emplace(*this, topLevelDecl, |
| &State->getTopLevelContext()); |
| if (initContext) |
| initParser.emplace(*this, initContext); |
| |
| |
| SourceLoc EqualLoc = consumeToken(tok::equal); |
| PBDEntries.back().setEqualLoc(EqualLoc); |
| |
| ParserResult<Expr> init = parseExpr(diag::expected_init_value); |
| PBDEntries.back().setOriginalInit(init.getPtrOrNull()); |
| |
| // If this Pattern binding was not supposed to have an initializer, but it |
| // did, diagnose this and remove it. |
| if (Flags & PD_DisallowInit && init.isNonNull()) { |
| diagnose(EqualLoc, diag::disallowed_init); |
| init = nullptr; |
| } |
| |
| // Otherwise, if this pattern binding *was* supposed (or allowed) to have |
| // an initializer, but it was a parse error, replace it with ErrorExpr so |
| // that downstream clients know that it was present (well, at least the = |
| // was present). This silences downstream diagnostics checking to make |
| // sure that some PBD's that require initializers actually had them. |
| if (!(Flags & PD_DisallowInit) && init.isNull()) |
| init = makeParserResult(init, new (Context) ErrorExpr(EqualLoc)); |
| |
| |
| // Remember this init for the PatternBindingDecl. |
| PatternInit = init.getPtrOrNull(); |
| PBDEntries.back().setInit(PatternInit); |
| |
| // If we set up an initialization context for a property or module-level |
| // global, record it. |
| PBDEntries.back().setInitContext(initContext); |
| |
| if (init.hasCodeCompletion()) { |
| Status |= init; |
| // If we are doing second pass of code completion, we don't want to |
| // suddenly cut off parsing and throw away the declaration. |
| if (isCodeCompletionFirstPass()) |
| return makeResult(makeParserCodeCompletionStatus()); |
| } |
| |
| if (init.isNull()) |
| return makeResult(makeParserError()); |
| } |
| |
| // If we syntactically match the second decl-var production, with a |
| // var-get-set clause, parse the var-get-set clause. |
| if (Tok.is(tok::l_brace)) { |
| HasAccessors = true; |
| auto boundVar = |
| parseDeclVarGetSet(pattern, Flags, StaticLoc, StaticSpelling, VarLoc, |
| PatternInit != nullptr, Attributes, Decls); |
| if (boundVar.hasCodeCompletion()) |
| return makeResult(makeParserCodeCompletionStatus()); |
| } |
| |
| // Add all parsed vardecls to this scope. |
| addPatternVariablesToScope(pattern); |
| |
| // Propagate back types for simple patterns, like "var A, B : T". |
| if (auto *TP = dyn_cast<TypedPattern>(pattern)) { |
| if (isa<NamedPattern>(TP->getSubPattern()) && PatternInit == nullptr) { |
| for (unsigned i = PBDEntries.size() - 1; i != 0; --i) { |
| Pattern *PrevPat = PBDEntries[i-1].getPattern(); |
| if (!isa<NamedPattern>(PrevPat) || PBDEntries[i-1].getInit()) |
| break; |
| if (HasAccessors) { |
| // FIXME -- offer a fixit to explicitly specify the type |
| diagnose(PrevPat->getLoc(), diag::getset_cannot_be_implied); |
| Status.setIsParseError(); |
| } |
| |
| TypedPattern *NewTP = new (Context) TypedPattern(PrevPat, |
| TP->getTypeRepr()); |
| NewTP->setPropagatedType(); |
| PBDEntries[i-1].setPattern(NewTP); |
| } |
| } |
| } |
| HasNext = consumeIf(tok::comma); |
| } while (HasNext); |
| |
| if (HasAccessors && PBDEntries.size() > 1) { |
| diagnose(VarLoc, diag::disallowed_var_multiple_getset); |
| Status.setIsParseError(); |
| } |
| |
| if (TryLoc.isValid()) { |
| auto inFlightDiag = diagnose(TryLoc, diag::try_on_var_let); |
| |
| if (PBDEntries.size() == 1 && PBDEntries.front().getInit() && |
| !isa<ErrorExpr>(PBDEntries.front().getInit())) { |
| auto *init = PBDEntries.front().getInit(); |
| inFlightDiag.fixItRemoveChars(TryLoc, VarLoc); |
| inFlightDiag.fixItInsert(init->getStartLoc(), "try "); |
| |
| // Note: We can't use TryLoc here because it's outside the PBD source |
| // range. |
| PBDEntries.front().setInit(new (Context) TryExpr(init->getStartLoc(), |
| init)); |
| } |
| } |
| |
| return makeResult(Status); |
| } |
| |
| void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, |
| const DeclAttributes &Attrs) { |
| auto BeginParserPosition = getParserPosition(); |
| SourceRange BodyRange; |
| BodyRange.Start = Tok.getLoc(); |
| |
| // Consume the '{', and find the matching '}'. |
| unsigned OpenBraces = skipBracedBlock(*this, SyntaxContext); |
| if (OpenBraces != 0 && Tok.isNot(tok::code_complete)) { |
| assert(Tok.is(tok::eof)); |
| // We hit EOF, and not every brace has a pair. Recover by searching |
| // for the next decl except variable decls and cutting off before |
| // that point. |
| backtrackToPosition(BeginParserPosition); |
| consumeToken(tok::l_brace); |
| while (Tok.is(tok::kw_var) || Tok.is(tok::kw_let) || |
| (Tok.isNot(tok::eof) && !isStartOfDecl())) { |
| consumeToken(); |
| } |
| } |
| |
| BodyRange.End = PreviousLoc; |
| |
| if (SourceMgr.getCodeCompletionLoc().isInvalid() || |
| SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) { |
| AFD->setBodyDelayed(BodyRange); |
| } else { |
| AFD->setBodySkipped(BodyRange); |
| } |
| } |
| |
| /// Parse a 'func' declaration, returning null on error. The caller |
| /// handles this case and does recovery as appropriate. |
| /// |
| /// \verbatim |
| /// decl-func: |
| /// attribute-list? ('static' | 'class')? 'mutating'? 'func' |
| /// any-identifier generic-params? func-signature where-clause? |
| /// stmt-brace? |
| /// \endverbatim |
| /// |
| /// \note The caller of this method must ensure that the next token is 'func'. |
| ParserResult<FuncDecl> Parser::parseDeclFunc(SourceLoc StaticLoc, |
| StaticSpellingKind StaticSpelling, |
| ParseDeclOptions Flags, |
| DeclAttributes &Attributes, |
| bool HasFuncKeyword) { |
| assert(StaticLoc.isInvalid() || StaticSpelling != StaticSpellingKind::None); |
| |
| if (StaticLoc.isValid()) { |
| if (!Flags.contains(PD_HasContainerType)) { |
| // Reject static functions at global scope. |
| diagnose(Tok, diag::static_func_decl_global_scope, StaticSpelling) |
| .fixItRemove(StaticLoc); |
| StaticLoc = SourceLoc(); |
| StaticSpelling = StaticSpellingKind::None; |
| } else if (Flags.contains(PD_InStruct) || Flags.contains(PD_InEnum) || |
| Flags.contains(PD_InProtocol)) { |
| if (StaticSpelling == StaticSpellingKind::KeywordClass) { |
| diagnose(Tok, diag::class_func_not_in_class, |
| Flags.contains(PD_InProtocol)) |
| .fixItReplace(StaticLoc, "static"); |
| |
| StaticSpelling = StaticSpellingKind::KeywordStatic; |
| } |
| } |
| } |
| |
| SourceLoc FuncLoc = |
| HasFuncKeyword ? consumeToken(tok::kw_func) : Tok.getLoc(); |
| |
| // Parse function name. |
| Identifier SimpleName; |
| SourceLoc NameLoc; |
| if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) { |
| // If the name is an operator token that ends in '<' and the following token |
| // is an identifier, split the '<' off as a separate token. This allows |
| // things like 'func ==<T>(x:T, y:T) {}' to parse as '==' with generic type |
| // variable '<T>' as expected. |
| auto NameStr = Tok.getText(); |
| if (NameStr.size() > 1 && NameStr.back() == '<' && |
| peekToken().is(tok::identifier)) { |
| NameStr = NameStr.slice(0, NameStr.size() - 1); |
| } |
| SimpleName = Context.getIdentifier(NameStr); |
| NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced, |
| NameStr.size()); |
| // Within a protocol, recover from a missing 'static'. |
| if (Flags & PD_InProtocol) { |
| switch (StaticSpelling) { |
| case StaticSpellingKind::None: { |
| diagnose(NameLoc, diag::operator_static_in_protocol, SimpleName.str()) |
| .fixItInsert(FuncLoc, "static "); |
| StaticSpelling = StaticSpellingKind::KeywordStatic; |
| break; |
| } |
| |
| case StaticSpellingKind::KeywordStatic: |
| // Okay, this is correct. |
| break; |
| |
| case StaticSpellingKind::KeywordClass: |
| llvm_unreachable("should have been fixed above"); |
| } |
| } |
| } else { |
| // This non-operator path is quite accepting of what tokens might be a name, |
| // because we're aggressive about recovering/providing good diagnostics for |
| // beginners. |
| auto NameStatus = parseIdentifierDeclName( |
| *this, SimpleName, NameLoc, "function", [&](const Token &next) { |
| return next.isAny(tok::l_paren, tok::arrow, tok::l_brace) || |
| startsWithLess(next); |
| }); |
| if (NameStatus.isError()) |
| return nullptr; |
| } |
| |
| DebuggerContextChange DCC(*this, SimpleName, DeclKind::Func); |
| |
| // Parse the generic-params, if present. |
| Optional<Scope> GenericsScope; |
| GenericsScope.emplace(this, ScopeKind::Generics); |
| GenericParamList *GenericParams; |
| bool SignatureHasCodeCompletion = false; |
| auto GenericParamResult = maybeParseGenericParams(); |
| GenericParams = GenericParamResult.getPtrOrNull(); |
| SignatureHasCodeCompletion |= GenericParamResult.hasCodeCompletion(); |
| if (SignatureHasCodeCompletion && !CodeCompletion) |
| return makeParserCodeCompletionStatus(); |
| |
| DefaultArgumentInfo DefaultArgs; |
| TypeRepr *FuncRetTy = nullptr; |
| DeclName FullName; |
| ParameterList *BodyParams; |
| SourceLoc throwsLoc; |
| bool rethrows; |
| ParserStatus SignatureStatus = |
| parseFunctionSignature(SimpleName, FullName, BodyParams, DefaultArgs, |
| throwsLoc, rethrows, FuncRetTy); |
| |
| SignatureHasCodeCompletion |= SignatureStatus.hasCodeCompletion(); |
| if (SignatureStatus.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return SignatureStatus; |
| } |
| |
| diagnoseWhereClauseInGenericParamList(GenericParams); |
| |
| // Create the decl for the func and add it to the parent scope. |
| auto *FD = FuncDecl::create(Context, StaticLoc, StaticSpelling, |
| FuncLoc, FullName, NameLoc, |
| /*Throws=*/throwsLoc.isValid(), throwsLoc, |
| GenericParams, |
| BodyParams, FuncRetTy, |
| CurDeclContext); |
| |
| // Let the source file track the opaque return type mapping, if any. |
| if (FuncRetTy && isa<OpaqueReturnTypeRepr>(FuncRetTy)) { |
| if (auto sf = CurDeclContext->getParentSourceFile()) { |
| sf->addUnvalidatedDeclWithOpaqueResultType(FD); |
| } |
| } |
| |
| // Parse a 'where' clause if present, adding it to our GenericParamList. |
| if (Tok.is(tok::kw_where)) { |
| ContextChange CC(*this, FD); |
| |
| auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); |
| SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion(); |
| if (whereStatus.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return whereStatus; |
| } |
| } |
| |
| // Protocol method arguments may not have default values. |
| if (Flags.contains(PD_InProtocol) && DefaultArgs.HasDefaultArgument) { |
| diagnose(FuncLoc, diag::protocol_method_argument_init); |
| return nullptr; |
| } |
| |
| // Add the 'rethrows' attribute. |
| if (rethrows) { |
| Attributes.add(new (Context) RethrowsAttr(throwsLoc)); |
| } |
| |
| diagnoseOperatorFixityAttributes(*this, Attributes, FD); |
| // Add the attributes here so if we need them while parsing the body |
| // they are available. |
| FD->getAttrs() = Attributes; |
| |
| // Pass the function signature to code completion. |
| if (SignatureHasCodeCompletion) |
| CodeCompletion->setParsedDecl(FD); |
| |
| DefaultArgs.setFunctionContext(FD, FD->getParameters()); |
| setLocalDiscriminator(FD); |
| |
| if (Flags.contains(PD_InProtocol)) { |
| if (Tok.is(tok::l_brace)) { |
| diagnose(Tok, diag::protocol_method_with_body); |
| skipSingle(); |
| } |
| } else { |
| parseAbstractFunctionBody(FD); |
| } |
| |
| // Exit the scope introduced for the generic parameters. |
| GenericsScope.reset(); |
| |
| addToScope(FD); |
| return DCC.fixupParserResult(FD); |
| } |
| |
| /// Parse function body into \p AFD. |
| void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { |
| Scope S(this, ScopeKind::FunctionBody); |
| |
| // Enter the arguments for the function into a new function-body scope. We |
| // need this even if there is no function body to detect argument name |
| // duplication. |
| if (auto *P = AFD->getImplicitSelfDecl()) |
| addToScope(P); |
| addParametersToScope(AFD->getParameters()); |
| |
| // Establish the new context. |
| ParseFunctionBody CC(*this, AFD); |
| setLocalDiscriminatorToParamList(AFD->getParameters()); |
| |
| if (!Tok.is(tok::l_brace)) { |
| checkForInputIncomplete(); |
| return; |
| } |
| |
| if (IsParsingInterfaceTokens) { |
| // Record the curly braces but nothing inside. |
| SF.recordInterfaceToken("{"); |
| SF.recordInterfaceToken("}"); |
| } |
| llvm::SaveAndRestore<bool> T(IsParsingInterfaceTokens, false); |
| |
| if (isDelayedParsingEnabled()) { |
| consumeAbstractFunctionBody(AFD, AFD->getAttrs()); |
| return; |
| } |
| |
| if (Context.Stats) |
| Context.Stats->getFrontendCounters().NumFunctionsParsed++; |
| |
| ParserResult<BraceStmt> Body = parseBraceItemList(diag::invalid_diagnostic); |
| if (!Body.isNull()) { |
| BraceStmt * BS = Body.get(); |
| AFD->setBodyParsed(BS); |
| |
| // If the body consists of a single expression, turn it into a return |
| // statement. |
| // |
| // But don't do this transformation during code completion, as the source |
| // may be incomplete and the type mismatch in return statement will just |
| // confuse the type checker. |
| if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) { |
| auto Element = BS->getElement(0); |
| if (auto *stmt = Element.dyn_cast<Stmt *>()) { |
| if (isa<FuncDecl>(AFD)) { |
| if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) { |
| if (!returnStmt->hasResult()) { |
| auto returnExpr = TupleExpr::createEmpty(Context, |
| SourceLoc(), |
| SourceLoc(), |
| /*implicit*/true); |
| returnStmt->setResult(returnExpr); |
| AFD->setHasSingleExpressionBody(); |
| AFD->setSingleExpressionBody(returnExpr); |
| } |
| } |
| } |
| } else if (auto *E = Element.dyn_cast<Expr *>()) { |
| if (auto SE = dyn_cast<SequenceExpr>(E->getSemanticsProvidingExpr())) { |
| if (SE->getNumElements() > 1 && isa<AssignExpr>(SE->getElement(1))) { |
| // This is an assignment. We don't want to implicitly return |
| // it. |
| return; |
| } |
| } |
| if (auto F = dyn_cast<FuncDecl>(AFD)) { |
| auto RS = new (Context) ReturnStmt(SourceLoc(), E); |
| BS->setElement(0, RS); |
| AFD->setHasSingleExpressionBody(); |
| AFD->setSingleExpressionBody(E); |
| } else if (auto *F = dyn_cast<ConstructorDecl>(AFD)) { |
| if (F->isFailable() && isa<NilLiteralExpr>(E)) { |
| // If it's a nil literal, just insert return. This is the only |
| // legal thing to return. |
| auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); |
| BS->setElement(0, RS); |
| AFD->setHasSingleExpressionBody(); |
| AFD->setSingleExpressionBody(E); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { |
| assert(AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::Unparsed && |
| "function body should be delayed"); |
| |
| auto bodyRange = AFD->getBodySourceRange(); |
| auto BeginParserPosition = getParserPosition({bodyRange.Start,bodyRange.End}); |
| auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc()); |
| |
| // ParserPositionRAII needs a primed parser to restore to. |
| if (Tok.is(tok::NUM_TOKENS)) |
| consumeTokenWithoutFeedingReceiver(); |
| |
| // Ensure that we restore the parser state at exit. |
| ParserPositionRAII PPR(*this); |
| |
| // Create a lexer that cannot go past the end state. |
| Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); |
| |
| // Temporarily swap out the parser's current lexer with our new one. |
| llvm::SaveAndRestore<Lexer *> T(L, &LocalLex); |
| |
| // Rewind to '{' of the function body. |
| restoreParserPosition(BeginParserPosition); |
| |
| // Re-enter the lexical scope. |
| Scope TopLevelScope(this, ScopeKind::TopLevel); |
| Scope S(this, ScopeKind::FunctionBody); |
| ParseFunctionBody CC(*this, AFD); |
| setLocalDiscriminatorToParamList(AFD->getParameters()); |
| |
| return parseBraceItemList(diag::func_decl_without_brace).getPtrOrNull(); |
| } |
| |
| /// Parse a 'enum' declaration, returning true (and doing no token |
| /// skipping) on error. |
| /// |
| /// \verbatim |
| /// decl-enum: |
| /// 'enum' attribute-list identifier generic-params? inheritance? |
| /// where-clause? '{' decl-enum-body '}' |
| /// decl-enum-body: |
| /// decl* |
| /// \endverbatim |
| ParserResult<EnumDecl> Parser::parseDeclEnum(ParseDeclOptions Flags, |
| DeclAttributes &Attributes) { |
| SourceLoc EnumLoc = consumeToken(tok::kw_enum); |
| |
| Identifier EnumName; |
| SourceLoc EnumNameLoc; |
| ParserStatus Status; |
| |
| Status |= parseIdentifierDeclName( |
| *this, EnumName, EnumNameLoc, "enum", [&](const Token &next) { |
| return next.isAny(tok::colon, tok::l_brace) || startsWithLess(next); |
| }); |
| if (Status.isError()) |
| return nullptr; |
| |
| DebuggerContextChange DCC(*this, EnumName, DeclKind::Enum); |
| |
| // Parse the generic-params, if present. |
| GenericParamList *GenericParams = nullptr; |
| { |
| Scope S(this, ScopeKind::Generics); |
| auto Result = maybeParseGenericParams(); |
| GenericParams = Result.getPtrOrNull(); |
| if (Result.hasCodeCompletion()) |
| return makeParserCodeCompletionStatus(); |
| } |
| |
| EnumDecl *ED = new (Context) EnumDecl(EnumLoc, EnumName, EnumNameLoc, |
| { }, GenericParams, CurDeclContext); |
| setLocalDiscriminator(ED); |
| ED->getAttrs() = Attributes; |
| |
| ContextChange CC(*this, ED); |
| |
| // Parse optional inheritance clause within the context of the enum. |
| if (Tok.is(tok::colon)) { |
| MutableArrayRef<TypeLoc> Inherited; |
| Status |= parseInheritance(Inherited, |
| /*allowClassRequirement=*/false, |
| /*allowAnyObject=*/false); |
| ED->setInherited(Inherited); |
| } |
| |
| diagnoseWhereClauseInGenericParamList(GenericParams); |
| |
| // Parse a 'where' clause if present, adding it to our GenericParamList. |
| if (Tok.is(tok::kw_where)) { |
| auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); |
| Status |= whereStatus; |
| if (whereStatus.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return whereStatus; |
| } |
| } |
| |
| SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock); |
| SourceLoc LBLoc, RBLoc; |
| SourceLoc PosBeforeLB = Tok.getLoc(); |
| if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_enum)) { |
| LBLoc = PreviousLoc; |
| RBLoc = LBLoc; |
| Status.setIsParseError(); |
| } else { |
| Scope S(this, ScopeKind::EnumBody); |
| |
| if (parseMemberDeclList(LBLoc, RBLoc, PosBeforeLB, |
| diag::expected_rbrace_enum, |
| ED)) |
| Status.setIsParseError(); |
| } |
| |
| ED->setBraces({LBLoc, RBLoc}); |
| |
| addToScope(ED); |
| |
| return DCC.fixupParserResult(Status, ED); |
| } |
| |
| /// Parse a 'case' of an enum. |
| /// |
| /// \verbatim |
| /// enum-case: |
| /// identifier type-tuple? |
| /// decl-enum-element: |
| /// 'case' attribute-list enum-case (',' enum-case)* |
| /// \endverbatim |
| ParserResult<EnumCaseDecl> |
| Parser::parseDeclEnumCase(ParseDeclOptions Flags, |
| DeclAttributes &Attributes, |
| llvm::SmallVectorImpl<Decl *> &Decls) { |
| ParserStatus Status; |
| SourceLoc CaseLoc = consumeToken(tok::kw_case); |
| |
| // Parse comma-separated enum elements. |
| SmallVector<EnumElementDecl*, 4> Elements; |
| |
| SourceLoc CommaLoc; |
| for (;;) { |
| SyntaxParsingContext ElementContext(SyntaxContext, |
| SyntaxKind::EnumCaseElement); |
| Identifier Name; |
| SourceLoc NameLoc; |
| |
| // Consume an extraneous '.' so we can recover the case name. |
| SourceLoc DotLoc; |
| consumeIf(tok::period_prefix, DotLoc); |
| |
| // Handle the likely case someone typed 'case X, case Y'. |
| if (Tok.is(tok::kw_case) && CommaLoc.isValid()) { |
| diagnose(Tok, diag::expected_identifier_after_case_comma); |
| Status.setIsParseError(); |
| return Status; |
| } |
| |
| if (Tok.is(tok::identifier)) { |
| Status |= parseIdentifierDeclName( |
| *this, Name, NameLoc, "enum 'case'", [](const Token &next) { |
| return next.isAny(tok::l_paren, tok::kw_case, tok::colon, |
| tok::r_brace); |
| }); |
| assert(Status.isSuccess()); |
| if (DotLoc.isValid()) |
| diagnose(DotLoc, diag::enum_case_dot_prefix) |
| .fixItRemove(DotLoc); |
| } else { |
| NameLoc = CaseLoc; |
| bool NameIsKeyword = Tok.isKeyword(); |
| SourceLoc TokLoc = Tok.getLoc(); |
| StringRef TokText = Tok.getText(); |
| |
| // For recovery, see if the user typed something resembling a switch |
| // "case" label. |
| { |
| BacktrackingScope backtrack(*this); |
| llvm::SaveAndRestore<decltype(InVarOrLetPattern)> |
| T(InVarOrLetPattern, Parser::IVOLP_InMatchingPattern); |
| parseMatchingPattern(/*isExprBasic*/false); |
| |
| if (consumeIf(tok::colon)) { |
| backtrack.cancelBacktrack(); |
| diagnose(CaseLoc, diag::case_outside_of_switch, "case"); |
| Status.setIsParseError(); |
| return Status; |
| } |
| } |
| |
| if (NameIsKeyword) { |
| diagnose(TokLoc, diag::keyword_cant_be_identifier, TokText); |
| diagnose(TokLoc, diag::backticks_to_escape) |
| .fixItReplace(TokLoc, "`" + TokText.str() + "`"); |
| if (!Tok.isAtStartOfLine()) { |
| Name = Context.getIdentifier(Tok.getText()); |
| NameLoc = consumeToken(); |
| } |
| } else if (CommaLoc.isValid()) { |
| diagnose(Tok, diag::expected_identifier_after_case_comma); |
| Status.setIsParseError(); |
| return Status; |
| } else { |
| diagnose(CaseLoc, diag::expected_identifier_in_decl, "enum 'case'"); |
| } |
| } |
| |
| // See if there's a following argument type. |
| ParserResult<ParameterList> ArgParams; |
| SmallVector<Identifier, 4> argumentNames; |
| DefaultArgumentInfo DefaultArgs; |
| if (Tok.isFollowingLParen()) { |
| ArgParams = parseSingleParameterClause(ParameterContextKind::EnumElement, |
| &argumentNames, &DefaultArgs); |
| if (ArgParams.isNull() || ArgParams.hasCodeCompletion()) |
| return ParserStatus(ArgParams); |
| } |
| |
| // See if there's a raw value expression. |
| SourceLoc EqualsLoc; |
| ParserResult<Expr> RawValueExpr; |
| LiteralExpr *LiteralRawValueExpr = nullptr; |
| if (Tok.is(tok::equal)) { |
| SyntaxParsingContext InitContext(SyntaxContext, |
| SyntaxKind::InitializerClause); |
| |
| EqualsLoc = consumeToken(); |
| { |
| CodeCompletionCallbacks::InEnumElementRawValueRAII |
| InEnumElementRawValue(CodeCompletion); |
| if (!CurLocalContext) { |
| // A local context is needed for parsing closures. We want to parse |
| // them anyways for proper diagnosis. |
| LocalContext tempContext{}; |
| CurLocalContext = &tempContext; |
| RawValueExpr = parseExpr(diag::expected_expr_enum_case_raw_value); |
| CurLocalContext = nullptr; |
| } else { |
| RawValueExpr = parseExpr(diag::expected_expr_enum_case_raw_value); |
| } |
| } |
| if (RawValueExpr.hasCodeCompletion()) { |
| Status.setHasCodeCompletion(); |
| return Status; |
| } |
| if (RawValueExpr.isNull()) { |
| Status.setIsParseError(); |
| return Status; |
| } |
| // The raw value must be syntactically a simple literal. |
| LiteralRawValueExpr = dyn_cast<LiteralExpr>(RawValueExpr.getPtrOrNull()); |
| if (!LiteralRawValueExpr |
| || isa<InterpolatedStringLiteralExpr>(LiteralRawValueExpr)) { |
| diagnose(RawValueExpr.getPtrOrNull()->getLoc(), |
| diag::nonliteral_enum_case_raw_value); |
| LiteralRawValueExpr = nullptr; |
| } |
| } |
| |
| // For recovery, again make sure the user didn't try to spell a switch |
| // case label: |
| // 'case Identifier:' or |
| // 'case Identifier where ...:' |
| if (Tok.is(tok::colon) || Tok.is(tok::kw_where)) { |
| diagnose(CaseLoc, diag::case_outside_of_switch, "case"); |
| skipUntilDeclRBrace(); |
| Status.setIsParseError(); |
| return Status; |
| } |
| |
| |
| // Create the element. |
| DeclName FullName; |
| if (ArgParams.isNull()) { |
| FullName = Name; |
| } else { |
| FullName = DeclName(Context, Name, argumentNames); |
| } |
| auto *result = new (Context) EnumElementDecl(NameLoc, FullName, |
| ArgParams.getPtrOrNull(), |
| EqualsLoc, |
| LiteralRawValueExpr, |
| CurDeclContext); |
| |
| DefaultArgs.setFunctionContext(result, result->getParameterList()); |
| |
| if (NameLoc == CaseLoc) { |
| result->setImplicit(); // Parse error |
| } |
| |
| result->getAttrs() = Attributes; |
| Elements.push_back(result); |
| |
| // Continue through the comma-separated list. |
| if (!Tok.is(tok::comma)) |
| break; |
| CommaLoc = consumeToken(tok::comma); |
| } |
| SyntaxContext->collectNodesInPlace(SyntaxKind::EnumCaseElementList); |
| |
| if (!(Flags & PD_AllowEnumElement)) { |
| diagnose(CaseLoc, diag::disallowed_enum_element); |
| // Don't add the EnumElementDecls unless the current context |
| // is allowed to have EnumElementDecls. |
| Status.setIsParseError(); |
| return Status; |
| } |
| |
| // Create and insert the EnumCaseDecl containing all the elements. |
| auto TheCase = EnumCaseDecl::create(CaseLoc, Elements, CurDeclContext); |
| Decls.push_back(TheCase); |
| |
| // Insert the element decls. |
| std::copy(Elements.begin(), Elements.end(), std::back_inserter(Decls)); |
| return makeParserResult(Status, TheCase); |
| } |
| |
| /// Parse a 'struct' declaration, returning true (and doing no token |
| /// skipping) on error. |
| /// |
| /// \verbatim |
| /// decl-struct: |
| /// 'struct' attribute-list identifier generic-params? inheritance? |
| /// where-clause? '{' decl-struct-body '} |
| /// decl-struct-body: |
| /// decl* |
| /// \endverbatim |
| ParserResult<StructDecl> Parser::parseDeclStruct(ParseDeclOptions Flags, |
| DeclAttributes &Attributes) { |
| SourceLoc StructLoc = consumeToken(tok::kw_struct); |
| |
| Identifier StructName; |
| SourceLoc StructNameLoc; |
| ParserStatus Status; |
| |
| Status |= parseIdentifierDeclName( |
| *this, StructName, StructNameLoc, "struct", [&](const Token &next) { |
| return next.isAny(tok::colon, tok::l_brace) || startsWithLess(next); |
| }); |
| if (Status.isError()) |
| return nullptr; |
| |
| DebuggerContextChange DCC (*this, StructName, DeclKind::Struct); |
| |
| // Parse the generic-params, if present. |
| GenericParamList *GenericParams = nullptr; |
| { |
| Scope S(this, ScopeKind::Generics); |
| auto Result = maybeParseGenericParams(); |
| GenericParams = Result.getPtrOrNull(); |
| if (Result.hasCodeCompletion()) |
| return makeParserCodeCompletionStatus(); |
| } |
| |
| StructDecl *SD = new (Context) StructDecl(StructLoc, StructName, |
| StructNameLoc, |
| { }, |
| GenericParams, |
| CurDeclContext); |
| setLocalDiscriminator(SD); |
| SD->getAttrs() = Attributes; |
| |
| ContextChange CC(*this, SD); |
| |
| // Parse optional inheritance clause within the context of the struct. |
| if (Tok.is(tok::colon)) { |
| MutableArrayRef<TypeLoc> Inherited; |
| Status |= parseInheritance(Inherited, |
| /*allowClassRequirement=*/false, |
| /*allowAnyObject=*/false); |
| SD->setInherited(Inherited); |
| } |
| |
| diagnoseWhereClauseInGenericParamList(GenericParams); |
| |
| // Parse a 'where' clause if present, adding it to our GenericParamList. |
| if (Tok.is(tok::kw_where)) { |
| auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); |
| Status |= whereStatus; |
| if (whereStatus.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return whereStatus; |
| } |
| } |
| |
| // Make the entities of the struct as a code block. |
| SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock); |
| SourceLoc LBLoc, RBLoc; |
| SourceLoc PosBeforeLB = Tok.getLoc(); |
| if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_struct)) { |
| LBLoc = PreviousLoc; |
| RBLoc = LBLoc; |
| Status.setIsParseError(); |
| } else { |
| // Parse the body. |
| Scope S(this, ScopeKind::StructBody); |
| |
| if (parseMemberDeclList(LBLoc, RBLoc, PosBeforeLB, |
| diag::expected_rbrace_struct, |
| SD)) |
| Status.setIsParseError(); |
| } |
| |
| SD->setBraces({LBLoc, RBLoc}); |
| |
| addToScope(SD); |
| |
| return DCC.fixupParserResult(Status, SD); |
| } |
| |
| /// Parse a 'class' declaration, doing no token skipping on error. |
| /// |
| /// \verbatim |
| /// decl-class: |
| /// 'class' attribute-list identifier generic-params? inheritance? |
| /// where-clause? '{' decl-class-body '} |
| /// decl-class-body: |
| /// decl* |
| /// \endverbatim |
| ParserResult<ClassDecl> Parser::parseDeclClass(ParseDeclOptions Flags, |
| DeclAttributes &Attributes) { |
| SourceLoc ClassLoc = consumeToken(tok::kw_class); |
| |
| Identifier ClassName; |
| SourceLoc ClassNameLoc; |
| ParserStatus Status; |
| |
| Status |= parseIdentifierDeclName( |
| *this, ClassName, ClassNameLoc, "class", [&](const Token &next) { |
| return next.isAny(tok::colon, tok::l_brace) || startsWithLess(next); |
| }); |
| if (Status.isError()) |
| return nullptr; |
| |
| DebuggerContextChange DCC (*this, ClassName, DeclKind::Class); |
| |
| // Parse the generic-params, if present. |
| GenericParamList *GenericParams = nullptr; |
| { |
| Scope S(this, ScopeKind::Generics); |
| auto Result = maybeParseGenericParams(); |
| GenericParams = Result.getPtrOrNull(); |
| if (Result.hasCodeCompletion()) |
| return makeParserCodeCompletionStatus(); |
| } |
| |
| // Create the class. |
| ClassDecl *CD = new (Context) ClassDecl(ClassLoc, ClassName, ClassNameLoc, |
| { }, GenericParams, CurDeclContext); |
| setLocalDiscriminator(CD); |
| CD->getAttrs() = Attributes; |
| |
| // Parsed classes never have missing vtable entries. |
| CD->setHasMissingVTableEntries(false); |
| |
| ContextChange CC(*this, CD); |
| |
| // Parse optional inheritance clause within the context of the class. |
| if (Tok.is(tok::colon)) { |
| MutableArrayRef<TypeLoc> Inherited; |
| Status |= parseInheritance(Inherited, |
| /*allowClassRequirement=*/false, |
| /*allowAnyObject=*/false); |
| CD->setInherited(Inherited); |
| |
| // Parse python style inheritance clause and replace parentheses with a colon |
| } else if (Tok.is(tok::l_paren)) { |
| bool isParenStyleInheritance = false; |
| { |
| BacktrackingScope backtrack(*this); |
| consumeToken(tok::l_paren); |
| isParenStyleInheritance = canParseType() && |
| Tok.isAny(tok::r_paren, tok::kw_where, tok::l_brace, tok::eof); |
| } |
| if(isParenStyleInheritance) { |
| SourceLoc LParenLoc = consumeToken(tok::l_paren); |
| auto TypeResult = parseType(); |
| if (TypeResult.isNull()) { |
| Status.setIsParseError(); |
| return Status; |
| } |
| SourceLoc RParenLoc; |
| consumeIf(tok::r_paren, RParenLoc); |
| diagnose(LParenLoc, diag::expected_colon_class) |
| .fixItReplace(LParenLoc, ": ") |
| .fixItRemove(RParenLoc); |
| } |
| } |
| |
| diagnoseWhereClauseInGenericParamList(GenericParams); |
| |
| // Parse a 'where' clause if present, adding it to our GenericParamList. |
| if (Tok.is(tok::kw_where)) { |
| auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); |
| Status |= whereStatus; |
| if (whereStatus.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return whereStatus; |
| } |
| } |
| |
| SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock); |
| SourceLoc LBLoc, RBLoc; |
| auto PosBeforeLB = Tok.getLoc(); |
| if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_class)) { |
| LBLoc = PreviousLoc; |
| RBLoc = LBLoc; |
| Status.setIsParseError(); |
| } else { |
| // Parse the body. |
| Scope S(this, ScopeKind::ClassBody); |
| |
| if (parseMemberDeclList(LBLoc, RBLoc, PosBeforeLB, |
| diag::expected_rbrace_class, |
| CD)) |
| Status.setIsParseError(); |
| } |
| |
| CD->setBraces({LBLoc, RBLoc}); |
| |
| addToScope(CD); |
| |
| return DCC.fixupParserResult(Status, CD); |
| } |
| |
| /// Parse a 'protocol' declaration, doing no token skipping on error. |
| /// |
| /// \verbatim |
| /// decl-protocol: |
| /// protocol-head '{' protocol-member* '}' |
| /// |
| /// protocol-head: |
| /// 'protocol' attribute-list identifier inheritance? |
| /// |
| /// protocol-member: |
| /// decl-func |
| /// decl-var-simple |
| /// decl-typealias |
| /// \endverbatim |
| ParserResult<ProtocolDecl> Parser:: |
| parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { |
| SourceLoc ProtocolLoc = consumeToken(tok::kw_protocol); |
| |
| SourceLoc NameLoc; |
| Identifier ProtocolName; |
| ParserStatus Status; |
| |
| Status |= parseIdentifierDeclName( |
| *this, ProtocolName, NameLoc, "protocol", |
| [&](const Token &next) { return next.isAny(tok::colon, tok::l_brace); }); |
| if (Status.isError()) |
| return nullptr; |
| |
| // Protocols don't support generic parameters, but people often want them and |
| // we want to have good error recovery if they try them out. Parse them and |
| // produce a specific diagnostic if present. |
| if (startsWithLess(Tok)) { |
| diagnose(Tok, diag::generic_arguments_protocol); |
| Scope S(this, ScopeKind::Generics); |
| maybeParseGenericParams(); |
| } |
| |
| DebuggerContextChange DCC (*this); |
| |
| // Parse optional inheritance clause. |
| MutableArrayRef<TypeLoc> InheritedProtocols; |
| SourceLoc colonLoc; |
| if (Tok.is(tok::colon)) { |
| colonLoc = Tok.getLoc(); |
| Status |= parseInheritance(InheritedProtocols, |
| /*allowClassRequirement=*/true, |
| /*allowAnyObject=*/true); |
| } |
| |
| TrailingWhereClause *TrailingWhere = nullptr; |
| // Parse a 'where' clause if present. |
| if (Tok.is(tok::kw_where)) { |
| auto whereStatus = parseProtocolOrAssociatedTypeWhereClause( |
| TrailingWhere, /*isProtocol=*/true); |
| if (whereStatus.shouldStopParsing()) |
| return whereStatus; |
| } |
| |
| ProtocolDecl *Proto = new (Context) |
| ProtocolDecl(CurDeclContext, ProtocolLoc, NameLoc, ProtocolName, |
| InheritedProtocols, TrailingWhere); |
| // No need to setLocalDiscriminator: protocols can't appear in local contexts. |
| |
| Proto->getAttrs() = Attributes; |
| |
| ContextChange CC(*this, Proto); |
| Scope ProtocolBodyScope(this, ScopeKind::ProtocolBody); |
| |
| // Parse the body. |
| { |
| SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock); |
| SourceLoc LBraceLoc; |
| SourceLoc RBraceLoc; |
| SourceLoc PosBeforeLB = Tok.getLoc(); |
| if (parseToken(tok::l_brace, LBraceLoc, diag::expected_lbrace_protocol)) { |
| LBraceLoc = PreviousLoc; |
| RBraceLoc = LBraceLoc; |
| Status.setIsParseError(); |
| } else { |
| // Parse the members. |
| if (parseMemberDeclList(LBraceLoc, RBraceLoc, PosBeforeLB, |
| diag::expected_rbrace_protocol, |
| Proto)) |
| Status.setIsParseError(); |
| } |
| |
| // Install the protocol elements. |
| Proto->setBraces({LBraceLoc, RBraceLoc}); |
| } |
| |
| return DCC.fixupParserResult(Status, Proto); |
| } |
| |
| /// Parse a 'subscript' declaration. |
| /// |
| /// \verbatim |
| /// decl-subscript: |
| /// subscript-head get-set |
| /// subscript-head |
| /// attribute-list? 'subscript' parameter-clause '->' type |
| /// \endverbatim |
| ParserResult<SubscriptDecl> |
| Parser::parseDeclSubscript(SourceLoc StaticLoc, |
| StaticSpellingKind StaticSpelling, |
| ParseDeclOptions Flags, |
| DeclAttributes &Attributes, |
| SmallVectorImpl<Decl *> &Decls) { |
| assert(StaticLoc.isInvalid() || StaticSpelling != StaticSpellingKind::None); |
| |
| if (StaticLoc.isValid()) { |
| if (Flags.contains(PD_InStruct) || Flags.contains(PD_InEnum) || |
| Flags.contains(PD_InProtocol)) { |
| if (StaticSpelling == StaticSpellingKind::KeywordClass) { |
| diagnose(Tok, diag::class_subscript_not_in_class, |
| Flags.contains(PD_InProtocol)) |
| .fixItReplace(StaticLoc, "static"); |
| |
| StaticSpelling = StaticSpellingKind::KeywordStatic; |
| } |
| } |
| } |
| |
| ParserStatus Status; |
| SourceLoc SubscriptLoc = consumeToken(tok::kw_subscript); |
| |
| // Diagnose 'subscript' with name. |
| if (Tok.is(tok::identifier) && |
| (peekToken().is(tok::l_paren) || startsWithLess(peekToken()))) { |
| diagnose(Tok, diag::subscript_has_name) |
| .fixItRemove(Tok.getLoc()); |
| consumeToken(tok::identifier); |
| } |
| |
| // Parse the generic-params, if present. |
| Optional<Scope> GenericsScope; |
| GenericsScope.emplace(this, ScopeKind::Generics); |
| GenericParamList *GenericParams; |
| bool SignatureHasCodeCompletion = false; |
| |
| auto Result = maybeParseGenericParams(); |
| GenericParams = Result.getPtrOrNull(); |
| SignatureHasCodeCompletion |= Result.hasCodeCompletion(); |
| |
| if (SignatureHasCodeCompletion && !CodeCompletion) |
| return makeParserCodeCompletionStatus(); |
| |
| // Parse the parameter list. |
| DefaultArgumentInfo DefaultArgs; |
| SmallVector<Identifier, 4> argumentNames; |
| ParserResult<ParameterList> Indices |
| = parseSingleParameterClause(ParameterContextKind::Subscript, |
| &argumentNames, &DefaultArgs); |
| Status |= Indices; |
| |
| SignatureHasCodeCompletion |= Indices.hasCodeCompletion(); |
| if (SignatureHasCodeCompletion && !CodeCompletion) |
| return makeParserCodeCompletionStatus(); |
| |
| SourceLoc ArrowLoc; |
| ParserResult<TypeRepr> ElementTy; |
| { |
| SyntaxParsingContext ReturnCtxt(SyntaxContext, SyntaxKind::ReturnClause); |
| |
| // '->' |
| if (!consumeIf(tok::arrow, ArrowLoc)) { |
| if (!Indices.isParseError()) |
| diagnose(Tok, diag::expected_arrow_subscript); |
| Status.setIsParseError(); |
| } |
| |
| if (!ArrowLoc.isValid() && |
| (Indices.isNull() || Indices.get()->size() == 0)) { |
| // This doesn't look much like a subscript, so let regular recovery take |
| // care of it. |
| return Status; |
| } |
| |
| // type |
| ElementTy = parseDeclResultType(diag::expected_type_subscript); |
| Status |= ElementTy; |
| SignatureHasCodeCompletion |= ElementTy.hasCodeCompletion(); |
| if (SignatureHasCodeCompletion && !CodeCompletion) { |
| return makeParserCodeCompletionStatus(); |
| } |
| if (ElementTy.isNull()) { |
| // Always set an element type. |
| ElementTy = makeParserResult(ElementTy, new (Context) ErrorTypeRepr()); |
| } |
| } |
| |
| diagnoseWhereClauseInGenericParamList(GenericParams); |
| |
| // Build an AST for the subscript declaration. |
| DeclName name = DeclName(Context, DeclBaseName::createSubscript(), |
| argumentNames); |
| auto *Subscript = new (Context) SubscriptDecl(name, |
| StaticLoc, StaticSpelling, |
| SubscriptLoc, Indices.get(), |
| ArrowLoc, ElementTy.get(), |
| CurDeclContext, |
| GenericParams); |
| Subscript->getAttrs() = Attributes; |
| |
| // Let the source file track the opaque return type mapping, if any. |
| if (ElementTy.get() && isa<OpaqueReturnTypeRepr>(ElementTy.get())) { |
| if (auto sf = CurDeclContext->getParentSourceFile()) { |
| sf->addUnvalidatedDeclWithOpaqueResultType(Subscript); |
| } |
| } |
| |
| DefaultArgs.setFunctionContext(Subscript, Subscript->getIndices()); |
| |
| // Parse a 'where' clause if present, adding it to our GenericParamList. |
| if (Tok.is(tok::kw_where)) { |
| ContextChange CC(*this, Subscript); |
| |
| auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); |
| SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion(); |
| if (whereStatus.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return whereStatus; |
| } |
| } |
| |
| // Pass the function signature to code completion. |
| if (SignatureHasCodeCompletion && CodeCompletion) { |
| CodeCompletion->setParsedDecl(Subscript); |
| } |
| |
| Decls.push_back(Subscript); |
| |
| // '{' |
| // Parse getter and setter. |
| ParsedAccessors accessors; |
| if (Tok.isNot(tok::l_brace)) { |
| // Subscript declarations must always have at least a getter, so they need |
| // to be followed by a {. |
| if (!Status.isError()) { |
| if (Flags.contains(PD_InProtocol)) { |
| diagnose(Tok, diag::expected_lbrace_subscript_protocol) |
| .fixItInsertAfter(ElementTy.get()->getEndLoc(), " { get <#set#> }"); |
| } else { |
| diagnose(Tok, diag::expected_lbrace_subscript); |
| } |
| Status.setIsParseError(); |
| } |
| } else { |
| Status |= parseGetSet(Flags, GenericParams, Indices.get(), |
| accessors, Subscript, StaticLoc); |
| } |
| |
| // Now that it's been parsed, set the end location. |
| Subscript->setEndLoc(PreviousLoc); |
| |
| bool Invalid = false; |
| // Reject 'subscript' functions outside of type decls |
| if (!(Flags & PD_HasContainerType)) { |
| diagnose(SubscriptLoc, diag::subscript_decl_wrong_scope); |
| Invalid = true; |
| } |
| |
| accessors.record(*this, Subscript, (Invalid || !Status.isSuccess())); |
| |
| // No need to setLocalDiscriminator because subscripts cannot |
| // validly appear outside of type decls. |
| return makeParserResult(Status, Subscript); |
| } |
| |
| ParserResult<ConstructorDecl> |
| Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { |
| assert(Tok.is(tok::kw_init)); |
| SourceLoc ConstructorLoc = consumeToken(); |
| bool Failable = false, IUO = false; |
| SourceLoc FailabilityLoc; |
| |
| const bool ConstructorsNotAllowed = !(Flags & PD_HasContainerType); |
| |
| // Reject constructors outside of types. |
| if (ConstructorsNotAllowed) { |
| diagnose(Tok, diag::initializer_decl_wrong_scope); |
| } |
| |
| // Parse the '!' or '?' for a failable initializer. |
| if (Tok.isAny(tok::exclaim_postfix, tok::sil_exclamation) || |
| (Tok.isAnyOperator() && Tok.getText() == "!")) { |
| Failable = true; |
| IUO = true; |
| FailabilityLoc = consumeToken(); |
| } else if (Tok.isAny(tok::question_postfix, tok::question_infix)) { |
| Failable = true; |
| FailabilityLoc = consumeToken(); |
| } |
| |
| // Reject named 'init'. e.g. 'init withString(string: str)'. |
| if (Tok.is(tok::identifier) && |
| (peekToken().is(tok::l_paren) || startsWithLess(peekToken()))) { |
| diagnose(Tok, diag::initializer_has_name) |
| .fixItRemove(Tok.getLoc()); |
| consumeToken(tok::identifier); |
| } |
| |
| // Parse the generic-params, if present. |
| Scope S(this, ScopeKind::Generics); |
| auto GPResult = maybeParseGenericParams(); |
| GenericParamList *GenericParams = GPResult.getPtrOrNull(); |
| if (GPResult.hasCodeCompletion()) |
| return makeParserCodeCompletionStatus(); |
| |
| // Parse the parameters. |
| DefaultArgumentInfo DefaultArgs; |
| llvm::SmallVector<Identifier, 4> namePieces; |
| bool SignatureHasCodeCompletion = false; |
| ParserResult<ParameterList> Params |
| = parseSingleParameterClause(ParameterContextKind::Initializer, |
| &namePieces, &DefaultArgs); |
| |
| SignatureHasCodeCompletion |= Params.hasCodeCompletion(); |
| if (Params.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return makeParserCodeCompletionStatus(); |
| } |
| |
| // Protocol initializer arguments may not have default values. |
| if (Flags.contains(PD_InProtocol) && DefaultArgs.HasDefaultArgument) { |
| diagnose(ConstructorLoc, diag::protocol_init_argument_init); |
| return nullptr; |
| } |
| |
| // Parse 'throws' or 'rethrows'. |
| SourceLoc throwsLoc; |
| if (consumeIf(tok::kw_throws, throwsLoc)) { |
| // okay |
| } else if (consumeIf(tok::kw_rethrows, throwsLoc)) { |
| Attributes.add(new (Context) RethrowsAttr(throwsLoc)); |
| } |
| |
| diagnoseWhereClauseInGenericParamList(GenericParams); |
| |
| DeclName FullName(Context, DeclBaseName::createConstructor(), namePieces); |
| auto *CD = new (Context) ConstructorDecl(FullName, ConstructorLoc, |
| Failable, FailabilityLoc, |
| throwsLoc.isValid(), throwsLoc, |
| Params.get(), GenericParams, |
| CurDeclContext); |
| CD->setImplicitlyUnwrappedOptional(IUO); |
| CD->getAttrs() = Attributes; |
| |
| // Parse a 'where' clause if present, adding it to our GenericParamList. |
| if (Tok.is(tok::kw_where)) { |
| ContextChange(*this, CD); |
| |
| auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); |
| SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion(); |
| if (whereStatus.hasCodeCompletion() && !CodeCompletion) { |
| // Trigger delayed parsing, no need to continue. |
| return whereStatus; |
| } |
| } |
| |
| // No need to setLocalDiscriminator. |
| |
| DefaultArgs.setFunctionContext(CD, CD->getParameters()); |
| |
| // Pass the function signature to code completion. |
| if (SignatureHasCodeCompletion) |
| CodeCompletion->setParsedDecl(CD); |
| |
| if (ConstructorsNotAllowed || Params.isParseError()) { |
| // Tell the type checker not to touch this constructor. |
| CD->setInvalid(); |
| } |
| |
| if (Flags.contains(PD_InProtocol)) { |
| if (Tok.is(tok::l_brace)) { |
| diagnose(Tok, diag::protocol_init_with_body); |
| skipSingle(); |
| } |
| } else { |
| parseAbstractFunctionBody(CD); |
| } |
| |
| return makeParserResult(CD); |
| } |
| |
| ParserResult<DestructorDecl> Parser:: |
| parseDeclDeinit(ParseDeclOptions Flags, DeclAttributes &Attributes) { |
| SourceLoc DestructorLoc = consumeToken(tok::kw_deinit); |
| |
| // Parse extraneous parentheses and remove them with a fixit. |
| auto skipParameterListIfPresent = [this] { |
| SourceLoc LParenLoc; |
| if (!consumeIf(tok::l_paren, LParenLoc)) |
| return; |
| SourceLoc RParenLoc; |
| skipUntil(tok::r_paren); |
| |
| if (Tok.is(tok::r_paren)) { |
| SourceLoc RParenLoc = consumeToken(); |
| diagnose(LParenLoc, diag::destructor_params) |
| .fixItRemove(SourceRange(LParenLoc, RParenLoc)); |
| } else { |
| diagnose(Tok, diag::opened_destructor_expected_rparen); |
| diagnose(LParenLoc, diag::opening_paren); |
| } |
| }; |
| |
| // '{' |
| if (!Tok.is(tok::l_brace)) { |
| switch (SF.Kind) { |
| case SourceFileKind::Interface: |
| case SourceFileKind::SIL: |
| // It's okay to have no body for SIL code or module interfaces. |
| break; |
| case SourceFileKind::Library: |
| case SourceFileKind::Main: |
| case SourceFileKind::REPL: |
| if (Tok.is(tok::identifier)) { |
| diagnose(Tok, diag::destructor_has_name).fixItRemove(Tok.getLoc()); |
| consumeToken(); |
| } |
| skipParameterListIfPresent(); |
| if (Tok.is(tok::l_brace)) |
| break; |
| |
| diagnose(Tok, diag::expected_lbrace_destructor); |
| return nullptr; |
| } |
| } |
| |
| auto *DD = new (Context) DestructorDecl(DestructorLoc, CurDeclContext); |
| parseAbstractFunctionBody(DD); |
| |
| DD->getAttrs() = Attributes; |
| |
| // Reject 'destructor' functions outside of classes |
| if (!(Flags & PD_AllowDestructor)) { |
| diagnose(DestructorLoc, diag::destructor_decl_outside_class); |
| |
| // Tell the type checker not to touch this destructor. |
| DD->setInvalid(); |
| } |
| |
| return makeParserResult(DD); |
| } |
| |
| ParserResult<OperatorDecl> |
| Parser::parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes) { |
| SourceLoc OperatorLoc = consumeToken(tok::kw_operator); |
| bool AllowTopLevel = Flags.contains(PD_AllowTopLevel); |
| |
| if (!Tok.isAnyOperator() && !Tok.is(tok::exclaim_postfix)) { |
| // A common error is to try to define an operator with something in the |
| // unicode plane considered to be an operator, or to try to define an |
| // operator like "not". Diagnose this specifically. |
| if (Tok.is(tok::identifier)) |
| diagnose(Tok, diag::identifier_when_expecting_operator, |
| Context.getIdentifier(Tok.getText())); |
| else |
| diagnose(Tok, diag::expected_operator_name_after_operator); |
| |
| // To improve recovery, check to see if we have a { right after this token. |
| // If so, swallow until the end } to avoid tripping over the body of the |
| // malformed operator decl. |
| if (peekToken().is(tok::l_brace)) { |
| consumeToken(); |
| skipSingle(); |
| } |
| |
| return nullptr; |
| } |
| |
| DebuggerContextChange DCC (*this); |
| |
| Identifier Name = Context.getIdentifier(Tok.getText()); |
| SourceLoc NameLoc = consumeToken(); |
| |
| if (Attributes.hasAttribute<PostfixAttr>()) { |
| if (!Name.empty() && (Name.get()[0] == '?' || Name.get()[0] == '!')) |
| diagnose(NameLoc, diag::expected_operator_name_after_operator); |
| } |
| |
| auto Result = parseDeclOperatorImpl(OperatorLoc, Name, NameLoc, Attributes); |
| |
| if (!DCC.movedToTopLevel() && !AllowTopLevel) { |
| diagnose(OperatorLoc, diag::operator_decl_inner_scope); |
| return nullptr; |
| } |
| |
| return DCC.fixupParserResult(Result); |
| } |
| |
| ParserResult<OperatorDecl> |
| Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, |
| SourceLoc NameLoc, DeclAttributes &Attributes) { |
| bool isPrefix = Attributes.hasAttribute<PrefixAttr>(); |
| bool isInfix = Attributes.hasAttribute<InfixAttr>(); |
| bool isPostfix = Attributes.hasAttribute<PostfixAttr>(); |
| |
| // Parse (or diagnose) a specified precedence group and/or |
| // designated protocol. These both look like identifiers, so we |
| // parse them both as identifiers here and sort it out in type |
| // checking. |
| SourceLoc colonLoc; |
| SmallVector<Identifier, 4> identifiers; |
| SmallVector<SourceLoc, 4> identifierLocs; |
| if (Tok.is(tok::colon)) { |
| SyntaxParsingContext GroupCtxt(SyntaxContext, |
| SyntaxKind::OperatorPrecedenceAndTypes); |
| colonLoc = consumeToken(); |
| if (Tok.is(tok::code_complete)) { |
| if (CodeCompletion && !isPrefix && !isPostfix) { |
| CodeCompletion->completeInPrecedenceGroup( |
| SyntaxKind::PrecedenceGroupRelation); |
| } |
| consumeToken(); |
| |
| return makeParserCodeCompletionResult<OperatorDecl>(); |
| } |
| |
| if (Context.LangOpts.EnableOperatorDesignatedTypes) { |
| if (Tok.is(tok::identifier)) { |
| SyntaxParsingContext GroupCtxt(SyntaxContext, |
| SyntaxKind::IdentifierList); |
| |
| Identifier name; |
| identifierLocs.push_back(consumeIdentifier(&name)); |
| identifiers.push_back(name); |
| |
| while (Tok.is(tok::comma)) { |
| auto comma = consumeToken(); |
| |
| if (Tok.is(tok::identifier)) { |
| Identifier name; |
| identifierLocs.push_back(consumeIdentifier(&name)); |
| identifiers.push_back(name); |
| } else { |
| if (Tok.isNot(tok::eof)) { |
| auto otherTokLoc = consumeToken(); |
| diagnose(otherTokLoc, diag::operator_decl_expected_type); |
| } else { |
| diagnose(comma, diag::operator_decl_trailing_comma); |
| } |
| } |
| } |
| } |
| } else if (Tok.is(tok::identifier)) { |
| SyntaxParsingContext GroupCtxt(SyntaxContext, |
| SyntaxKind::IdentifierList); |
| |
| identifiers.push_back(Context.getIdentifier(Tok.getText())); |
| identifierLocs.push_back(consumeToken(tok::identifier)); |
| |
| if (isPrefix || isPostfix) { |
| diagnose(colonLoc, diag::precedencegroup_not_infix) |
| .fixItRemove({colonLoc, identifierLocs.back()}); |
| } |
| // Nothing to complete here, simply consume the token. |
| if (Tok.is(tok::code_complete)) |
| consumeToken(); |
| } |
| } |
| |
| // Diagnose deprecated operator body syntax `operator + { ... }`. |
| SourceLoc lBraceLoc; |
| if (consumeIf(tok::l_brace, lBraceLoc)) { |
| if (isInfix && !Tok.is(tok::r_brace)) { |
| diagnose(lBraceLoc, diag::deprecated_operator_body_use_group); |
| } else { |
| auto Diag = diagnose(lBraceLoc, diag::deprecated_operator_body); |
| if (Tok.is(tok::r_brace)) { |
| SourceLoc lastGoodLoc = |
| !identifierLocs.empty() ? identifierLocs.back() : SourceLoc(); |
| if (lastGoodLoc.isInvalid()) |
| lastGoodLoc = NameLoc; |
| SourceLoc lastGoodLocEnd = Lexer::getLocForEndOfToken(SourceMgr, |
| lastGoodLoc); |
| SourceLoc rBraceEnd = Lexer::getLocForEndOfToken(SourceMgr, Tok.getLoc()); |
| Diag.fixItRemoveChars(lastGoodLocEnd, rBraceEnd); |
| } |
| } |
| |
| skipUntilDeclRBrace(); |
| (void) consumeIf(tok::r_brace); |
| } |
| |
| OperatorDecl *res; |
| if (Attributes.hasAttribute<PrefixAttr>()) |
| res = new (Context) |
| PrefixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, |
| Context.AllocateCopy(identifiers), |
| Context.AllocateCopy(identifierLocs)); |
| else if (Attributes.hasAttribute<PostfixAttr>()) |
| res = new (Context) |
| PostfixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, |
| Context.AllocateCopy(identifiers), |
| Context.AllocateCopy(identifierLocs)); |
| else |
| res = new (Context) |
| InfixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, colonLoc, |
| Context.AllocateCopy(identifiers), |
| Context.AllocateCopy(identifierLocs)); |
| |
| diagnoseOperatorFixityAttributes(*this, Attributes, res); |
| |
| res->getAttrs() = Attributes; |
| return makeParserResult(res); |
| } |
| |
| ParserResult<PrecedenceGroupDecl> |
| Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, |
| DeclAttributes &attributes) { |
| SourceLoc precedenceGroupLoc = consumeToken(tok::kw_precedencegroup); |
| DebuggerContextChange DCC (*this); |
| |
| if (!CodeCompletion && !DCC.movedToTopLevel() && !(flags & PD_AllowTopLevel)) |
| { |
| diagnose(precedenceGroupLoc, diag::decl_inner_scope); |
| return nullptr; |
| } |
| |
| Identifier name; |
| SourceLoc nameLoc; |
| if (parseIdentifier(name, nameLoc, diag::expected_precedencegroup_name)) { |
| // If the identifier is missing or a keyword or something, try to |
| // skip the entire body. |
| if (consumeIf(tok::l_brace)) { |
| skipUntilDeclRBrace(); |
| (void) consumeIf(tok::r_brace); |
| } else if (Tok.isNot(tok::eof) && peekToken().is(tok::l_brace)) { |
| consumeToken(); |
| skipBracedBlock(*this, SyntaxContext); |
| } |
| return nullptr; |
| } |
| |
| SourceLoc lbraceLoc, rbraceLoc; |
| SourceLoc associativityKeywordLoc, associativityValueLoc; |
| SourceLoc assignmentKeywordLoc, assignmentValueLoc; |
| SourceLoc higherThanKeywordLoc, lowerThanKeywordLoc; |
| SmallVector<PrecedenceGroupDecl::Relation, 4> higherThan, lowerThan; |
| Associativity associativity = Associativity::None; |
| bool assignment = false; |
| bool invalid = false; |
| bool hasCodeCompletion = false; |
| |
| // Helper functions. |
| auto create = [&] { |
| auto result = PrecedenceGroupDecl::create(CurDeclContext, |
| precedenceGroupLoc, |
| nameLoc, name, lbraceLoc, |
| associativityKeywordLoc, |
| associativityValueLoc, |
| associativity, |
| assignmentKeywordLoc, |
| assignmentValueLoc, |
| assignment, |
| higherThanKeywordLoc, higherThan, |
| lowerThanKeywordLoc, lowerThan, |
| rbraceLoc); |
| result->getAttrs() = attributes; |
| return result; |
| }; |
| auto createInvalid = [&](bool hasCodeCompletion) { |
| // Use the last consumed token location as the rbrace to satisfy |
| // the AST invariant about a decl's source range including all of |
| // its components. |
| if (!rbraceLoc.isValid()) rbraceLoc = PreviousLoc; |
| |
| auto result = create(); |
| result->setInvalid(); |
| if (hasCodeCompletion) |
| return makeParserCodeCompletionResult(result); |
| return makeParserErrorResult(result); |
| }; |
| |
| // Expect the body to start here. |
| if (!consumeIf(tok::l_brace, lbraceLoc)) { |
| diagnose(Tok, diag::expected_precedencegroup_lbrace); |
| return createInvalid(/*hasCodeCompletion*/false); |
| } |
| // Empty body. |
| if (Tok.is(tok::r_brace)) { |
| // Create empty attribute list. |
| SyntaxParsingContext(SyntaxContext, |
| SyntaxKind::PrecedenceGroupAttributeList); |
| rbraceLoc = consumeToken(tok::r_brace); |
| return makeParserResult(create()); |
| } |
| |
| auto abortBody = [&](bool hasCodeCompletion = false) { |
| skipUntilDeclRBrace(); |
| (void) consumeIf(tok::r_brace, rbraceLoc); |
| return createInvalid(hasCodeCompletion); |
| }; |
| |
| auto parseAttributePrefix = [&](SourceLoc &attrKeywordLoc) { |
| auto attrName = Tok.getText(); |
| if (attrKeywordLoc.isValid()) { |
| diagnose(Tok, diag::precedencegroup_attribute_redeclared, attrName); |
| // We want to continue parsing after this. |
| invalid = true; |
| } |
| attrKeywordLoc = consumeToken(tok::identifier); |
| if (!consumeIf(tok::colon)) { |
| diagnose(Tok, diag::expected_precedencegroup_attribute_colon, attrName); |
| // Try to recover by allowing the colon to be missing. |
| } |
| }; |
| |
| auto checkCodeCompletion = [&](SyntaxKind SK) -> bool { |
| if (Tok.is(tok::code_complete)) { |
| if (CodeCompletion) |
| CodeCompletion->completeInPrecedenceGroup(SK); |
| consumeToken(); |
| return true; |
| } |
| return false; |
| }; |
| |
| // Skips the CC token if it comes without spacing. |
| auto skipUnspacedCodeCompleteToken = [&]() -> bool { |
| if (Tok.is(tok::code_complete) && getEndOfPreviousLoc() == Tok.getLoc()) { |
| consumeToken(); |
| return true; |
| } |
| return false; |
| }; |
| |
| // Parse the attributes in the body. |
| while (Tok.isNot(tok::r_brace)) { |
| if (checkCodeCompletion(SyntaxKind::PrecedenceGroupAttributeList)) { |
| hasCodeCompletion = true; |
| continue; |
| } else if (Tok.isNot(tok::identifier)) { |
| diagnose(Tok, diag::expected_precedencegroup_attribute); |
| return abortBody(); |
| } |
| auto attrName = Tok.getText(); |
| |
| if (attrName == "associativity") { |
| SyntaxParsingContext AttrCtxt(SyntaxContext, |
| SyntaxKind::PrecedenceGroupAssociativity); |
| // "associativity" is considered as a contextual keyword. |
| TokReceiver->registerTokenKindChange(Tok.getLoc(), |
| tok::contextual_keyword); |
| parseAttributePrefix(associativityKeywordLoc); |
| |
| if (checkCodeCompletion(SyntaxKind::PrecedenceGroupAssociativity)) |
| return abortBody(/*hasCodeCompletion*/true); |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Tok, diag::expected_precedencegroup_associativity); |
| return abortBody(); |
| } |
| |
| auto parsedAssociativity |
| = llvm::StringSwitch<Optional<Associativity>>(Tok.getText()) |
| .Case("none", Associativity::None) |
| .Case("left", Associativity::Left) |
| .Case("right", Associativity::Right) |
| .Default(None); |
| |
| if (!parsedAssociativity) { |
| diagnose(Tok, diag::expected_precedencegroup_associativity); |
| parsedAssociativity = Associativity::None; |
| invalid = true; |
| } else { |
| // "left", "right" or "none" are considered contextual keywords. |
| TokReceiver->registerTokenKindChange(Tok.getLoc(), |
| tok::contextual_keyword); |
| } |
| associativity = *parsedAssociativity; |
| associativityValueLoc = consumeToken(); |
| |
| if (skipUnspacedCodeCompleteToken()) |
| return abortBody(/*hasCodeCompletion*/true); |
| continue; |
| } |
| |
| if (attrName == "assignment") { |
| SyntaxParsingContext AttrCtxt(SyntaxContext, |
| SyntaxKind::PrecedenceGroupAssignment); |
| parseAttributePrefix(assignmentKeywordLoc); |
| |
| // "assignment" is considered as a contextual keyword. |
| TokReceiver->registerTokenKindChange(assignmentKeywordLoc, |
| tok::contextual_keyword); |
| if (checkCodeCompletion(SyntaxKind::PrecedenceGroupAssignment)) |
| return abortBody(/*hasCodeCompletion*/true); |
| |
| if (consumeIf(tok::kw_true, assignmentValueLoc)) { |
| assignment = true; |
| } else if (consumeIf(tok::kw_false, assignmentValueLoc)) { |
| assignment = false; |
| } else { |
| diagnose(Tok, diag::expected_precedencegroup_assignment); |
| return abortBody(); |
| } |
| if (skipUnspacedCodeCompleteToken()) |
| return abortBody(/*hasCodeCompletion*/true); |
| continue; |
| } |
| |
| bool isLowerThan = false; |
| if (attrName == "higherThan" || |
| (isLowerThan = (attrName == "lowerThan"))) { |
| SyntaxParsingContext AttrCtxt(SyntaxContext, |
| SyntaxKind::PrecedenceGroupRelation); |
| // "lowerThan" and "higherThan" are contextual keywords. |
| TokReceiver->registerTokenKindChange(Tok.getLoc(), |
| tok::contextual_keyword); |
| parseAttributePrefix(isLowerThan ? lowerThanKeywordLoc |
| : higherThanKeywordLoc); |
| auto &relations = (isLowerThan ? lowerThan : higherThan); |
| |
| do { |
| SyntaxParsingContext NameCtxt(SyntaxContext, |
| SyntaxKind::PrecedenceGroupNameElement); |
| if (checkCodeCompletion(SyntaxKind::PrecedenceGroupRelation)) { |
| return abortBody(/*hasCodeCompletion*/true); |
| } |
| |
| if (Tok.isNot(tok::identifier)) { |
| diagnose(Tok, diag::expected_precedencegroup_relation, attrName); |
| return abortBody(); |
| } |
| Identifier name; |
| SourceLoc nameLoc = consumeIdentifier(&name); |
| relations.push_back({nameLoc, name, nullptr}); |
| |
| if (skipUnspacedCodeCompleteToken()) |
| return abortBody(/*hasCodeCompletion*/true); |
| if (!consumeIf(tok::comma)) |
| break; |
| } while (true); |
| SyntaxContext->collectNodesInPlace(SyntaxKind::PrecedenceGroupNameList); |
| continue; |
| } |
| |
| diagnose(Tok, diag::unknown_precedencegroup_attribute, attrName); |
| return abortBody(); |
| } |
| SyntaxContext->collectNodesInPlace(SyntaxKind::PrecedenceGroupAttributeList); |
| rbraceLoc = consumeToken(tok::r_brace); |
| |
| auto result = create(); |
| if (invalid) result->setInvalid(); |
| if (hasCodeCompletion) |
| return makeParserCodeCompletionResult(result); |
| return makeParserResult(result); |
| } |