/// parseTypeComposition /// /// type-composition: /// 'protocol' '<' type-composition-list? '>' /// /// type-composition-list: /// type-identifier (',' type-identifier)* /// ParserResult<ProtocolCompositionTypeRepr> Parser::parseTypeComposition() { SourceLoc ProtocolLoc = consumeToken(tok::kw_protocol); // Check for the starting '<'. if (!startsWithLess(Tok)) { diagnose(Tok, diag::expected_langle_protocol); return nullptr; } SourceLoc LAngleLoc = consumeStartingLess(); // Check for empty protocol composition. if (startsWithGreater(Tok)) { SourceLoc RAngleLoc = consumeStartingGreater(); return makeParserResult(new (Context) ProtocolCompositionTypeRepr( ArrayRef<IdentTypeRepr *>(), ProtocolLoc, SourceRange(LAngleLoc, RAngleLoc))); } // Parse the type-composition-list. ParserStatus Status; SmallVector<IdentTypeRepr *, 4> Protocols; do { // Parse the type-identifier. ParserResult<IdentTypeRepr> Protocol = parseTypeIdentifier(); Status |= Protocol; if (Protocol.isNonNull()) Protocols.push_back(Protocol.get()); } while (consumeIf(tok::comma)); // Check for the terminating '>'. SourceLoc EndLoc = PreviousLoc; if (startsWithGreater(Tok)) { EndLoc = consumeStartingGreater(); } else { if (Status.isSuccess()) { diagnose(Tok, diag::expected_rangle_protocol); diagnose(LAngleLoc, diag::opening_angle); Status.setIsParseError(); } // Skip until we hit the '>'. EndLoc = skipUntilGreaterInTypeList(/*protocolComposition=*/true); } return makeParserResult(Status, ProtocolCompositionTypeRepr::create( Context, Protocols, ProtocolLoc, SourceRange(LAngleLoc, EndLoc))); }
/// parseGenericWhereClause - Parse a 'where' clause, which places additional /// constraints on generic parameters or types based on them. /// /// where-clause: /// 'where' requirement (',' requirement) * /// /// requirement: /// conformance-requirement /// same-type-requirement /// /// conformance-requirement: /// type-identifier ':' type-identifier /// type-identifier ':' type-composition /// /// same-type-requirement: /// type-identifier '==' type ParserStatus Parser::parseGenericWhereClause( SourceLoc &WhereLoc, SmallVectorImpl<RequirementRepr> &Requirements, bool &FirstTypeInComplete) { ParserStatus Status; // Parse the 'where'. WhereLoc = consumeToken(tok::kw_where); FirstTypeInComplete = false; do { // Parse the leading type-identifier. ParserResult<TypeRepr> FirstType = parseTypeIdentifier(); if (FirstType.isNull()) { Status.setIsParseError(); if (FirstType.hasCodeCompletion()) { Status.setHasCodeCompletion(); FirstTypeInComplete = true; } break; } if (Tok.is(tok::colon)) { // A conformance-requirement. SourceLoc ColonLoc = consumeToken(); // Parse the protocol or composition. ParserResult<TypeRepr> Protocol = parseTypeIdentifierOrTypeComposition(); if (Protocol.isNull()) { Status.setIsParseError(); if (Protocol.hasCodeCompletion()) Status.setHasCodeCompletion(); break; } // Add the requirement. Requirements.push_back(RequirementRepr::getTypeConstraint(FirstType.get(), ColonLoc, Protocol.get())); } else if ((Tok.isAnyOperator() && Tok.getText() == "==") || Tok.is(tok::equal)) { // A same-type-requirement if (Tok.is(tok::equal)) { diagnose(Tok, diag::requires_single_equal) .fixItReplace(SourceRange(Tok.getLoc()), "=="); } SourceLoc EqualLoc = consumeToken(); // Parse the second type. ParserResult<TypeRepr> SecondType = parseType(); if (SecondType.isNull()) { Status.setIsParseError(); if (SecondType.hasCodeCompletion()) Status.setHasCodeCompletion(); break; } // Add the requirement Requirements.push_back(RequirementRepr::getSameType(FirstType.get(), EqualLoc, SecondType.get())); } else { diagnose(Tok, diag::expected_requirement_delim); Status.setIsParseError(); break; } // If there's a comma, keep parsing the list. } while (consumeIf(tok::comma)); return Status; }
ParserStatus Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, tok SeparatorK, bool OptionalSep, bool AllowSepAfterLast, Diag<> ErrorDiag, std::function<ParserStatus()> callback) { assert(SeparatorK == tok::comma || SeparatorK == tok::semi); if (Tok.is(RightK)) { RightLoc = consumeToken(RightK); return makeParserSuccess(); } ParserStatus Status; while (true) { while (Tok.is(SeparatorK)) { diagnose(Tok, diag::unexpected_separator, SeparatorK == tok::comma ? "," : ";") .fixItRemove(SourceRange(Tok.getLoc())); consumeToken(); } SourceLoc StartLoc = Tok.getLoc(); Status |= callback(); if (Tok.is(RightK)) break; // If the lexer stopped with an EOF token whose spelling is ")", then this // is actually the tuple that is a string literal interpolation context. // Just accept the ")" and build the tuple as we usually do. if (Tok.is(tok::eof) && Tok.getText() == ")" && RightK == tok::r_paren) { RightLoc = Tok.getLoc(); return Status; } if (consumeIf(SeparatorK)) { if (AllowSepAfterLast && Tok.is(RightK)) break; else continue; } if (!OptionalSep) { // If we're in a comma-separated list and the next token starts a new // declaration at the beginning of a new line, skip until the end. if (SeparatorK == tok::comma && Tok.isAtStartOfLine() && isStartOfDecl() && Tok.getLoc() != StartLoc) { skipUntilDeclRBrace(RightK, SeparatorK); break; } StringRef Separator = (SeparatorK == tok::comma ? "," : ";"); diagnose(Tok, diag::expected_separator, Separator) .fixItInsertAfter(PreviousLoc, Separator); Status.setIsParseError(); } // If we haven't made progress, skip ahead if (Tok.getLoc() == StartLoc) { skipUntilDeclRBrace(RightK, SeparatorK); if (Tok.is(RightK)) break; if (Tok.is(tok::eof) || Tok.is(tok::pound_endif)) { RightLoc = PreviousLoc.isValid()? PreviousLoc : Tok.getLoc(); IsInputIncomplete = true; Status.setIsParseError(); return Status; } if (consumeIf(SeparatorK) || OptionalSep) continue; break; } } if (parseMatchingToken(RightK, RightLoc, ErrorDiag, LeftLoc)) { Status.setIsParseError(); } return Status; }
/// parseTypeIdentifierOrTypeComposition /// - Identifiers and compositions both start with the same identifier /// token, parse it and continue constructing a composition if the /// next token is '&' /// /// type-composition: /// type-identifier ('&' type-identifier)* /// 'protocol' '<' type-composition-list-deprecated? '>' /// /// type-composition-list-deprecated: /// type-identifier (',' type-identifier)* ParserResult<TypeRepr> Parser::parseTypeIdentifierOrTypeComposition() { // Handle deprecated case if (Tok.getKind() == tok::kw_protocol && startsWithLess(peekToken())) { SourceLoc ProtocolLoc = consumeToken(tok::kw_protocol); SourceLoc LAngleLoc = consumeStartingLess(); // Parse the type-composition-list. ParserStatus Status; SmallVector<IdentTypeRepr *, 4> Protocols; bool IsEmpty = startsWithGreater(Tok); if (!IsEmpty) { do { // Parse the type-identifier. ParserResult<TypeRepr> Protocol = parseTypeIdentifier(); Status |= Protocol; if (auto *ident = dyn_cast_or_null<IdentTypeRepr>( Protocol.getPtrOrNull())) Protocols.push_back(ident); } while (consumeIf(tok::comma)); } // Check for the terminating '>'. SourceLoc RAngleLoc = PreviousLoc; if (startsWithGreater(Tok)) { RAngleLoc = consumeStartingGreater(); } else { if (Status.isSuccess()) { diagnose(Tok, diag::expected_rangle_protocol); diagnose(LAngleLoc, diag::opening_angle); Status.setIsParseError(); } // Skip until we hit the '>'. RAngleLoc = skipUntilGreaterInTypeList(/*protocolComposition=*/true); } auto composition = ProtocolCompositionTypeRepr::create( Context, Protocols, ProtocolLoc, {LAngleLoc, RAngleLoc}); if (Status.isSuccess()) { // Only if we have complete protocol<...> construct, diagnose deprecated. SmallString<32> replacement; if (Protocols.empty()) { replacement = "Any"; } else { auto extractText = [&](IdentTypeRepr *Ty) -> StringRef { auto SourceRange = Ty->getSourceRange(); return SourceMgr.extractText( Lexer::getCharSourceRangeFromSourceRange(SourceMgr, SourceRange)); }; auto Begin = Protocols.begin(); replacement += extractText(*Begin); while (++Begin != Protocols.end()) { replacement += " & "; replacement += extractText(*Begin); } } // Copy trailing content after '>' to the replacement string. // FIXME: lexer should smartly separate '>' and trailing contents like '?'. StringRef TrailingContent = L->getTokenAt(RAngleLoc).getRange().str(). substr(1); if (!TrailingContent.empty()) { if (Protocols.size() > 1) { replacement.insert(replacement.begin(), '('); replacement += ")"; } replacement += TrailingContent; } // Replace 'protocol<T1, T2>' with 'T1 & T2' diagnose(ProtocolLoc, IsEmpty ? diag::deprecated_any_composition : Protocols.size() > 1 ? diag::deprecated_protocol_composition : diag::deprecated_protocol_composition_single) .highlight(composition->getSourceRange()) .fixItReplace(composition->getSourceRange(), replacement); } return makeParserResult(Status, composition); } SourceLoc FirstTypeLoc = Tok.getLoc(); // Parse the first type ParserResult<TypeRepr> FirstType = parseTypeIdentifier(); if (!Tok.isContextualPunctuator("&")) return FirstType; SmallVector<IdentTypeRepr *, 4> Protocols; ParserStatus Status; // If it is not 'Any', add it to the protocol list if (auto *ident = dyn_cast_or_null<IdentTypeRepr>(FirstType.getPtrOrNull())) Protocols.push_back(ident); Status |= FirstType; auto FirstAmpersandLoc = Tok.getLoc(); while (Tok.isContextualPunctuator("&")) { consumeToken(); // consume '&' ParserResult<TypeRepr> Protocol = parseTypeIdentifier(); Status |= Protocol; if (auto *ident = dyn_cast_or_null<IdentTypeRepr>(Protocol.getPtrOrNull())) Protocols.push_back(ident); }; return makeParserResult(Status, ProtocolCompositionTypeRepr::create( Context, Protocols, FirstTypeLoc, {FirstAmpersandLoc, PreviousLoc})); }
/// parseTypeIdentifier /// /// type-identifier: /// identifier generic-args? ('.' identifier generic-args?)* /// ParserResult<TypeRepr> Parser::parseTypeIdentifier() { if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) { // is this the 'Any' type if (Tok.is(tok::kw_Any)) { return parseAnyType(); } else if (Tok.is(tok::code_complete)) { if (CodeCompletion) CodeCompletion->completeTypeSimpleBeginning(); // Eat the code completion token because we handled it. consumeToken(tok::code_complete); return makeParserCodeCompletionResult<IdentTypeRepr>(); } diagnose(Tok, diag::expected_identifier_for_type); // If there is a keyword at the start of a new line, we won't want to // skip it as a recovery but rather keep it. if (Tok.isKeyword() && !Tok.isAtStartOfLine()) consumeToken(); return nullptr; } ParserStatus Status; SmallVector<ComponentIdentTypeRepr *, 4> ComponentsR; SourceLoc EndLoc; while (true) { SourceLoc Loc; Identifier Name; if (Tok.is(tok::kw_Self)) { Loc = consumeIdentifier(&Name); } else { // FIXME: specialize diagnostic for 'Type': type cannot start with // 'metatype' // FIXME: offer a fixit: 'self' -> 'Self' if (parseIdentifier(Name, Loc, diag::expected_identifier_in_dotted_type)) Status.setIsParseError(); } if (Loc.isValid()) { SourceLoc LAngle, RAngle; SmallVector<TypeRepr*, 8> GenericArgs; if (startsWithLess(Tok)) { if (parseGenericArguments(GenericArgs, LAngle, RAngle)) return nullptr; } EndLoc = Loc; ComponentIdentTypeRepr *CompT; if (!GenericArgs.empty()) CompT = new (Context) GenericIdentTypeRepr(Loc, Name, Context.AllocateCopy(GenericArgs), SourceRange(LAngle, RAngle)); else CompT = new (Context) SimpleIdentTypeRepr(Loc, Name); ComponentsR.push_back(CompT); } // Treat 'Foo.<anything>' as an attempt to write a dotted type // unless <anything> is 'Type'. if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) { if (peekToken().is(tok::code_complete)) { Status.setHasCodeCompletion(); break; } if (!peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { consumeToken(); continue; } } else if (Tok.is(tok::code_complete)) { if (!Tok.isAtStartOfLine()) Status.setHasCodeCompletion(); break; } break; } IdentTypeRepr *ITR = nullptr; if (!ComponentsR.empty()) { // Lookup element #0 through our current scope chains in case it is some // thing local (this returns null if nothing is found). if (auto Entry = lookupInScope(ComponentsR[0]->getIdentifier())) if (isa<TypeDecl>(Entry)) ComponentsR[0]->setValue(Entry); ITR = IdentTypeRepr::create(Context, ComponentsR); } if (Status.hasCodeCompletion() && CodeCompletion) { if (Tok.isNot(tok::code_complete)) { // We have a dot. consumeToken(); CodeCompletion->completeTypeIdentifierWithDot(ITR); } else { CodeCompletion->completeTypeIdentifierWithoutDot(ITR); } // Eat the code completion token because we handled it. consumeToken(tok::code_complete); } return makeParserResult(Status, ITR); }
ParserStatus Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, bool AllowSepAfterLast, Diag<> ErrorDiag, SyntaxKind Kind, std::function<ParserStatus()> callback) { llvm::Optional<SyntaxParsingContext> ListContext; ListContext.emplace(SyntaxContext, Kind); if (Kind == SyntaxKind::Unknown) ListContext->setTransparent(); SyntaxKind ElementKind = getListElementKind(Kind); if (Tok.is(RightK)) { ListContext.reset(); RightLoc = consumeToken(RightK); return makeParserSuccess(); } ParserStatus Status; while (true) { while (Tok.is(tok::comma)) { diagnose(Tok, diag::unexpected_separator, ",") .fixItRemove(SourceRange(Tok.getLoc())); consumeToken(); } SourceLoc StartLoc = Tok.getLoc(); SyntaxParsingContext ElementContext(SyntaxContext, ElementKind); if (ElementKind == SyntaxKind::Unknown) ElementContext.setTransparent(); Status |= callback(); if (Tok.is(RightK)) break; // If the lexer stopped with an EOF token whose spelling is ")", then this // is actually the tuple that is a string literal interpolation context. // Just accept the ")" and build the tuple as we usually do. if (Tok.is(tok::eof) && Tok.getText() == ")" && RightK == tok::r_paren) { RightLoc = Tok.getLoc(); return Status; } // If we haven't made progress, or seeing any error, skip ahead. if (Tok.getLoc() == StartLoc || Status.isError()) { assert(Status.isError() && "no progress without error"); skipUntilDeclRBrace(RightK, tok::comma); if (Tok.is(RightK) || Tok.isNot(tok::comma)) break; } if (consumeIf(tok::comma)) { if (Tok.isNot(RightK)) continue; if (!AllowSepAfterLast) { diagnose(Tok, diag::unexpected_separator, ",") .fixItRemove(SourceRange(PreviousLoc)); } break; } // If we're in a comma-separated list, the next token is at the // beginning of a new line and can never start an element, break. if (Tok.isAtStartOfLine() && (Tok.is(tok::r_brace) || isStartOfDecl() || isStartOfStmt())) { break; } // If we found EOF or such, bailout. if (Tok.isAny(tok::eof, tok::pound_endif)) { IsInputIncomplete = true; break; } diagnose(Tok, diag::expected_separator, ",") .fixItInsertAfter(PreviousLoc, ","); Status.setIsParseError(); } ListContext.reset(); if (Status.isError()) { // If we've already got errors, don't emit missing RightK diagnostics. RightLoc = Tok.is(RightK) ? consumeToken() : PreviousLoc; } else if (parseMatchingToken(RightK, RightLoc, ErrorDiag, LeftLoc)) { Status.setIsParseError(); } return Status; }
/// Parse a function definition signature. /// func-signature: /// func-arguments func-throws? func-signature-result? /// func-signature-result: /// '->' type /// /// Note that this leaves retType as null if unspecified. ParserStatus Parser::parseFunctionSignature(Identifier SimpleName, DeclName &FullName, SmallVectorImpl<ParameterList*> &bodyParams, DefaultArgumentInfo &defaultArgs, SourceLoc &throwsLoc, bool &rethrows, TypeRepr *&retType) { SmallVector<Identifier, 4> NamePieces; NamePieces.push_back(SimpleName); FullName = SimpleName; ParserStatus Status; // We force first type of a func declaration to be a tuple for consistency. if (Tok.is(tok::l_paren)) { ParameterContextKind paramContext; if (SimpleName.isOperator()) paramContext = ParameterContextKind::Operator; else paramContext = ParameterContextKind::Function; Status = parseFunctionArguments(NamePieces, bodyParams, paramContext, defaultArgs); FullName = DeclName(Context, SimpleName, llvm::makeArrayRef(NamePieces.begin() + 1, NamePieces.end())); if (bodyParams.empty()) { // If we didn't get anything, add a () pattern to avoid breaking // invariants. assert(Status.hasCodeCompletion() || Status.isError()); bodyParams.push_back(ParameterList::createEmpty(Context)); } } else { diagnose(Tok, diag::func_decl_without_paren); Status = makeParserError(); // Recover by creating a '() -> ?' signature. bodyParams.push_back(ParameterList::createEmpty(Context, PreviousLoc, PreviousLoc)); FullName = DeclName(Context, SimpleName, bodyParams.back()); } // Check for the 'throws' keyword. rethrows = false; if (Tok.is(tok::kw_throws)) { throwsLoc = consumeToken(); } else if (Tok.is(tok::kw_rethrows)) { throwsLoc = consumeToken(); rethrows = true; } else if (Tok.is(tok::kw_throw)) { throwsLoc = consumeToken(); diagnose(throwsLoc, diag::throw_in_function_type) .fixItReplace(throwsLoc, "throws"); } SourceLoc arrowLoc; // If there's a trailing arrow, parse the rest as the result type. if (Tok.isAny(tok::arrow, tok::colon)) { if (!consumeIf(tok::arrow, arrowLoc)) { // FixIt ':' to '->'. diagnose(Tok, diag::func_decl_expected_arrow) .fixItReplace(SourceRange(Tok.getLoc()), "->"); arrowLoc = consumeToken(tok::colon); } ParserResult<TypeRepr> ResultType = parseType(diag::expected_type_function_result); if (ResultType.hasCodeCompletion()) return ResultType; retType = ResultType.getPtrOrNull(); if (!retType) { Status.setIsParseError(); return Status; } } else { // Otherwise, we leave retType null. retType = nullptr; } // Check for 'throws' and 'rethrows' after the type and correct it. if (!throwsLoc.isValid()) { if (Tok.is(tok::kw_throws)) { throwsLoc = consumeToken(); } else if (Tok.is(tok::kw_rethrows)) { throwsLoc = consumeToken(); rethrows = true; } if (throwsLoc.isValid()) { assert(arrowLoc.isValid()); assert(retType); auto diag = rethrows ? diag::rethrows_after_function_result : diag::throws_after_function_result; SourceLoc typeEndLoc = Lexer::getLocForEndOfToken(SourceMgr, retType->getEndLoc()); SourceLoc throwsEndLoc = Lexer::getLocForEndOfToken(SourceMgr, throwsLoc); diagnose(Tok, diag) .fixItInsert(arrowLoc, rethrows ? "rethrows " : "throws ") .fixItRemoveChars(typeEndLoc, throwsEndLoc); } } return Status; }
ParserStatus Parser::parseParameterClause(SourceLoc &leftParenLoc, SmallVectorImpl<ParsedParameter> ¶ms, SourceLoc &rightParenLoc, DefaultArgumentInfo *defaultArgs, ParameterContextKind paramContext) { assert(params.empty() && leftParenLoc.isInvalid() && rightParenLoc.isInvalid() && "Must start with empty state"); // Consume the starting '('; leftParenLoc = consumeToken(tok::l_paren); // Trivial case: empty parameter list. if (Tok.is(tok::r_paren)) { rightParenLoc = consumeToken(tok::r_paren); return ParserStatus(); } // Parse the parameter list. bool isClosure = paramContext == ParameterContextKind::Closure; return parseList(tok::r_paren, leftParenLoc, rightParenLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/false, diag::expected_rparen_parameter, [&]() -> ParserStatus { ParsedParameter param; ParserStatus status; SourceLoc StartLoc = Tok.getLoc(); unsigned defaultArgIndex = defaultArgs ? defaultArgs->NextIndex++ : 0; // Attributes. bool FoundCCToken; parseDeclAttributeList(param.Attrs, FoundCCToken); if (FoundCCToken) { if (CodeCompletion) { CodeCompletion->completeDeclAttrKeyword(nullptr, isInSILMode(), true); } else { status |= makeParserCodeCompletionStatus(); } } // ('inout' | 'let' | 'var')? bool hasSpecifier = false; while (Tok.isAny(tok::kw_inout, tok::kw_let, tok::kw_var)) { if (!hasSpecifier) { if (Tok.is(tok::kw_let)) { diagnose(Tok, diag::parameter_let_as_attr) .fixItRemove(Tok.getLoc()); } else { // We handle the var error in sema for a better fixit and inout is // handled later in this function for better fixits. param.SpecifierKind = Tok.is(tok::kw_inout) ? ParsedParameter::InOut : ParsedParameter::Var; } param.LetVarInOutLoc = consumeToken(); hasSpecifier = true; } else { // Redundant specifiers are fairly common, recognize, reject, and recover // from this gracefully. diagnose(Tok, diag::parameter_inout_var_let_repeated) .fixItRemove(Tok.getLoc()); consumeToken(); } } if (startsParameterName(*this, isClosure)) { // identifier-or-none for the first name if (Tok.is(tok::kw__)) { param.FirstNameLoc = consumeToken(); } else { assert(Tok.canBeArgumentLabel() && "startsParameterName() lied"); param.FirstName = Context.getIdentifier(Tok.getText()); param.FirstNameLoc = consumeToken(); } // identifier-or-none? for the second name if (Tok.canBeArgumentLabel()) { if (!Tok.is(tok::kw__)) param.SecondName = Context.getIdentifier(Tok.getText()); param.SecondNameLoc = consumeToken(); } // Operators and closures cannot have API names. if ((paramContext == ParameterContextKind::Operator || paramContext == ParameterContextKind::Closure) && !param.FirstName.empty() && param.SecondNameLoc.isValid()) { diagnose(param.FirstNameLoc, diag::parameter_operator_keyword_argument, isClosure) .fixItRemoveChars(param.FirstNameLoc, param.SecondNameLoc); param.FirstName = param.SecondName; param.FirstNameLoc = param.SecondNameLoc; param.SecondName = Identifier(); param.SecondNameLoc = SourceLoc(); } // (':' type)? if (consumeIf(tok::colon)) { auto type = parseType(diag::expected_parameter_type); status |= type; param.Type = type.getPtrOrNull(); // If we didn't parse a type, then we already diagnosed that the type // was invalid. Remember that. if (type.isParseError() && !type.hasCodeCompletion()) param.isInvalid = true; } } else { // Otherwise, we have invalid code. Check to see if this looks like a // type. If so, diagnose it as a common error. bool isBareType = false; { BacktrackingScope backtrack(*this); isBareType = canParseType() && Tok.isAny(tok::comma, tok::r_paren, tok::equal); } if (isBareType) { // Otherwise, if this is a bare type, then the user forgot to name the // parameter, e.g. "func foo(Int) {}" SourceLoc typeStartLoc = Tok.getLoc(); auto type = parseType(diag::expected_parameter_type, false); status |= type; param.Type = type.getPtrOrNull(); // Unnamed parameters must be written as "_: Type". if (param.Type) { diagnose(typeStartLoc, diag::parameter_unnamed) .fixItInsert(typeStartLoc, "_: "); } } else { // Otherwise, we're not sure what is going on, but this doesn't smell // like a parameter. diagnose(Tok, diag::expected_parameter_name); param.isInvalid = true; param.FirstNameLoc = Tok.getLoc(); status.setIsParseError(); } } // '...'? if (Tok.isEllipsis()) param.EllipsisLoc = consumeToken(); // ('=' expr)? if (Tok.is(tok::equal)) { SourceLoc EqualLoc = Tok.getLoc(); status |= parseDefaultArgument(*this, defaultArgs, defaultArgIndex, param.DefaultArg, paramContext); if (param.EllipsisLoc.isValid() && param.DefaultArg) { // The range of the complete default argument. SourceRange defaultArgRange; if (auto init = param.DefaultArg) defaultArgRange = SourceRange(param.EllipsisLoc, init->getEndLoc()); diagnose(EqualLoc, diag::parameter_vararg_default) .highlight(param.EllipsisLoc) .fixItRemove(defaultArgRange); param.isInvalid = true; param.DefaultArg = nullptr; } } // If we haven't made progress, don't add the parameter. if (Tok.getLoc() == StartLoc) { // If we took a default argument index for this parameter, but didn't add // one, then give it back. if (defaultArgs) defaultArgs->NextIndex--; return status; } params.push_back(param); return status; }); }
ParserStatus Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc, SmallVectorImpl<GenericTypeParamDecl *> &GenericParams) { ParserStatus Result; SyntaxParsingContext GPSContext(SyntaxContext, SyntaxKind::GenericParameterList); bool HasNextParam; do { SyntaxParsingContext GParamContext(SyntaxContext, SyntaxKind::GenericParameter); // Note that we're parsing a declaration. StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(), StructureMarkerKind::Declaration); if (ParsingDecl.isFailed()) { return makeParserError(); } // Parse attributes. DeclAttributes attributes; if (Tok.hasComment()) attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange())); bool foundCCTokenInAttr; parseDeclAttributeList(attributes, foundCCTokenInAttr); // Parse the name of the parameter. Identifier Name; SourceLoc NameLoc; if (parseIdentifier(Name, NameLoc, diag::expected_generics_parameter_name)) { Result.setIsParseError(); break; } // Parse the ':' followed by a type. SmallVector<TypeLoc, 1> Inherited; if (Tok.is(tok::colon)) { (void)consumeToken(); ParserResult<TypeRepr> Ty; if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol, tok::kw_Any)) { Ty = parseType(); } else if (Tok.is(tok::kw_class)) { diagnose(Tok, diag::unexpected_class_constraint); diagnose(Tok, diag::suggest_anyobject) .fixItReplace(Tok.getLoc(), "AnyObject"); consumeToken(); Result.setIsParseError(); } else { diagnose(Tok, diag::expected_generics_type_restriction, Name); Result.setIsParseError(); } if (Ty.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (Ty.isNonNull()) Inherited.push_back(Ty.get()); } // We always create generic type parameters with an invalid depth. // Semantic analysis fills in the depth when it processes the generic // parameter list. auto Param = new (Context) GenericTypeParamDecl(CurDeclContext, Name, NameLoc, GenericTypeParamDecl::InvalidDepth, GenericParams.size()); if (!Inherited.empty()) Param->setInherited(Context.AllocateCopy(Inherited)); GenericParams.push_back(Param); // Attach attributes. Param->getAttrs() = attributes; // Add this parameter to the scope. addToScope(Param); // Parse the comma, if the list continues. HasNextParam = consumeIf(tok::comma); } while (HasNextParam); return Result; }
/// parseGenericWhereClause - Parse a 'where' clause, which places additional /// constraints on generic parameters or types based on them. /// /// where-clause: /// 'where' requirement (',' requirement) * /// /// requirement: /// conformance-requirement /// same-type-requirement /// /// conformance-requirement: /// type-identifier ':' type-identifier /// type-identifier ':' type-composition /// /// same-type-requirement: /// type-identifier '==' type ParserStatus Parser::parseGenericWhereClause( SourceLoc &WhereLoc, SmallVectorImpl<RequirementRepr> &Requirements, bool &FirstTypeInComplete, bool AllowLayoutConstraints) { SyntaxParsingContext ClauseContext(SyntaxContext, SyntaxKind::GenericWhereClause); ParserStatus Status; // Parse the 'where'. WhereLoc = consumeToken(tok::kw_where); FirstTypeInComplete = false; SyntaxParsingContext ReqListContext(SyntaxContext, SyntaxKind::GenericRequirementList); bool HasNextReq; do { SyntaxParsingContext ReqContext(SyntaxContext, SyntaxContextKind::Syntax); // Parse the leading type. It doesn't necessarily have to be just a type // identifier if we're dealing with a same-type constraint. ParserResult<TypeRepr> FirstType = parseType(); if (FirstType.hasCodeCompletion()) { Status.setHasCodeCompletion(); FirstTypeInComplete = true; } if (FirstType.isNull()) { Status.setIsParseError(); break; } if (Tok.is(tok::colon)) { // A conformance-requirement. SourceLoc ColonLoc = consumeToken(); ReqContext.setCreateSyntax(SyntaxKind::ConformanceRequirement); if (Tok.is(tok::identifier) && getLayoutConstraint(Context.getIdentifier(Tok.getText()), Context) ->isKnownLayout()) { // Parse a layout constraint. Identifier LayoutName; auto LayoutLoc = consumeIdentifier(&LayoutName); auto LayoutInfo = parseLayoutConstraint(LayoutName); if (!LayoutInfo->isKnownLayout()) { // There was a bug in the layout constraint. Status.setIsParseError(); } auto Layout = LayoutInfo; // Types in SIL mode may contain layout constraints. if (!AllowLayoutConstraints && !isInSILMode()) { diagnose(LayoutLoc, diag::layout_constraints_only_inside_specialize_attr); } else { // Add the layout requirement. Requirements.push_back(RequirementRepr::getLayoutConstraint( FirstType.get(), ColonLoc, LayoutConstraintLoc(Layout, LayoutLoc))); } } else { // Parse the protocol or composition. ParserResult<TypeRepr> Protocol = parseType(); if (Protocol.isNull()) { Status.setIsParseError(); if (Protocol.hasCodeCompletion()) Status.setHasCodeCompletion(); break; } // Add the requirement. Requirements.push_back(RequirementRepr::getTypeConstraint( FirstType.get(), ColonLoc, Protocol.get())); } } else if ((Tok.isAnyOperator() && Tok.getText() == "==") || Tok.is(tok::equal)) { ReqContext.setCreateSyntax(SyntaxKind::SameTypeRequirement); // A same-type-requirement if (Tok.is(tok::equal)) { diagnose(Tok, diag::requires_single_equal) .fixItReplace(SourceRange(Tok.getLoc()), "=="); } SourceLoc EqualLoc = consumeToken(); // Parse the second type. ParserResult<TypeRepr> SecondType = parseType(); if (SecondType.isNull()) { Status.setIsParseError(); if (SecondType.hasCodeCompletion()) Status.setHasCodeCompletion(); break; } // Add the requirement Requirements.push_back(RequirementRepr::getSameType(FirstType.get(), EqualLoc, SecondType.get())); } else { diagnose(Tok, diag::expected_requirement_delim); Status.setIsParseError(); break; } HasNextReq = consumeIf(tok::comma); // If there's a comma, keep parsing the list. } while (HasNextReq); if (Requirements.empty()) WhereLoc = SourceLoc(); return Status; }