/// 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); }
/// 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; }
/// 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; }