bool Parser::parseGenericArguments(SmallVectorImpl<TypeRepr*> &Args, SourceLoc &LAngleLoc, SourceLoc &RAngleLoc) { // Parse the opening '<'. assert(startsWithLess(Tok) && "Generic parameter list must start with '<'"); LAngleLoc = consumeStartingLess(); do { ParserResult<TypeRepr> Ty = parseType(diag::expected_type); if (Ty.isNull() || Ty.hasCodeCompletion()) { // Skip until we hit the '>'. RAngleLoc = skipUntilGreaterInTypeList(); return true; } Args.push_back(Ty.get()); // Parse the comma, if the list continues. } while (consumeIf(tok::comma)); if (!startsWithGreater(Tok)) { checkForInputIncomplete(); diagnose(Tok, diag::expected_rangle_generic_arg_list); diagnose(LAngleLoc, diag::opening_angle); // Skip until we hit the '>'. RAngleLoc = skipUntilGreaterInTypeList(); return true; } else { RAngleLoc = consumeStartingGreater(); } return false; }
/// Parse an optional type annotation on a pattern. /// /// pattern-type-annotation ::= (':' type)? /// ParserResult<Pattern> Parser:: parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result, bool isOptional) { // Parse an optional type annotation. if (!consumeIf(tok::colon)) return result; Pattern *P; if (result.isNull()) // Recover by creating AnyPattern. P = new (Context) AnyPattern(Tok.getLoc()); else P = result.get(); ParserResult<TypeRepr> Ty = parseType(); if (Ty.hasCodeCompletion()) return makeParserCodeCompletionResult<Pattern>(); TypeRepr *repr = Ty.getPtrOrNull(); if (!repr) repr = new (Context) ErrorTypeRepr(PreviousLoc); // In an if-let, the actual type of the expression is Optional of whatever // was written. if (isOptional) repr = new (Context) OptionalTypeRepr(repr, Tok.getLoc()); return makeParserResult(new (Context) TypedPattern(P, repr)); }
/// Parse a pattern with an optional type annotation. /// /// typed-pattern ::= pattern (':' type)? /// ParserResult<Pattern> Parser::parseTypedPattern() { auto result = parsePattern(); // Now parse an optional type annotation. if (Tok.is(tok::colon)) { SourceLoc colonLoc = consumeToken(tok::colon); if (result.isNull()) // Recover by creating AnyPattern. result = makeParserErrorResult(new (Context) AnyPattern(colonLoc)); ParserResult<TypeRepr> Ty = parseType(); if (Ty.hasCodeCompletion()) return makeParserCodeCompletionResult<Pattern>(); if (!Ty.isNull()) { // Attempt to diagnose initializer calls incorrectly written // as typed patterns, such as "var x: [Int]()". if (Tok.isFollowingLParen()) { BacktrackingScope backtrack(*this); // Create a local context if needed so we can parse trailing closures. LocalContext dummyContext; Optional<ContextChange> contextChange; if (!CurLocalContext) { contextChange.emplace(*this, CurDeclContext, &dummyContext); } SourceLoc lParenLoc, rParenLoc; SmallVector<Expr *, 2> args; SmallVector<Identifier, 2> argLabels; SmallVector<SourceLoc, 2> argLabelLocs; Expr *trailingClosure; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic=*/false, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, trailingClosure); if (status.isSuccess()) { backtrack.cancelBacktrack(); // Suggest replacing ':' with '=' diagnose(lParenLoc, diag::initializer_as_typed_pattern) .highlight({Ty.get()->getStartLoc(), rParenLoc}) .fixItReplace(colonLoc, " = "); result.setIsParseError(); } } } else { Ty = makeParserResult(new (Context) ErrorTypeRepr(PreviousLoc)); } result = makeParserResult(result, new (Context) TypedPattern(result.get(), Ty.get())); } return result; }
bool SceneGraph::add(const char *filename, bool bInitialize, void (*callbackFn)(int nLine, void *info), void *callbackFnInfo) { int fileFormat = GetFileFormat(filename); Parser *parser = NULL; ParserResult *presult = getParserResult(); presult->init(); presult->setParserResult(false); switch (fileFormat) { case FILE_FORMAT_VRML: parser = new VRML97Parser(); break; #ifdef CX3D_SUPPORT_X3D case FILE_FORMAT_XML: parser = new X3DParser(); break; #endif #ifdef CX3D_SUPPORT_OBJ case FILE_FORMAT_OBJ: parser = new OBJParser(); break; #endif } if (parser == NULL) return false; SetParserResultObject(presult); bool parserRet = parser->load(filename, callbackFn, callbackFnInfo); presult->setParserResult(parserRet); if (parserRet == false) { delete parser; return false; } moveParserNodes(parser); moveParserRoutes(parser); delete parser; if (bInitialize) initialize(); setBackgroundNode(findBackgroundNode(), true); setFogNode(findFogNode(), true); setNavigationInfoNode(findNavigationInfoNode(), true); setViewpointNode(findViewpointNode(), true); return true; }
/// 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))); }
SAWYER_EXPORT std::string PodFormatter::toNroff(const ParserResult &parsed) { // Generate POD documentation into a temporary file TempFile tmpfile(tempFileName(".pod")); parsed.emit(tmpfile.stream, sharedFromThis()); tmpfile.stream.close(); std::string cmd = "pod2man" " --center='" + escapeSingleQuoted(chapterName_) + "'" " --date='" + escapeSingleQuoted(dateString_) + "'" " --name='" + escapeSingleQuoted(pageName_) + "'" " --release='" + escapeSingleQuoted(versionString_) + "'" " --section='" + escapeSingleQuoted(chapterNumber_) + "'" " " + tmpfile.name; FILE *f = popen(cmd.c_str(), "r"); if (!f) { #include <Sawyer/WarningsOff.h> // suppress strerror unsafe warning from Microsoft C++ throw std::runtime_error(std::string("cannot run command: ") + strerror(errno) + "\ncommand: " + cmd); #include <Sawyer/WarningsRestore.h> } std::string result; while (1) { std::string line = readOneLine(f); if (line.empty()) break; result += line; } if (-1 == Sawyer::pclose(f)) throw std::runtime_error("command failed: " + cmd); return result; }
ParserResult<TypeRepr> Parser::parseTypeCollection() { // Parse the leading '['. assert(Tok.is(tok::l_square)); Parser::StructureMarkerRAII parsingCollection(*this, Tok); SourceLoc lsquareLoc = consumeToken(); // Parse the element type. ParserResult<TypeRepr> firstTy = parseType(diag::expected_element_type); // If there is a ':', this is a dictionary type. SourceLoc colonLoc; ParserResult<TypeRepr> secondTy; if (Tok.is(tok::colon)) { colonLoc = consumeToken(); // Parse the second type. secondTy = parseType(diag::expected_dictionary_value_type); } // Parse the closing ']'. SourceLoc rsquareLoc; parseMatchingToken(tok::r_square, rsquareLoc, colonLoc.isValid() ? diag::expected_rbracket_dictionary_type : diag::expected_rbracket_array_type, lsquareLoc); if (firstTy.hasCodeCompletion() || secondTy.hasCodeCompletion()) return makeParserCodeCompletionStatus(); // If we couldn't parse anything for one of the types, propagate the error. if (firstTy.isNull() || (colonLoc.isValid() && secondTy.isNull())) return makeParserError(); // Form the dictionary type. SourceRange brackets(lsquareLoc, rsquareLoc); if (colonLoc.isValid()) return makeParserResult(ParserStatus(firstTy) | ParserStatus(secondTy), new (Context) DictionaryTypeRepr(firstTy.get(), secondTy.get(), colonLoc, brackets)); // Form the array type. return makeParserResult(firstTy, new (Context) ArrayTypeRepr(firstTy.get(), brackets)); }
/// Parse a pattern with an optional type annotation. /// /// typed-pattern ::= pattern (':' type)? /// ParserResult<Pattern> Parser::parseTypedPattern() { auto result = parsePattern(); // Now parse an optional type annotation. if (consumeIf(tok::colon)) { if (result.isNull()) // Recover by creating AnyPattern. result = makeParserErrorResult(new (Context) AnyPattern(PreviousLoc)); ParserResult<TypeRepr> Ty = parseType(); if (Ty.hasCodeCompletion()) return makeParserCodeCompletionResult<Pattern>(); if (Ty.isNull()) Ty = makeParserResult(new (Context) ErrorTypeRepr(PreviousLoc)); result = makeParserResult(result, new (Context) TypedPattern(result.get(), Ty.get())); } return result; }
/// Parse an element of a tuple pattern. /// /// pattern-tuple-element: /// (identifier ':')? pattern std::pair<ParserStatus, Optional<TuplePatternElt>> Parser::parsePatternTupleElement() { // If this element has a label, parse it. Identifier Label; SourceLoc LabelLoc; // If the tuple element has a label, parse it. if (Tok.is(tok::identifier) && peekToken().is(tok::colon)) { LabelLoc = consumeIdentifier(&Label); consumeToken(tok::colon); } // Parse the pattern. ParserResult<Pattern> pattern = parsePattern(); if (pattern.hasCodeCompletion()) return std::make_pair(makeParserCodeCompletionStatus(), None); if (pattern.isNull()) return std::make_pair(makeParserError(), None); auto Elt = TuplePatternElt(Label, LabelLoc, pattern.get()); return std::make_pair(makeParserSuccess(), Elt); }
ParserResult<Pattern> Parser::parseMatchingPatternAsLetOrVar(bool isLet, SourceLoc varLoc, bool isExprBasic) { // 'var' and 'let' patterns shouldn't nest. if (InVarOrLetPattern == IVOLP_InLet || InVarOrLetPattern == IVOLP_InVar) diagnose(varLoc, diag::var_pattern_in_var, unsigned(isLet)); // 'let' isn't valid inside an implicitly immutable context, but var is. if (isLet && InVarOrLetPattern == IVOLP_ImplicitlyImmutable) diagnose(varLoc, diag::let_pattern_in_immutable_context); // In our recursive parse, remember that we're in a var/let pattern. llvm::SaveAndRestore<decltype(InVarOrLetPattern)> T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar); ParserResult<Pattern> subPattern = parseMatchingPattern(isExprBasic); if (subPattern.isNull()) return nullptr; auto *varP = new (Context) VarPattern(varLoc, isLet, subPattern.get()); return makeParserResult(varP); }
/// matching-pattern ::= 'is' type /// matching-pattern ::= matching-pattern-var /// matching-pattern ::= expr /// ParserResult<Pattern> Parser::parseMatchingPattern(bool isExprBasic) { // TODO: Since we expect a pattern in this position, we should optimistically // parse pattern nodes for productions shared by pattern and expression // grammar. For short-term ease of initial implementation, we always go // through the expr parser for ambiguous productions. // Parse productions that can only be patterns. if (Tok.isAny(tok::kw_var, tok::kw_let)) { assert(Tok.isAny(tok::kw_let, tok::kw_var) && "expects var or let"); bool isLet = Tok.is(tok::kw_let); SourceLoc varLoc = consumeToken(); return parseMatchingPatternAsLetOrVar(isLet, varLoc, isExprBasic); } // matching-pattern ::= 'is' type if (Tok.is(tok::kw_is)) { SourceLoc isLoc = consumeToken(tok::kw_is); ParserResult<TypeRepr> castType = parseType(); if (castType.isNull() || castType.hasCodeCompletion()) return nullptr; return makeParserResult(new (Context) IsPattern(isLoc, castType.get(), nullptr)); } // matching-pattern ::= expr // Fall back to expression parsing for ambiguous forms. Name lookup will // disambiguate. ParserResult<Expr> subExpr = parseExprImpl(diag::expected_pattern, isExprBasic); if (subExpr.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (subExpr.isNull()) return nullptr; // The most common case here is to parse something that was a lexically // obvious pattern, which will come back wrapped in an immediate // UnresolvedPatternExpr. Transform this now to simplify later code. if (auto *UPE = dyn_cast<UnresolvedPatternExpr>(subExpr.get())) return makeParserResult(UPE->getSubPattern()); return makeParserResult(new (Context) ExprPattern(subExpr.get())); }
ParserResult Parser::parse(string input){ vector<string> output; ParserResult parserResult; try { string userInput = input; LOG(INFO) << "Parser-Initial input:" << userInput; if (userInput.empty()) { resetAll(); throw std::out_of_range(ERROR_EMPTY_INPUT); } userInput = removeExtraSpacePadding(userInput); setCommand(userInput); LOG(INFO) << "Parser-After setCommand:" << userInput; setDateAndTime(userInput); LOG(INFO) << "Parser-After setDateAndTime:" << userInput; setIndex(userInput); LOG(INFO) << "Parser-After setIndex:" << userInput; setDescription(userInput); LOG(INFO) << "Parser-After setDescription:" << userInput; } catch (const out_of_range& error) { LOG(INFO) << "Parser-Exception:" << error.what(); resetAll(); throw std::out_of_range(error.what()); } parserResult.setUserCommand(_userCommand); parserResult.setDescription(_description); parserResult.setIndex(_index); parserResult.setEntryType(_entryType); parserResult.setStartDate(_startYear, _startMonth, _startDay); parserResult.setStartTime(_startTime); parserResult.setEndDate(_endYear, _endMonth, _endDay); parserResult.setEndTime(_endTime); resetAll(); return parserResult; }
SAWYER_EXPORT void PodFormatter::emit(const ParserResult &parsed) { // Generate POD documentation into a temporary file. Since perldoc doesn't support the "name" property, but rather // uses the file name, we create a temporary directory and place a POD file inside with the name we want. TempDir tmpdir(tempFileName()); std::string fileName = tmpdir.name + pageName_ + ".pod"; { std::ofstream stream(fileName.c_str()); parsed.emit(stream, sharedFromThis()); } std::string cmd = "perldoc " " -o man" " -w 'center:" + escapeSingleQuoted(chapterName_) + "'" " -w 'date:" + escapeSingleQuoted(dateString_) + "'" // " -w 'name:" + escapeSingleQuoted(pageName_) + "'" " -w 'release:" + escapeSingleQuoted(versionString_) + "'" " -w 'section:" + escapeSingleQuoted(chapterNumber_) + "'" " " + fileName; system(cmd.c_str()); }
/// parseTypeTupleBody /// type-tuple: /// '(' type-tuple-body? ')' /// type-tuple-body: /// type-tuple-element (',' type-tuple-element)* '...'? /// type-tuple-element: /// identifier ':' type /// type ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() { Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok); SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren); SourceLoc EllipsisLoc; unsigned EllipsisIdx; SmallVector<TypeRepr *, 8> ElementsR; // We keep track of the labels separately, and apply them at the end. SmallVector<std::tuple<Identifier, SourceLoc, Identifier, SourceLoc>, 4> Labels; ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/false, diag::expected_rparen_tuple_type_list, [&] () -> ParserStatus { // If this is a deprecated use of the inout marker in an argument list, // consume the inout. SourceLoc InOutLoc; bool hasAnyInOut = false; bool hasValidInOut = false; if (consumeIf(tok::kw_inout, InOutLoc)) { hasAnyInOut = true; hasValidInOut = false; } // If the tuple element starts with a potential argument label followed by a // ':' or another potential argument label, then the identifier is an // element tag, and it is followed by a type annotation. if (Tok.canBeArgumentLabel() && (peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) { // Consume the name Identifier name; if (!Tok.is(tok::kw__)) name = Context.getIdentifier(Tok.getText()); SourceLoc nameLoc = consumeToken(); // If there is a second name, consume it as well. Identifier secondName; SourceLoc secondNameLoc; if (Tok.canBeArgumentLabel()) { if (!Tok.is(tok::kw__)) secondName = Context.getIdentifier(Tok.getText()); secondNameLoc = consumeToken(); } // Consume the ':'. if (!consumeIf(tok::colon)) diagnose(Tok, diag::expected_parameter_colon); SourceLoc postColonLoc = Tok.getLoc(); // Consume 'inout' if present. if (!hasAnyInOut && consumeIf(tok::kw_inout, InOutLoc)) { hasValidInOut = true; } SourceLoc extraneousInOutLoc; while (consumeIf(tok::kw_inout, extraneousInOutLoc)) { diagnose(Tok, diag::parameter_inout_var_let_repeated) .fixItRemove(extraneousInOutLoc); } // Parse the type annotation. ParserResult<TypeRepr> type = parseType(diag::expected_type); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); if (!hasValidInOut && hasAnyInOut) { diagnose(Tok.getLoc(), diag::inout_as_attr_disallowed) .fixItRemove(InOutLoc) .fixItInsert(postColonLoc, "inout "); } // If an 'inout' marker was specified, build the type. Note that we bury // the inout locator within the named locator. This is weird but required // by sema apparently. if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); // Record the label. We will look at these at the end. if (Labels.empty()) { Labels.assign(ElementsR.size(), std::make_tuple(Identifier(), SourceLoc(), Identifier(), SourceLoc())); } Labels.push_back(std::make_tuple(name, nameLoc, secondName, secondNameLoc)); ElementsR.push_back(type.get()); } else { // Otherwise, this has to be a type. ParserResult<TypeRepr> type = parseType(); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); if (!Labels.empty()) { Labels.push_back(std::make_tuple(Identifier(), SourceLoc(), Identifier(), SourceLoc())); } ElementsR.push_back(type.get()); } // Parse '= expr' here so we can complain about it directly, rather // than dying when we see it. if (Tok.is(tok::equal)) { SourceLoc equalLoc = consumeToken(tok::equal); auto init = parseExpr(diag::expected_init_value); auto inFlight = diagnose(equalLoc, diag::tuple_type_init); if (init.isNonNull()) inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc())); } if (Tok.isEllipsis()) { if (EllipsisLoc.isValid()) { diagnose(Tok, diag::multiple_ellipsis_in_tuple) .highlight(EllipsisLoc) .fixItRemove(Tok.getLoc()); (void)consumeToken(); } else { EllipsisLoc = consumeToken(); EllipsisIdx = ElementsR.size() - 1; } } return makeParserSuccess(); }); if (EllipsisLoc.isValid() && ElementsR.empty()) { EllipsisLoc = SourceLoc(); } if (EllipsisLoc.isInvalid()) EllipsisIdx = ElementsR.size(); // If there were any labels, figure out which labels should go into the type // representation. if (!Labels.empty()) { assert(Labels.size() == ElementsR.size()); bool isFunctionType = Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows); for (unsigned i : indices(ElementsR)) { auto ¤tLabel = Labels[i]; Identifier firstName = std::get<0>(currentLabel); SourceLoc firstNameLoc = std::get<1>(currentLabel); Identifier secondName = std::get<2>(currentLabel); SourceLoc secondNameLoc = std::get<3>(currentLabel); // True tuples have labels. if (!isFunctionType) { // If there were two names, complain. if (firstNameLoc.isValid() && secondNameLoc.isValid()) { auto diag = diagnose(firstNameLoc, diag::tuple_type_multiple_labels); if (firstName.empty()) { diag.fixItRemoveChars(firstNameLoc, ElementsR[i]->getStartLoc()); } else { diag.fixItRemove( SourceRange(Lexer::getLocForEndOfToken(SourceMgr,firstNameLoc), secondNameLoc)); } } // Form the named type representation. ElementsR[i] = new (Context) NamedTypeRepr(firstName, ElementsR[i], firstNameLoc); continue; } // If there was a first name, complain; arguments in function types are // always unlabeled. if (firstNameLoc.isValid() && !firstName.empty()) { auto diag = diagnose(firstNameLoc, diag::function_type_argument_label, firstName); if (secondNameLoc.isInvalid()) diag.fixItInsert(firstNameLoc, "_ "); else if (secondName.empty()) diag.fixItRemoveChars(firstNameLoc, ElementsR[i]->getStartLoc()); else diag.fixItReplace(SourceRange(firstNameLoc), "_"); } if (firstNameLoc.isValid() || secondNameLoc.isValid()) { // Form the named parameter type representation. ElementsR[i] = new (Context) NamedTypeRepr(secondName, ElementsR[i], secondNameLoc, firstNameLoc); } } } return makeParserResult(Status, TupleTypeRepr::create(Context, ElementsR, SourceRange(LPLoc, RPLoc), EllipsisLoc, EllipsisIdx)); }
/// parseTypeSimple /// type-simple: /// type-identifier /// type-tuple /// type-composition /// 'Any' /// type-simple '.Type' /// type-simple '.Protocol' /// type-simple '?' /// type-simple '!' /// type-collection ParserResult<TypeRepr> Parser::parseTypeSimple(Diag<> MessageID, bool HandleCodeCompletion) { ParserResult<TypeRepr> ty; // If this is an "inout" marker for an identifier type, consume the inout. SourceLoc InOutLoc; consumeIf(tok::kw_inout, InOutLoc); switch (Tok.getKind()) { case tok::kw_Self: ty = parseTypeIdentifier(); break; case tok::identifier: case tok::kw_protocol: case tok::kw_Any: ty = parseTypeIdentifierOrTypeComposition(); break; case tok::l_paren: ty = parseTypeTupleBody(); break; case tok::code_complete: if (!HandleCodeCompletion) break; if (CodeCompletion) CodeCompletion->completeTypeSimpleBeginning(); // Eat the code completion token because we handled it. consumeToken(tok::code_complete); return makeParserCodeCompletionResult<TypeRepr>(); case tok::kw_super: case tok::kw_self: // These keywords don't start a decl or a statement, and thus should be // safe to skip over. diagnose(Tok, MessageID); ty = makeParserErrorResult(new (Context) ErrorTypeRepr(Tok.getLoc())); consumeToken(); // FIXME: we could try to continue to parse. return ty; case tok::l_square: ty = parseTypeCollection(); break; default: checkForInputIncomplete(); diagnose(Tok, MessageID); return nullptr; } // '.Type', '.Protocol', '?', and '!' still leave us with type-simple. while (ty.isNonNull()) { if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) { if (peekToken().isContextualKeyword("Type")) { consumeToken(); SourceLoc metatypeLoc = consumeToken(tok::identifier); ty = makeParserResult(ty, new (Context) MetatypeTypeRepr(ty.get(), metatypeLoc)); continue; } if (peekToken().isContextualKeyword("Protocol")) { consumeToken(); SourceLoc protocolLoc = consumeToken(tok::identifier); ty = makeParserResult(ty, new (Context) ProtocolTypeRepr(ty.get(), protocolLoc)); continue; } } if (!Tok.isAtStartOfLine()) { if (isOptionalToken(Tok)) { ty = parseTypeOptional(ty.get()); continue; } if (isImplicitlyUnwrappedOptionalToken(Tok)) { ty = parseTypeImplicitlyUnwrappedOptional(ty.get()); continue; } } break; } // If we parsed an inout modifier, prepend it. if (InOutLoc.isValid()) ty = makeParserResult(new (Context) InOutTypeRepr(ty.get(), InOutLoc)); return ty; }
/// 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})); }
/// parseTypeTupleBody /// type-tuple: /// '(' type-tuple-body? ')' /// type-tuple-body: /// type-tuple-element (',' type-tuple-element)* '...'? /// type-tuple-element: /// identifier ':' type /// type ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() { Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok); SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren); SourceLoc EllipsisLoc; unsigned EllipsisIdx; SmallVector<TypeRepr *, 8> ElementsR; ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/false, diag::expected_rparen_tuple_type_list, [&] () -> ParserStatus { // If this is an inout marker in an argument list, consume the inout. SourceLoc InOutLoc; consumeIf(tok::kw_inout, InOutLoc); // If the tuple element starts with "ident :", then // the identifier is an element tag, and it is followed by a type // annotation. if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) { // Consume the name Identifier name; if (!Tok.is(tok::kw__)) name = Context.getIdentifier(Tok.getText()); SourceLoc nameLoc = consumeToken(); // Consume the ':'. consumeToken(tok::colon); // Parse the type annotation. ParserResult<TypeRepr> type = parseType(diag::expected_type); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); // If an 'inout' marker was specified, build the type. Note that we bury // the inout locator within the named locator. This is weird but required // by sema apparently. if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); ElementsR.push_back( new (Context) NamedTypeRepr(name, type.get(), nameLoc)); } else { // Otherwise, this has to be a type. ParserResult<TypeRepr> type = parseType(); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); ElementsR.push_back(type.get()); } // Parse '= expr' here so we can complain about it directly, rather // than dying when we see it. if (Tok.is(tok::equal)) { SourceLoc equalLoc = consumeToken(tok::equal); auto init = parseExpr(diag::expected_init_value); auto inFlight = diagnose(equalLoc, diag::tuple_type_init); if (init.isNonNull()) inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc())); } if (Tok.isEllipsis()) { if (EllipsisLoc.isValid()) { diagnose(Tok, diag::multiple_ellipsis_in_tuple) .highlight(EllipsisLoc) .fixItRemove(Tok.getLoc()); (void)consumeToken(); } else { EllipsisLoc = consumeToken(); EllipsisIdx = ElementsR.size() - 1; } } return makeParserSuccess(); }); if (EllipsisLoc.isValid() && ElementsR.empty()) { EllipsisLoc = SourceLoc(); } if (EllipsisLoc.isInvalid()) EllipsisIdx = ElementsR.size(); return makeParserResult(Status, TupleTypeRepr::create(Context, ElementsR, SourceRange(LPLoc, RPLoc), EllipsisLoc, EllipsisIdx)); }
ParserResult<TypeRepr> Parser::parseTypeIdentifierWithRecovery( Diag<> MessageID, Diag<TypeLoc> NonIdentifierTypeMessageID) { ParserResult<TypeRepr> Ty = parseType(MessageID); if (!Ty.isParseError() && !isa<IdentTypeRepr>(Ty.get()) && !isa<ErrorTypeRepr>(Ty.get())) { diagnose(Ty.get()->getStartLoc(), NonIdentifierTypeMessageID, Ty.get()) .highlight(Ty.get()->getSourceRange()); Ty.setIsParseError(); Ty = makeParserResult( Ty, new (Context) ErrorTypeRepr(Ty.get()->getSourceRange())); } assert(Ty.isNull() || isa<IdentTypeRepr>(Ty.get()) || isa<ErrorTypeRepr>(Ty.get())); return Ty; }
/// parseType /// type: /// attribute-list type-function /// attribute-list type-array /// /// type-function: /// type-simple '->' type /// ParserResult<TypeRepr> Parser::parseType(Diag<> MessageID, bool HandleCodeCompletion) { // Parse attributes. TypeAttributes attrs; parseTypeAttributeList(attrs); // Parse Generic Parameters. Generic Parameters are visible in the function // body. GenericParamList *generics = nullptr; if (isInSILMode()) { generics = maybeParseGenericParams().getPtrOrNull(); } ParserResult<TypeRepr> ty = parseTypeSimple(MessageID, HandleCodeCompletion); if (ty.hasCodeCompletion()) return makeParserCodeCompletionResult<TypeRepr>(); if (ty.isNull()) return nullptr; // Parse a throws specifier. 'throw' is probably a typo for 'throws', // but in local contexts we could just be at the end of a statement, // so we need to check for the arrow. ParserPosition beforeThrowsPos; SourceLoc throwsLoc; bool rethrows = false; if (Tok.isAny(tok::kw_throws, tok::kw_rethrows) || (Tok.is(tok::kw_throw) && peekToken().is(tok::arrow))) { if (Tok.is(tok::kw_throw)) { diagnose(Tok.getLoc(), diag::throw_in_function_type) .fixItReplace(Tok.getLoc(), "throws"); } beforeThrowsPos = getParserPosition(); rethrows = Tok.is(tok::kw_rethrows); throwsLoc = consumeToken(); } // Handle type-function if we have an arrow. SourceLoc arrowLoc; if (consumeIf(tok::arrow, arrowLoc)) { ParserResult<TypeRepr> SecondHalf = parseType(diag::expected_type_function_result); if (SecondHalf.hasCodeCompletion()) return makeParserCodeCompletionResult<TypeRepr>(); if (SecondHalf.isNull()) return nullptr; if (rethrows) { // 'rethrows' is only allowed on function declarations for now. diagnose(throwsLoc, diag::rethrowing_function_type); } auto fnTy = new (Context) FunctionTypeRepr(generics, ty.get(), throwsLoc, arrowLoc, SecondHalf.get()); return makeParserResult(applyAttributeToType(fnTy, attrs)); } else if (throwsLoc.isValid()) { // Don't consume 'throws', so we can emit a more useful diagnostic when // parsing a function decl. restoreParserPosition(beforeThrowsPos); return ty; } // Only function types may be generic. if (generics) { auto brackets = generics->getSourceRange(); diagnose(brackets.Start, diag::generic_non_function); } // Parse legacy array types for migration. while (ty.isNonNull() && !Tok.isAtStartOfLine()) { if (Tok.is(tok::l_square)) { ty = parseTypeArray(ty.get()); } else { break; } } if (ty.isNonNull() && !ty.hasCodeCompletion()) { ty = makeParserResult(applyAttributeToType(ty.get(), attrs)); } return ty; }
/// 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::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) { 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; }
/// Parse a pattern. /// pattern ::= identifier /// pattern ::= '_' /// pattern ::= pattern-tuple /// pattern ::= 'var' pattern /// pattern ::= 'let' pattern /// ParserResult<Pattern> Parser::parsePattern() { switch (Tok.getKind()) { case tok::l_paren: return parsePatternTuple(); case tok::kw__: return makeParserResult(new (Context) AnyPattern(consumeToken(tok::kw__))); case tok::identifier: { Identifier name; SourceLoc loc = consumeIdentifier(&name); bool isLet = InVarOrLetPattern != IVOLP_InVar; return makeParserResult(createBindingFromPattern(loc, name, isLet)); } case tok::code_complete: if (!CurDeclContext->isNominalTypeOrNominalTypeExtensionContext()) { // This cannot be an overridden property, so just eat the token. We cannot // code complete anything here -- we expect an identifier. consumeToken(tok::code_complete); } return nullptr; case tok::kw_var: case tok::kw_let: { bool isLet = Tok.is(tok::kw_let); SourceLoc varLoc = consumeToken(); // 'var' and 'let' patterns shouldn't nest. if (InVarOrLetPattern == IVOLP_InLet || InVarOrLetPattern == IVOLP_InVar) diagnose(varLoc, diag::var_pattern_in_var, unsigned(isLet)); // 'let' isn't valid inside an implicitly immutable context, but var is. if (isLet && InVarOrLetPattern == IVOLP_ImplicitlyImmutable) diagnose(varLoc, diag::let_pattern_in_immutable_context); // In our recursive parse, remember that we're in a var/let pattern. llvm::SaveAndRestore<decltype(InVarOrLetPattern)> T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar); ParserResult<Pattern> subPattern = parsePattern(); if (subPattern.hasCodeCompletion()) return makeParserCodeCompletionResult<Pattern>(); if (subPattern.isNull()) return nullptr; return makeParserResult(new (Context) VarPattern(varLoc, isLet, subPattern.get())); } default: if (Tok.isKeyword() && (peekToken().is(tok::colon) || peekToken().is(tok::equal))) { diagnose(Tok, diag::expected_pattern_is_keyword, Tok.getText()); SourceLoc Loc = Tok.getLoc(); consumeToken(); return makeParserErrorResult(new (Context) AnyPattern(Loc)); } diagnose(Tok, diag::expected_pattern); return nullptr; } }
GenericParamList *Parser::parseGenericParameters(SourceLoc LAngleLoc) { // Parse the generic parameter list. SmallVector<GenericTypeParamDecl *, 4> GenericParams; bool Invalid = false; do { // Parse the name of the parameter. Identifier Name; SourceLoc NameLoc; if (parseIdentifier(Name, NameLoc, diag::expected_generics_parameter_name)) { Invalid = true; break; } // Parse the ':' followed by a type. SmallVector<TypeLoc, 1> Inherited; if (Tok.is(tok::colon)) { (void)consumeToken(); ParserResult<TypeRepr> Ty; if (Tok.getKind() == tok::identifier) { Ty = parseTypeIdentifier(); } else if (Tok.getKind() == tok::kw_protocol) { Ty = parseTypeComposition(); } else { diagnose(Tok, diag::expected_generics_type_restriction, Name); Invalid = true; } // FIXME: code completion not handled. if (Ty.isNonNull()) Inherited.push_back(Ty.get()); } // We always create generic type parameters with a depth of zero. // Semantic analysis fills in the depth when it processes the generic // parameter list. auto Param = new (Context) GenericTypeParamDecl(CurDeclContext, Name, NameLoc, /*Depth=*/0, GenericParams.size()); if (!Inherited.empty()) Param->setInherited(Context.AllocateCopy(Inherited)); GenericParams.push_back(Param); // Add this parameter to the scope. addToScope(Param); // Parse the comma, if the list continues. } while (consumeIf(tok::comma)); // Parse the optional where-clause. SourceLoc WhereLoc; SmallVector<RequirementRepr, 4> Requirements; if (Tok.is(tok::kw_where) && parseGenericWhereClause(WhereLoc, Requirements)) { Invalid = true; } // Parse the closing '>'. SourceLoc RAngleLoc; if (!startsWithGreater(Tok)) { if (!Invalid) { diagnose(Tok, diag::expected_rangle_generics_param); diagnose(LAngleLoc, diag::opening_angle); Invalid = true; } // Skip until we hit the '>'. skipUntilGreaterInTypeList(); if (startsWithGreater(Tok)) RAngleLoc = consumeStartingGreater(); else RAngleLoc = Tok.getLoc(); } else { RAngleLoc = consumeStartingGreater(); } if (GenericParams.empty()) return nullptr; return GenericParamList::create(Context, LAngleLoc, GenericParams, WhereLoc, Requirements, RAngleLoc); }
int main(int argc, char *argv[]) { // Command-line parsing will "push" parsed values into members of this struct when ParserResult::apply is called. // Alternatively, we could have used a "pull" paradigm in which we query the ParserResult to obtain the values. In // fact, nothing prevents us from doing both, except if we use a pull paradigm we would probably want to set some // of the Switch::key properties so related switches (like -E, -F, -G, -P) all store their result in the same place // within the ParserResult. Options opt; // We split the switches into individual groups only to make the documentation better. The name supplied to the // switch group constructor is the subsection under which the documentation for those switches appears. SwitchGroup generic("Generic Program Information"); generic.switchOrder(INSERTION_ORDER); generic.doc("This paragraph is attached to a switch group and appears before the switches that this " "group contains. It may be as many paragraphs as you like and may contain markup like for the " "individual switches.\n\n" "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum " "mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. " "Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta " "lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum " "dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a " "semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur " "dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna " "ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. " "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum " "accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est."); generic.insert(Switch("help") .action(showHelpAndExit(0)) .doc("Print a usage message briefly summarizing these command-line options and the bug-reporting " "address, then exit.")); generic.insert(Switch("version", 'V') .action(showVersion(VERSION_STRING)) .doc("Print the version number of @prop{programName} to the standard output stream. This version " "number should be included in all bug reports (see below).")); generic.insert(Switch("log") .action(configureDiagnostics("log", Sawyer::Message::mfacilities)) .argument("config") .whichValue(SAVE_ALL) .doc("Controls Sawyer diagnostics. See Sawyer::Message::Facilities::control for details.")); SwitchGroup matcher("Matcher Selection"); matcher.switchOrder(INSERTION_ORDER); matcher.insert(Switch("extended-regexp", 'E') .intrinsicValue(MATCHER_EXTENDED, opt.matcher) .doc("Interpret @v{pattern} as an extended regular expression (ERE, see below). (@s{E} is specified " "by POSIX.)")); matcher.insert(Switch("fixed-strings", 'F') .intrinsicValue(MATCHER_STRINGS, opt.matcher) .doc("Interpret @v{pattern} as a list of fixed strings, separated by newlines, any of which is to " "be matched. (@s{F} is specified by POSIX.)")); matcher.insert(Switch("basic-regexp", 'G') .intrinsicValue(MATCHER_BASIC, opt.matcher) .doc("Interpret @v{pattern} as a basic regular expression (BRE, see below). This is the default.")); matcher.insert(Switch("perl-regexp", 'P') .intrinsicValue(MATCHER_PERL, opt.matcher) .doc("Interpret @v{pattern} as a Perl regular expression. This is highly experimental and " "@prop{programName} @s{P} may warn of unimplemented features.")); SwitchGroup control("Matching Control"); control.switchOrder(INSERTION_ORDER); control.insert(Switch("regexp", 'e') .argument("pattern", anyParser(opt.pattern)) .doc("Use @v{pattern} as the pattern. This can be used to specify multiple search patterns, or to " "protect a pattern beginning with a hyphen (\"-\"). (@s{e} is specified by POSIX.)")); control.insert(Switch("file", 'f') .argument("file", anyParser(opt.patternFile)) .doc("Obtain patterns from @v{file}, one per line. The empty file contains zero patterns, and " "therefore matches nothing. (@s{f} is specified by POSIX.)")); control.insert(Switch("ignore-case", 'i') .intrinsicValue(true, opt.ignoreCase) .doc("Ignore case distinctions in both the @v{pattern} and the input files. (@s{i} is specified " "by POSIX.)")); control.insert(Switch("invert-match", 'v') .intrinsicValue(true, opt.invert) .doc("Invert the sense of matching, to select non-matching lines. (@s{v} is specified by POSIX.)")); control.insert(Switch("word-regexp", 'w') .intrinsicValue(true, opt.wordRegexp) .doc("Select only those lines containing matches that form whole words. The test is that the " "matching substring must either be at the beginning of the line, or preceded by a non-word " "constituent character. Similarly, it must be either at the end of the line or followed by " "a non-word constituent character. Word-constituent characters are letters, digits, and " "the underscore.")); control.insert(Switch("line-regexp", 'x') .intrinsicValue(true, opt.lineRegexp) .doc("Select only those matches that exactly match the whole line. (@s{x} is specified by POSIX.)")); control.insert(Switch("", 'y') .whichValue(SAVE_NONE) .doc("Obsolete synonym for @s{i}.")); SwitchGroup output("General Output Control"); output.name("output"); output.doc("In this example, we've given this group of switches a name, \"output\", to demonstrate how switch group " "name spaces work. The switches here can be invoked by the name they were given in the C++ source code, " "or they can include the switch group name, as in @s{count} or @s{output-count}."); output.switchOrder(INSERTION_ORDER); output.insert(Switch("count", 'c') .intrinsicValue(true, opt.count) .doc("Suppress normal output; instead print a count of matching lines for each input file. With " "the @s{v}, @s{invert-match} option (see below), count non-matching lines. (@s{c} is specified " "by POSIX.)")); output.insert(Switch("color") .longName("colour") .argument("when", enumParser(opt.colorWhen) ->with("never", COLOR_NEVER) ->with("always", COLOR_ALWAYS) ->with("auto", COLOR_AUTO), "auto") .doc("Surround the matched (non-empty) strings, matching lines, context lines, file names, line " "numbers, byte offsets, and separators (for fields and groups of context lines) with escape " "sequences to display them in color on the terminal. The colors are defined by the " "environment variable \"GREP_COLORS\". The deprecated environment variable \"GREP_COLOR\" " "is still supported, but its setting does not have priority. @v{when} is \"never\", \"always\", " "or \"auto\".")); output.insert(Switch("files-without-match", 'L') .intrinsicValue(true, opt.filesWithoutMatch) .doc("Suppress normal output; instead print the name of each input file from which no output " "would normally have been printed. The scanning will stop on the first match.")); output.insert(Switch("files-with-matches", 'l') .intrinsicValue(true, opt.filesWithMatches) .doc("Suppress normal output; instead print the name of each input file from which output would " "normally have been printed. The scanning will stop on the first match. (@s{l} is specified " "by POSIX.)")); output.insert(Switch("max-count", 'm') .argument("num", nonNegativeIntegerParser(opt.maxCount)) .doc("Stop reading a file after @v{num} matching lines. If the input is standard input from a " "regular file, and @v{num} matching lines are output, @prop{programName} ensures that the " "standard input is positioned to just after the last matching line before exiting, " "regardless of the presence of trailing context lines. This enables a calling process " "to resume a search. When @prop{programName} stops after @v{num} matching lines, it " "outputs any trailing context lines. When the @s{c} or @s{count} options is also used, " "@prop{programName} does not output a count greater than @v{num}. When the @s{v} or " "@s{invert-match} option is also used, @prop{programName} stops after outputting @v{num} " "non-matching lines.")); output.insert(Switch("only-matching", 'o') .intrinsicValue(true, opt.onlyMatching) .doc("Print only the matched (non-empty) parts of a matching line, with each such part on a " "separate output line.")); output.insert(Switch("quiet", 'q') .longName("silent") .intrinsicValue(true, opt.quiet) .doc("Quiet; do not write anything to standard output. Exit immediately with zero status " "if any match is found, even if an error was detected. Also see the @s{s} or " "@s{no-messages} option. (@s{q} is specified by POSIX.)")); output.insert(Switch("no-messages", 's') .intrinsicValue(true, opt.noMessages) .doc("Suppress error messages about nonexistent or unreadable files. Portability note: " "unlike GNU grep, 7th Edition Unix grep did not conform to POSIX, because it lacked " "@s{q} and its @s{s} option behaved like GNU grep's @s{q} option. USG-style grep " "also lacked @s{q} but its @s{s} option behaved like GNU grep. Portable shell " "scripts should avoid both @s{q} and @s{s} and should redirect standard and error " "output to /dev/null instead. (@s{s} is specified by POSIX.)")); SwitchGroup prefix("Output Line Prefix Control"); prefix.switchOrder(INSERTION_ORDER); prefix.insert(Switch("byte-offset", 'b') .intrinsicValue(true, opt.byteOffset) .doc("Print the 0-based byte offset within the input file before each line of output. " "If @s{o} (@s{only-matching}) is specified, print the offset of the matching part " "itself.")); prefix.insert(Switch("with-filename", 'H') .intrinsicValue(true, opt.withFileName) .doc("Print the file name for each match. This is the default when there is more than one " "file to search.")); prefix.insert(Switch("no-filename", 'h') .intrinsicValue(false, opt.withFileName) .doc("Suppress the prefixing of file names on output. This is the default when there is only " "one file (or only standard input) to search.")); prefix.insert(Switch("label") .argument("label", anyParser(opt.label)) .doc("Display input actually coming from standard input as input coming from file @v{label}. " "This is especially useful when implementing tools like zgep, e.g., \"gzip -cd foo.gz | " "grep --label=foo -H something\". See also the @s{H} option.")); prefix.insert(Switch("line-number", 'n') .intrinsicValue(true, opt.lineNumbers) .doc("Prefix each line of output with the 1-based line number within its input file. (@s{n} " "is specified by POSIX.)")); prefix.insert(Switch("initial-tab", 'T') .intrinsicValue(true, opt.initialTab) .doc("Make sure that the first character of actual line content lies on a tab stop, so that " "the alignment of tabs looks normal. This is useful with options that prefix their " "output to the actual content: @s{H}, @s{n}, and @s{b}. In order to improve the " "probability that lines from a single file will all start at the same column, this " "also causes the line number and byte offset (if present) to be printed in a minimum " "size field width.")); prefix.insert(Switch("unix-byte-offsets", 'u') .intrinsicValue(true, opt.unixByteOffsets) .doc("Report Unix-style byte offsets. This switch causes @prop{programName} to report byte " "offsets as if the file were a Unix-style text file, i.e., with CR characters stripped " "off. This will produce results identical to running @prop{programName} on a Unix " "machine. This option has no effect unless @s{b} option is also used; it has no " "effect on platforms other than MS-DOS and MS-Windows.")); prefix.insert(Switch("null", 'Z') .intrinsicValue(true, opt.nullAfterName) .doc("Output a zero byte (the ASCII NUL character) instead of the character that normally " "follows a file name. For example, \"grep -lZ\" outputs a zero byte after each file " "name instead of the usual newline. This option makes the output unambiguous, even in " "the presence of file name containing unusual characters like newlines. This option " "can be used with commands like \"find -print0\", \"perl -0\", \"sort -z\", and \"xargs " "-0\" to process arbitrary file names, even those that contain newline characters.")); SwitchGroup context("Context Line Control"); context.switchOrder(INSERTION_ORDER); context.insert(Switch("after-context", 'A') .argument("num", nonNegativeIntegerParser(opt.afterContext)) .doc("Print @v{num} lines of trailing context after matching lines. Places a line containing " "a group separator (\"--\") between contiguous groups of matches. With the @s{o} or " "@s{only-matching} option, this has no effect and a warning is given.")); context.insert(Switch("before-context", 'B') .argument("num", nonNegativeIntegerParser(opt.beforeContext)) .doc("Print @v{num} lines of leading context before matching lines. Places a line containing " "a group separator (\"--\") between contigous groups of matches. With the @s{o} or " "@s{only-matching} option, this has no effect and a warning is given.")); context.insert(Switch("context", 'C') // FIXME[Robb Matzke 2014-03-15]: allow "-NUM" .argument("num", nonNegativeIntegerParser(opt.contextLines)) .doc("Print @v{num} lines of output context. Places a line containing a group separator " "(\"--\") between contigous groups of matches. With the @s{o} or @s{only-matching} " "option, this has no effect and a warning is given.")); SwitchGroup selection("File and Directory Selection"); selection.switchOrder(INSERTION_ORDER); selection.insert(Switch("text", 'a') .intrinsicValue(BIN_TEXT, opt.binaryFile) .doc("Process a binary file as if it were text; this is the equivalent to the " "\"@s{binary-files}=text\" option.")); selection.insert(Switch("binary-files") .argument("type", enumParser(opt.binaryFile) ->with("binary", BIN_BINARY) ->with("without-match", BIN_SKIP) ->with("text", BIN_TEXT)) .doc("If the first few bytes of a file indicate that the file contains binary data, assume " "that the file is of type @v{type}. By default, @v{type} is \"binary\", and " "@prop{programName} normally outputs either a one-line message saying that a binary " "file matches, or no message if there is no match. If @v{type} is \"without-match" "@prop{programName} assumes that a binary file does not match; this is equivalent to " "the @s{I} option. If @v{type} is \"text\", @prop{programName} processes a binary " "file as if it were text; ths is equivalent to the @s{a} option. @b{Warning:} " "\"@prop{programName} @s{binary-files}=text\" might output binary garbage, which can " "have nasty side effects if the output is a terminal and if the terminal driver " "interprets some of it as commands.")); selection.insert(Switch("devices", 'D') .argument("action", enumParser(opt.deviceAction) ->with("read", ACTION_READ) ->with("skip", ACTION_SKIP)) .doc("If an input file is a device, FIFO, or socket, use @v{action} to process it. By " "default, @v{action} is \"read\", which means that devices are read just as if they " "were ordinary files. If @v{action} is \"skip\", devices are silently skipped.")); selection.insert(Switch("directories", 'd') .argument("action", enumParser(opt.directoryAction) ->with("read", ACTION_READ) ->with("skip", ACTION_SKIP) ->with("recurse", ACTION_RECURSE)) .doc("If an input file is a directory, use @v{action} to process it. By default, " "@v{action} is \"read\", which means that directories are read just as if they were " "ordinary files. If @v{action} is \"skip\", directories are silently skipped. If " "@v{action} is \"recurse\", @prop{programName} reads all files under each directory, " "recursively; this is equivalent to the @s{r} option.")); selection.insert(Switch("exclude") .argument("glob", anyParser(opt.excludeGlob)) .doc("Skip files whose base name matches @v{glob} (using wildcard matching). A file-name " "glob can use \"*\", \"?\", and \"[...]\" as wildcards, and \"\\\" to quote a wildcard " "or backslash character literally.")); selection.insert(Switch("exclude-from") .argument("file", anyParser(opt.excludeFromFile)) .doc("Skip files whose base name matches any of the file-name globs read from @v{file} " "(using wildcard matching as described under @s{exclude}).")); selection.insert(Switch("", 'I') .intrinsicValue(BIN_SKIP, opt.binaryFile) .doc("Process a binary file as if it did not contain matching data; this is equivalent " "to the \"@s{binary-files}=without-match\" option.")); selection.insert(Switch("include") .argument("glob", anyParser(opt.includeGlob)) .doc("Search only files whose base names match @v{glob} (using wildcard matching as " "described under @s{exclude}).")); selection.insert(Switch("recursive", 'R') .shortName('r') .intrinsicValue(ACTION_RECURSE, opt.directoryAction) .doc("Read all files under each directory, recursively; this is equivalent to the " "\"@s{d} recurse\" option.")); SwitchGroup misc("Other Options"); misc.switchOrder(INSERTION_ORDER); misc.insert(Switch("line-buffered") .intrinsicValue(true, opt.lineBuffered) .doc("Use line buffering on output. This can cause a performance penalty.")); misc.insert(Switch("mmap") .intrinsicValue(true, opt.useMmap) .doc("If possible, use the @man{mmap}{2} system call to read input, instead of the default " "@man{read}{2} system call. In some situations, @s{mmap} yields better performance. " "However, @s{mmap} can cause undefined behavior (including core dumps) if an input " "file shrinks while @prop{programName} is operating, or if an I/O error occurs.")); misc.insert(Switch("binary", 'U') .intrinsicValue(true, opt.openAsBinary) .doc("Treat the file(s) as binary. By default, under MS-DOS and MS-Windows, " "@prop{programName} guesses the file type by looking at the contents of the first 32kB " "read from the file. If @prop{programName} decides the file is a text file, it strips " "the CR characters from the original file contents (to make regular expressions with " "\"^\" and \"$\" work correctly). Specifying @s{U} overrules this guesswork, causing all " "files to be read and passed to the matching mechanism verbatim; if the file is a text file " "with CR/LF pairs at the end of each line, this will cause some regular expressions to " "fail. This option has no effect on platforms other than MS-DOS and MS-Windows.")); misc.insert(Switch("null-data", 'Z') .intrinsicValue(true, opt.nulTerminatedLines) .doc("Treat the input as a set of lines, each terminated by a zero byte (the ASCII NUL character) " "instead of a newline. Like the @s{Z} or @s{null} option, this option can be used with " "commands like \"sort -z\" to process arbitrary file names.")); // Build the parser Parser parser; parser .with(generic) .with(matcher) .with(control) .with(output) .with(prefix) .with(context) .with(selection) .with(misc); // Add some top-level documentation. parser .programName("demoGrep") // override the real command name .purpose("print lines matching a pattern") .version(VERSION_STRING) .doc("Synopsis", "@b{@prop{programName}} [@v{options}] @v{pattern} [@v{file}...]\n\n" "@b{@prop{programName}} [@v{options}] [@s{e} @v{pattern} | @s{f} @v{file}] [@v{file}...]") .doc("Description", "@prop{programName} searches the named input @v{file}s (or standard input if no files are named, or if " "a single hyphen-minus (\"-\") is given as the file name) for lines containing a match to the given " "@v{pattern}. By default, @prop{programName} prints the matching lines." "\n\n" "In addition, three variant programs egrep, fgrep, and rgrep are available. egrep is the same as " "\"@prop{programName} @s{E}\"; fgrep is the same as \"@prop{programName} @s{F}\"; rgrep is the same as " "\"@prop{programName} @s{r}\". Direct invocation as either \"egrep\" or \"fgrep\" is deprecated, but " "is provided to allow historical applications that rely on them to run unmodified.") .doc("Regular Expressions", "A regular expression is a pattern that describes a set of strings. Regular expressions are " "constructed analogously to arithmetic expressions, by using various operators to combine smaller " "expressions." "\n\n" "@prop{programName} understands three different versions of regular expression syntax: \"basic,\" " "\"extended\" and \"perl.\" In @prop{programName}, there is no difference in available functionality " "between basic and extended syntaxes. In other implementations, basic regular expressions are less " "powerful. The following description applies to extended regular expressions; differences for basic " "regular expressions are summarized afterwards. Perl regular expressions give additional functionality, " "and are documented in @man{pcresyntax}{3} and @man{pcrepattern}{3}, but may not be available on every " "system." "\n\n" "The fundamental building blocks are the regular expressions that match a single character. Most " "characters, including all letters and digits, are regular expressions that match themselves. Any " "meta-character with special meaning may be quoted by preceding it with a backslash." "\n\n" "The period \".\" matches any single character."); // Parse the command-line ParserResult cmdline = parser.parse(argc, argv); // Apply the parser results, causing values to be saved and actions to be executed. cmdline.apply(); }
static ParserStatus parseDefaultArgument(Parser &P, Parser::DefaultArgumentInfo *defaultArgs, unsigned argIndex, ExprHandle *&init, Parser::ParameterContextKind paramContext) { SourceLoc equalLoc = P.consumeToken(tok::equal); // Enter a fresh default-argument context with a meaningless parent. // We'll change the parent to the function later after we've created // that declaration. auto initDC = P.Context.createDefaultArgumentContext(P.CurDeclContext, argIndex); Parser::ParseFunctionBody initScope(P, initDC); ParserResult<Expr> initR = P.parseExpr(diag::expected_init_value); // Give back the default-argument context if we didn't need it. if (!initScope.hasClosures()) { P.Context.destroyDefaultArgumentContext(initDC); // Otherwise, record it if we're supposed to accept default // arguments here. } else if (defaultArgs) { defaultArgs->ParsedContexts.push_back(initDC); } Diag<> diagID = { DiagID() }; switch (paramContext) { case Parser::ParameterContextKind::Function: case Parser::ParameterContextKind::Operator: case Parser::ParameterContextKind::Initializer: break; case Parser::ParameterContextKind::Closure: diagID = diag::no_default_arg_closure; break; case Parser::ParameterContextKind::Subscript: diagID = diag::no_default_arg_subscript; break; case Parser::ParameterContextKind::Curried: diagID = diag::no_default_arg_curried; break; } assert(((diagID.ID != DiagID()) == !defaultArgs || // Sometimes curried method parameter lists get default arg info. // Remove this when they go away. paramContext == Parser::ParameterContextKind::Curried) && "Default arguments specified for an unexpected parameter list kind"); if (diagID.ID != DiagID()) { auto inFlight = P.diagnose(equalLoc, diagID); if (initR.isNonNull()) inFlight.fixItRemove(SourceRange(equalLoc, initR.get()->getEndLoc())); return ParserStatus(); } defaultArgs->HasDefaultArgument = true; if (initR.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (initR.isNull()) return makeParserError(); init = ExprHandle::get(P.Context, initR.get()); return ParserStatus(); }
ParserResult<GenericParamList> Parser::parseGenericParameters(SourceLoc LAngleLoc) { // Parse the generic parameter list. SmallVector<GenericTypeParamDecl *, 4> GenericParams; bool Invalid = false; do { // Note that we're parsing a declaration. StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(), StructureMarkerKind::Declaration); // 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)) { Invalid = true; 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 = parseTypeIdentifierOrTypeComposition(); } else if (Tok.is(tok::kw_class)) { diagnose(Tok, diag::unexpected_class_constraint); diagnose(Tok, diag::suggest_anyobject, Name) .fixItReplace(Tok.getLoc(), "AnyObject"); consumeToken(); Invalid = true; } else { diagnose(Tok, diag::expected_generics_type_restriction, Name); Invalid = true; } if (Ty.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (Ty.isNonNull()) Inherited.push_back(Ty.get()); } // We always create generic type parameters with a depth of zero. // Semantic analysis fills in the depth when it processes the generic // parameter list. auto Param = new (Context) GenericTypeParamDecl(CurDeclContext, Name, NameLoc, /*Depth=*/0, 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. } while (consumeIf(tok::comma)); // Parse the optional where-clause. SourceLoc WhereLoc; SmallVector<RequirementRepr, 4> Requirements; bool FirstTypeInComplete; if (Tok.is(tok::kw_where) && parseGenericWhereClause(WhereLoc, Requirements, FirstTypeInComplete).isError()) { Invalid = true; } // Parse the closing '>'. SourceLoc RAngleLoc; if (!startsWithGreater(Tok)) { if (!Invalid) { diagnose(Tok, diag::expected_rangle_generics_param); diagnose(LAngleLoc, diag::opening_angle); Invalid = true; } // Skip until we hit the '>'. skipUntilGreaterInTypeList(); if (startsWithGreater(Tok)) RAngleLoc = consumeStartingGreater(); else RAngleLoc = Tok.getLoc(); } else { RAngleLoc = consumeStartingGreater(); } if (GenericParams.empty()) return nullptr; return makeParserResult(GenericParamList::create(Context, LAngleLoc, GenericParams, WhereLoc, Requirements, RAngleLoc)); }
/// 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; }