//===--- SyntaxModel.cpp - Routines for IDE syntax model ------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/IDE/SyntaxModel.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Pattern.h" #include "swift/AST/Module.h" #include "swift/AST/Stmt.h" #include "swift/AST/TypeRepr.h" #include "swift/Basic/SourceManager.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/Token.h" #include "swift/Config.h" #include "swift/Subsystems.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/MemoryBuffer.h" #include #include using namespace swift; using namespace ide; void SyntaxModelWalker::anchor() {} struct SyntaxModelContext::Implementation { SourceFile &SrcFile; const LangOptions &LangOpts; const SourceManager &SrcMgr; std::vector TokenNodes; Implementation(SourceFile &SrcFile) : SrcFile(SrcFile), LangOpts(SrcFile.getASTContext().LangOpts), SrcMgr(SrcFile.getASTContext().SourceMgr) {} }; SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) : Impl(*new Implementation(SrcFile)) { const bool IsPlayground = Impl.LangOpts.Playground; const SourceManager &SM = Impl.SrcMgr; std::vector Tokens = swift::tokenize(Impl.LangOpts, SM, *Impl.SrcFile.getBufferID(), /*Offset=*/0, /*EndOffset=*/0, /*KeepComments=*/true, /*TokenizeInterpolatedString=*/true); std::vector Nodes; SourceLoc AttrLoc; SourceLoc UnaryMinusLoc; auto LiteralStartLoc = Optional(); for (unsigned I = 0, E = Tokens.size(); I != E; ++I) { auto &Tok = Tokens[I]; SyntaxNodeKind Kind; SourceLoc Loc; Optional Length; if (AttrLoc.isValid()) { // This token is following @, see if it's a known attribute name. if (TypeAttributes::getAttrKindFromString(Tok.getText()) != TAK_Count || DeclAttribute::getAttrKindFromString(Tok.getText()) != DAK_Count) { // It's a known attribute, so treat it as a syntactic attribute node for // syntax coloring. If swift gets user attributes then all identifiers // will be treated as syntactic attribute nodes. Loc = AttrLoc; Length = SM.getByteDistance(Loc, Tok.getLoc()) + Tok.getLength(); Kind = SyntaxNodeKind::AttributeId; } AttrLoc = SourceLoc(); } if (!Loc.isValid()) { Loc = Tok.getLoc(); Length = Tok.getLength(); if (LiteralStartLoc.hasValue() && Length.hasValue()) { // We are still inside an object literal until we hit a r_square_lit. if (Tok.getKind() != tok::r_square_lit) { continue; } Kind = SyntaxNodeKind::ObjectLiteral; Nodes.emplace_back(Kind, CharSourceRange(SM, LiteralStartLoc.getValue(), Tok.getRange().getEnd())); LiteralStartLoc = Optional(); continue; } switch(Tok.getKind()) { #define KEYWORD(X) case tok::kw_##X: Kind = SyntaxNodeKind::Keyword; break; #include "swift/Parse/Tokens.def" #undef KEYWORD case tok::pound_selector: case tok::pound_file: case tok::pound_column: case tok::pound_function: case tok::pound_dsohandle: Kind = SyntaxNodeKind::Keyword; break; case tok::pound_line: Kind = Tok.isAtStartOfLine() ? SyntaxNodeKind::BuildConfigKeyword : SyntaxNodeKind::Keyword; break; case tok::pound_available: Kind = SyntaxNodeKind::BuildConfigKeyword; break; case tok::identifier: if (Tok.getText().startswith("<#")) Kind = SyntaxNodeKind::EditorPlaceholder; else Kind = SyntaxNodeKind::Identifier; break; case tok::dollarident: Kind = SyntaxNodeKind::DollarIdent; break; case tok::string_literal: Kind = SyntaxNodeKind::String; break; case tok::integer_literal: Kind = SyntaxNodeKind::Integer; if (UnaryMinusLoc.isValid()) { Loc = UnaryMinusLoc; Length = *Length + SM.getByteDistance(UnaryMinusLoc, Tok.getLoc()); } break; case tok::floating_literal: Kind = SyntaxNodeKind::Floating; if (UnaryMinusLoc.isValid()) { Loc = UnaryMinusLoc; Length = *Length + SM.getByteDistance(UnaryMinusLoc, Tok.getLoc()); } break; case tok::oper_prefix: if (Tok.getText() == "-") UnaryMinusLoc = Loc; continue; case tok::comment: if (Tok.getText().startswith("///") || (IsPlayground && Tok.getText().startswith("//:"))) Kind = SyntaxNodeKind::DocCommentLine; else if (Tok.getText().startswith("/**") || (IsPlayground && Tok.getText().startswith("/*:"))) Kind = SyntaxNodeKind::DocCommentBlock; else if (Tok.getText().startswith("//")) Kind = SyntaxNodeKind::CommentLine; else Kind = SyntaxNodeKind::CommentBlock; break; case tok::at_sign: // Set the location of @ and continue. Next token should be the // attribute name. AttrLoc = Tok.getLoc(); continue; case tok::l_paren: { // Check if this is a string interpolation paren. if (I == 0) continue; auto &PrevTok = Tokens[I-1]; if (PrevTok.getKind() != tok::string_literal) continue; StringRef StrText = PrevTok.getText(); if (StrText.size() > 1 && StrText.back() == '\"' && !StrText.endswith("\\\"")) continue; Kind = SyntaxNodeKind::StringInterpolationAnchor; break; } case tok::r_paren: { // Check if this is a string interpolation paren. if (I+1 == E) continue; auto &NextTok = Tokens[I+1]; if (NextTok.getKind() != tok::string_literal) continue; StringRef StrText = NextTok.getText(); if (StrText.size() > 1 && StrText.front() == '\"') continue; Kind = SyntaxNodeKind::StringInterpolationAnchor; break; } case tok::l_square_lit: { LiteralStartLoc = Loc; continue; } default: continue; } } UnaryMinusLoc = SourceLoc(); // Reset. assert(Loc.isValid()); assert(Nodes.empty() || SM.isBeforeInBuffer(Nodes.back().Range.getStart(), Loc)); Nodes.emplace_back(Kind, CharSourceRange(Loc, Length.getValue())); } Impl.TokenNodes = std::move(Nodes); } SyntaxModelContext::~SyntaxModelContext() { delete &Impl; } namespace { typedef ASTWalker::ParentTy ASTNodeType; struct StructureElement { SyntaxStructureNode StructureNode; ASTNodeType ASTNode; StructureElement(const SyntaxStructureNode &StructureNode, const ASTNodeType &ASTNode) :StructureNode(StructureNode), ASTNode(ASTNode) { } }; static const std::vector URLProtocols = { // Use RegexStrURL: "acap", "afp", "afs", "cid", "data", "fax", "feed", "file", "ftp", "go", "gopher", "http", "https", "imap", "ldap", "mailserver", "mid", "modem", "news", "nntp", "opaquelocktoken", "pop", "prospero", "rdar", "rtsp", "service" "sip", "soap.beep", "soap.beeps", "tel", "telnet", "tip", "tn3270", "urn", "vemmi", "wais", "xcdoc", "z39.50r","z39.50s", // Use RegexStrMailURL: "mailto", "im", // Use RegexStrRadarURL: "radar" }; static const char *const RegexStrURL = "(acap|afp|afs|cid|data|fax|feed|file|ftp|go|" "gopher|http|https|imap|ldap|mailserver|mid|modem|news|nntp|opaquelocktoken|" "pop|prospero|rdar|rtsp|service|sip|soap\\.beep|soap\\.beeps|tel|telnet|tip|" "tn3270|urn|vemmi|wais|xcdoc|z39\\.50r|z39\\.50s)://" "([a-zA-Z0-9\\-_.]+/)?[a-zA-Z0-9;/?:@\\&=+$,\\-_.!~*'()%#]+"; #define MARKUP_SIMPLE_FIELD(Id, Keyword, XMLKind) \ #Keyword "|" static const char *const RegexStrDocCommentField = "^[ ]?- (" #include "swift/Markup/SimpleFields.def" "returns):"; static const char *const RegexStrParameter = "^[ ]?- (parameter) [^:]*:"; static const char *const RegexStrDocCommentParametersHeading = "^[ ]?- (Parameters):"; static const char *const RegexStrMailURL = "(mailto|im):[a-zA-Z0-9\\-_]+@[a-zA-Z0-9\\-_\\.!%]+"; static const char *const RegexStrRadarURL = "radar:[a-zA-Z0-9;/?:@\\&=+$,\\-_.!~*'()%#]+"; class ModelASTWalker : public ASTWalker { const LangOptions &LangOpts; const SourceManager &SM; unsigned BufferID; std::vector SubStructureStack; SourceLoc LastLoc; static const std::regex &getURLRegex(unsigned Index); static const std::regex &getDocCommentRegex(unsigned Index); Optional parseFieldNode(StringRef Text, StringRef OrigText, SourceLoc OrigLoc); llvm::DenseSet VisitedNodesInsideIfConfig; public: SyntaxModelWalker &Walker; ArrayRef TokenNodes; ModelASTWalker(const LangOptions &LangOpts, const SourceManager &SM, unsigned BufferID, SyntaxModelWalker &Walker) : LangOpts(LangOpts), SM(SM), BufferID(BufferID), Walker(Walker) { } void visitSourceFile(SourceFile &SrcFile, ArrayRef Tokens); std::pair walkToExprPre(Expr *E) override; Expr *walkToExprPost(Expr *E) override; std::pair walkToStmtPre(Stmt *S) override; Stmt *walkToStmtPost(Stmt *S) override; bool walkToDeclPre(Decl *D) override; bool walkToDeclPost(Decl *D) override; bool walkToTypeReprPre(TypeRepr *T) override; std::pair walkToPatternPre(Pattern *P) override; bool shouldWalkIntoFunctionGenericParams() override { return true; } private: static bool findUrlStartingLoc(StringRef Text, unsigned &Start, std::regex& Regex); bool annotateIfConfigConditionIdentifiers(Expr *Cond); bool handleAttrs(const DeclAttributes &Attrs); bool handleAttrs(const TypeAttributes &Attrs); typedef std::pair DeclAttributeAndRange; bool handleSpecialDeclAttribute(const DeclAttribute *Decl, std::vector &Toks); bool handleAttrRanges(ArrayRef DeclRanges); void handleStmtCondition(StmtCondition cond); bool shouldPassBraceStructureNode(BraceStmt *S); enum PassNodesBehavior { /// Pass all nodes up to but not including the location. ExcludeNodeAtLocation, /// Pass all nodes up to and including the location. IncludeNodeAtLocation, /// Like ExcludeNodeAtLocation, and skip past any node at the location. DisplaceNodeAtLocation }; bool passTokenNodesUntil(SourceLoc Loc, PassNodesBehavior Behavior); bool passNonTokenNode(const SyntaxNode &Node); bool passNode(const SyntaxNode &Node); bool pushStructureNode(const SyntaxStructureNode &Node, const ASTNodeType& ASTNode); bool popStructureNode(); bool isCurrentCallArgExpr(const Expr *E); bool processComment(CharSourceRange Range); bool searchForURL(CharSourceRange Range); bool findFieldsInDocCommentLine(SyntaxNode Node); bool findFieldsInDocCommentBlock(SyntaxNode Node); bool isVisitedBeforeInIfConfigStmt(ASTNode Node) { return VisitedNodesInsideIfConfig.count(Node) > 0; } }; const std::regex &ModelASTWalker::getURLRegex(unsigned Index) { static const std::regex Regexes[3] = { std::regex{ RegexStrURL, std::regex::ECMAScript | std::regex::nosubs }, std::regex{ RegexStrMailURL, std::regex::ECMAScript | std::regex::nosubs }, std::regex{ RegexStrRadarURL, std::regex::ECMAScript | std::regex::nosubs } }; return Regexes[Index]; } const std::regex &ModelASTWalker::getDocCommentRegex(unsigned Index) { static const std::regex Regexes[3] = { std::regex { RegexStrParameter, std::regex::egrep | std::regex::icase | std::regex::optimize }, std::regex { RegexStrDocCommentParametersHeading, std::regex::egrep | std::regex::icase | std::regex::optimize }, std::regex { RegexStrDocCommentField, std::regex::egrep | std::regex::icase | std::regex::optimize } }; return Regexes[Index]; } SyntaxStructureKind syntaxStructureKindFromNominalTypeDecl(NominalTypeDecl *N) { if (isa(N)) return SyntaxStructureKind::Class; else if (isa(N)) return SyntaxStructureKind::Struct; else if (isa(N)) return SyntaxStructureKind::Protocol; else { // All other known NominalTypeDecl derived classes covered, so assert() here. assert(isa(N)); return SyntaxStructureKind::Enum; } } CharSourceRange charSourceRangeFromSourceRange(const SourceManager &SM, const SourceRange &SR) { return Lexer::getCharSourceRangeFromSourceRange(SM, SR); } CharSourceRange innerCharSourceRangeFromSourceRange(const SourceManager &SM, const SourceRange &SR) { if (SR.isInvalid()) return CharSourceRange(); SourceLoc SRS = Lexer::getLocForEndOfToken(SM, SR.Start); return CharSourceRange(SM, SRS, (SR.End != SR.Start) ? SR.End : SRS); } CharSourceRange parameterNameRangeOfCallArg(const TupleExpr *TE, const Expr *Arg) { if (!TE->hasElementNameLocs() || !TE->hasElementNames()) return CharSourceRange(); // Loop over the elements to find the index representing Arg. // This is somewhat inefficient but the only way to find the corresponding // name without the index, and the number of parameters in a call is normally // very low. If this becomes a performance problem, we could perhaps have // ASTWalker visit the element name as well. unsigned i = 0; for (auto E : TE->getElements()) { if (E == Arg) { SourceLoc NL = TE->getElementNameLoc(i); Identifier Name = TE->getElementName(i); if (NL.isValid() && !Name.empty()) return CharSourceRange(NL, Name.getLength()); return CharSourceRange(); } ++i; } return CharSourceRange(); } } // anonymous namespace bool SyntaxModelContext::walk(SyntaxModelWalker &Walker) { ModelASTWalker ASTWalk(Impl.LangOpts, Impl.SrcMgr, *Impl.SrcFile.getBufferID(), Walker); ASTWalk.visitSourceFile(Impl.SrcFile, Impl.TokenNodes); return true; } void ModelASTWalker::visitSourceFile(SourceFile &SrcFile, ArrayRef Tokens) { TokenNodes = Tokens; SrcFile.walk(*this); // Pass the rest of the token nodes. for (auto &TokNode : TokenNodes) passNode(TokNode); } std::pair ModelASTWalker::walkToExprPre(Expr *E) { if (isVisitedBeforeInIfConfigStmt(E)) return {false, E}; if (E->isImplicit()) return { true, E }; if (auto *ParentTupleExpr = dyn_cast_or_null(Parent.getAsExpr())) { if (isCurrentCallArgExpr(ParentTupleExpr)) { CharSourceRange NR = parameterNameRangeOfCallArg(ParentTupleExpr, E); SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::Parameter; SN.NameRange = NR; SN.BodyRange = charSourceRangeFromSourceRange(SM, E->getSourceRange()); if (NR.isValid()) SN.Range = charSourceRangeFromSourceRange(SM, SourceRange(NR.getStart(), E->getEndLoc())); else SN.Range = SN.BodyRange; pushStructureNode(SN, E); } } auto addExprElem = [&](const Expr *Elem, SyntaxStructureNode &SN) { if (isa(Elem)) return; SourceRange R = Elem->getSourceRange(); if (R.isInvalid()) return; SN.Elements.emplace_back(SyntaxStructureElementKind::Expr, charSourceRangeFromSourceRange(SM, R)); }; if (auto *CE = dyn_cast(E)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::CallExpression; SN.Range = charSourceRangeFromSourceRange(SM, E->getSourceRange()); if (CE->getFn() && CE->getFn()->getSourceRange().isValid()) SN.NameRange = charSourceRangeFromSourceRange(SM, CE->getFn()->getSourceRange()); if (CE->getArg() && CE->getArg()->getSourceRange().isValid()) SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, CE->getArg()->getSourceRange()); pushStructureNode(SN, CE); } else if (auto *ObjectE = dyn_cast(E)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::ObjectLiteralExpression; SN.Range = charSourceRangeFromSourceRange(SM, E->getSourceRange()); SourceLoc NRStart = ObjectE->getNameLoc(); SourceLoc NREnd = NRStart.getAdvancedLoc(ObjectE->getName().getLength()); SN.NameRange = CharSourceRange(SM, NRStart, NREnd); SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, E->getSourceRange()); pushStructureNode(SN, E); } else if (auto *ArrayE = dyn_cast(E)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::ArrayExpression; SN.Range = charSourceRangeFromSourceRange(SM, E->getSourceRange()); for (auto *Elem : ArrayE->getElements()) addExprElem(Elem, SN); SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, E->getSourceRange()); pushStructureNode(SN, E); } else if (auto *DictE = dyn_cast(E)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::DictionaryExpression; SN.Range = charSourceRangeFromSourceRange(SM, E->getSourceRange()); for (auto *Elem : DictE->getElements()) { if (auto *TupleE = dyn_cast(Elem)) { for (auto *TE : TupleE->getElements()) addExprElem(TE, SN); } else { addExprElem(Elem, SN); } } SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, E->getSourceRange()); pushStructureNode(SN, E); } return { true, E }; } Expr *ModelASTWalker::walkToExprPost(Expr *E) { while (!SubStructureStack.empty() && SubStructureStack.back().ASTNode.getAsExpr() == E) popStructureNode(); return E; } void ModelASTWalker::handleStmtCondition(StmtCondition cond) { for (const auto &elt : cond) { if (elt.getKind() != StmtConditionElement::CK_Availability) continue; SmallVector PlatformRanges; elt.getAvailability()->getPlatformKeywordRanges(PlatformRanges); std::for_each(PlatformRanges.begin(), PlatformRanges.end(), [&](CharSourceRange &Range) { passNonTokenNode({SyntaxNodeKind::Keyword, Range}); }); } } std::pair ModelASTWalker::walkToStmtPre(Stmt *S) { if (isVisitedBeforeInIfConfigStmt(S)) { return {false, S}; } auto addExprElem = [&](SyntaxStructureElementKind K, const Expr *Elem, SyntaxStructureNode &SN) { if (isa(Elem)) return; SourceRange R = Elem->getSourceRange(); if (R.isInvalid()) return; SN.Elements.emplace_back(K, charSourceRangeFromSourceRange(SM, R)); }; if (auto *ForEachS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::ForEachStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); if (ForEachS->getPattern()) SN.Elements.emplace_back(SyntaxStructureElementKind::Id, charSourceRangeFromSourceRange(SM, ForEachS->getPattern()->getSourceRange())); if (ForEachS->getSequence()) addExprElem(SyntaxStructureElementKind::Expr, ForEachS->getSequence(),SN); pushStructureNode(SN, S); } else if (auto *ForS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::ForStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); if (!ForS->getInitializerVarDecls().empty()) { auto InitDs = ForS->getInitializerVarDecls(); // Initializer decls come as a PatternBindingDecl followed by VarDecl's // for each pattern set up. The PBD covers the whole initializer. assert(isa(InitDs[0])); SourceRange ElemRange = InitDs[0]->getSourceRange(); SN.Elements.emplace_back(SyntaxStructureElementKind::InitExpr, charSourceRangeFromSourceRange(SM, ElemRange)); } else if (ForS->getInitializer()) { addExprElem(SyntaxStructureElementKind::InitExpr, ForS->getInitializer().get(), SN); } if (ForS->getCond()) { addExprElem(SyntaxStructureElementKind::Expr, ForS->getCond().get(), SN); } if (ForS->getIncrement()) { addExprElem(SyntaxStructureElementKind::Expr, ForS->getIncrement().get(), SN); } pushStructureNode(SN, S); } else if (auto *WhileS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::WhileStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); if (!WhileS->getCond().empty()) { auto Conds = WhileS->getCond(); SourceRange ElemRange = SourceRange(Conds.front().getSourceRange().Start, Conds.back().getSourceRange().End); SN.Elements.emplace_back(SyntaxStructureElementKind::ConditionExpr, charSourceRangeFromSourceRange(SM, ElemRange)); } pushStructureNode(SN, S); handleStmtCondition(WhileS->getCond()); } else if (auto *RepeatWhileS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::RepeatWhileStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); if (RepeatWhileS->getCond()) { addExprElem(SyntaxStructureElementKind::Expr, RepeatWhileS->getCond(), SN); } pushStructureNode(SN, S); } else if (auto *IfS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::IfStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); if (!IfS->getCond().empty()) { auto Conds = IfS->getCond(); SourceRange ElemRange = SourceRange(Conds.front().getSourceRange().Start, Conds.back().getSourceRange().End); SN.Elements.emplace_back(SyntaxStructureElementKind::ConditionExpr, charSourceRangeFromSourceRange(SM, ElemRange)); } pushStructureNode(SN, S); handleStmtCondition(IfS->getCond()); } else if (auto *GS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::GuardStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); if (!GS->getCond().empty()) { auto Conds = GS->getCond(); SourceRange ElemRange = SourceRange(Conds.front().getSourceRange().Start, Conds.back().getSourceRange().End); SN.Elements.emplace_back(SyntaxStructureElementKind::ConditionExpr, charSourceRangeFromSourceRange(SM, ElemRange)); } pushStructureNode(SN, S); handleStmtCondition(GS->getCond()); } else if (auto *SwitchS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::SwitchStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); if (SwitchS->getSubjectExpr()) { addExprElem(SyntaxStructureElementKind::Expr, SwitchS->getSubjectExpr(), SN); } pushStructureNode(SN, S); } else if (auto *CaseS = dyn_cast(S)) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::CaseStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); for (const CaseLabelItem &Item : CaseS->getCaseLabelItems()) { SN.Elements.emplace_back(SyntaxStructureElementKind::Pattern, charSourceRangeFromSourceRange(SM, Item.getSourceRange())); } pushStructureNode(SN, S); } else if (isa(S) && shouldPassBraceStructureNode(cast(S))) { // Pass BraceStatement structure node. SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::BraceStatement; SN.Range = charSourceRangeFromSourceRange(SM, S->getSourceRange()); SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, S->getSourceRange()); pushStructureNode(SN, S); } else if (auto *SW = dyn_cast(S)) { if (SW->getLBraceLoc().isValid() && SW->getRBraceLoc().isValid()) { SourceRange BraceRange(SW->getLBraceLoc(), SW->getRBraceLoc()); SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::BraceStatement; SN.Range = charSourceRangeFromSourceRange(SM, BraceRange); SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, BraceRange); pushStructureNode(SN, SW); } } else if (auto ConfigS = dyn_cast(S)) { for (auto &Clause : ConfigS->getClauses()) { unsigned TokLen; if (&Clause == &*ConfigS->getClauses().begin()) TokLen = 3; // '#if' else if (Clause.Cond == nullptr) TokLen = 5; // '#else' else TokLen = 7; // '#elseif' if (!passNonTokenNode({SyntaxNodeKind::BuildConfigKeyword, CharSourceRange(Clause.Loc, TokLen) })) return { false, nullptr }; if (Clause.Cond && !annotateIfConfigConditionIdentifiers(Clause.Cond)) return { false, nullptr }; for(auto &Element : Clause.Elements) { if (Expr *E = Element.dyn_cast()) { E->walk(*this); } else if (Stmt *S = Element.dyn_cast()) { S->walk(*this); } else { Element.get()->walk(*this); } VisitedNodesInsideIfConfig.insert(Element); } } if (!ConfigS->hadMissingEnd()) if (!passNonTokenNode({ SyntaxNodeKind::BuildConfigKeyword, CharSourceRange(ConfigS->getEndLoc(), 6/*'#endif'*/) })) return { false, nullptr }; } else if (auto *DeferS = dyn_cast(S)) { if (auto *FD = DeferS->getTempDecl()) { auto *RetS = FD->getBody()->walk(*this); // Already walked children. return { false, RetS }; } } return { true, S }; } Stmt *ModelASTWalker::walkToStmtPost(Stmt *S) { while (!SubStructureStack.empty() && SubStructureStack.back().ASTNode.getAsStmt() == S) popStructureNode(); return S; } bool ModelASTWalker::walkToDeclPre(Decl *D) { if (isVisitedBeforeInIfConfigStmt(D)) return false; if (D->isImplicit()) return false; if (!handleAttrs(D->getAttrs())) return false; if (auto *AFD = dyn_cast(D)) { FuncDecl *FD = dyn_cast(AFD); if (FD && FD->isAccessor()) { // Pass context sensitive keyword token. SourceLoc SL = FD->getFuncLoc(); // Make sure the func loc is not the start of the function body, in which // case the context sensitive keyword was implied. if (FD->getBodySourceRange().Start != SL) { unsigned TokLen; switch (FD->getAccessorKind()) { case AccessorKind::NotAccessor: llvm_unreachable("expected accessor"); case AccessorKind::IsGetter: TokLen = 3; break; case AccessorKind::IsSetter: TokLen = 3; break; case AccessorKind::IsAddressor: TokLen = 7; break; case AccessorKind::IsMutableAddressor: TokLen = 14; break; case AccessorKind::IsWillSet: TokLen = 7; break; case AccessorKind::IsDidSet: TokLen = 6; break; case AccessorKind::IsMaterializeForSet: llvm_unreachable("always implicit"); } if (!passNonTokenNode({ SyntaxNodeKind::Keyword, CharSourceRange(SL, TokLen)})) return false; } } else { // Pass Function / Method structure node. SyntaxStructureNode SN; SN.Dcl = D; const DeclContext *DC = AFD->getDeclContext(); if (DC->isTypeContext()) { if (FD && FD->isStatic()) { if (FD->getStaticSpelling() == StaticSpellingKind::KeywordClass) SN.Kind = SyntaxStructureKind::ClassFunction; else SN.Kind = SyntaxStructureKind::StaticFunction; } else { SN.Kind = SyntaxStructureKind::InstanceFunction; } } else SN.Kind = SyntaxStructureKind::FreeFunction; SN.Range = charSourceRangeFromSourceRange(SM, AFD->getSourceRange()); SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, AFD->getBodySourceRange()); SN.NameRange = charSourceRangeFromSourceRange(SM, AFD->getSignatureSourceRange()); SN.Attrs = AFD->getAttrs(); pushStructureNode(SN, AFD); } } else if (auto *NTD = dyn_cast(D)) { SyntaxStructureNode SN; SN.Dcl = D; SN.Kind = syntaxStructureKindFromNominalTypeDecl(NTD); SN.Range = charSourceRangeFromSourceRange(SM, NTD->getSourceRange()); SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, NTD->getBraces()); SourceLoc NRStart = NTD->getNameLoc(); SourceLoc NREnd = NRStart.getAdvancedLoc(NTD->getName().getLength()); SN.NameRange = CharSourceRange(SM, NRStart, NREnd); for (const TypeLoc &TL : NTD->getInherited()) { CharSourceRange TR = charSourceRangeFromSourceRange(SM, TL.getSourceRange()); SN.InheritedTypeRanges.push_back(TR); SN.Elements.emplace_back(SyntaxStructureElementKind::TypeRef, TR); } SN.Attrs = NTD->getAttrs(); pushStructureNode(SN, NTD); } else if (auto *ED = dyn_cast(D)) { SyntaxStructureNode SN; SN.Dcl = D; SN.Kind = SyntaxStructureKind::Extension; SN.Range = charSourceRangeFromSourceRange(SM, ED->getSourceRange()); SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, ED->getBraces()); SourceRange NSR = ED->getExtendedTypeLoc().getSourceRange(); SN.NameRange = charSourceRangeFromSourceRange(SM, NSR); for (const TypeLoc &TL : ED->getInherited()) { CharSourceRange TR = charSourceRangeFromSourceRange(SM, TL.getSourceRange()); SN.InheritedTypeRanges.push_back(TR); SN.Elements.emplace_back(SyntaxStructureElementKind::TypeRef, TR); } SN.Attrs = ED->getAttrs(); pushStructureNode(SN, ED); } else if (auto *PD = dyn_cast(D)) { SyntaxStructureNode SN; SN.Dcl = D; SN.Kind = SyntaxStructureKind::Parameter; if (!PD->getArgumentName().empty()) { SourceLoc ArgStart = PD->getSourceRange().Start; SN.NameRange = CharSourceRange(ArgStart, PD->getArgumentName().getLength()); passTokenNodesUntil(ArgStart, PassNodesBehavior::ExcludeNodeAtLocation); const_cast(TokenNodes.front()).Kind = SyntaxNodeKind:: Identifier; } SN.Range = charSourceRangeFromSourceRange(SM, PD->getSourceRange()); SN.Attrs = PD->getAttrs(); SN.TypeRange = charSourceRangeFromSourceRange(SM, PD->getTypeSourceRangeForDiagnostics()); pushStructureNode(SN, PD); } else if (auto *VD = dyn_cast(D)) { const DeclContext *DC = VD->getDeclContext(); if (DC->isTypeContext() || DC->isModuleScopeContext()) { SyntaxStructureNode SN; SN.Dcl = D; SourceRange SR; if (auto *PBD = VD->getParentPatternBinding()) SR = PBD->getSourceRange(); else SR = VD->getSourceRange(); SN.Range = charSourceRangeFromSourceRange(SM, SR); if (VD->hasAccessorFunctions()) SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, VD->getBracesRange()); SourceLoc NRStart = VD->getNameLoc(); SourceLoc NREnd = NRStart.getAdvancedLoc(VD->getName().getLength()); SN.NameRange = CharSourceRange(SM, NRStart, NREnd); SN.TypeRange = charSourceRangeFromSourceRange(SM, VD->getTypeSourceRangeForDiagnostics()); if (DC->isTypeContext()) { if (VD->isStatic()) { StaticSpellingKind Spell = StaticSpellingKind::KeywordStatic; if (auto *PBD = VD->getParentPatternBinding()) Spell = PBD->getStaticSpelling(); if (Spell == StaticSpellingKind::KeywordClass) SN.Kind = SyntaxStructureKind::ClassVariable; else SN.Kind = SyntaxStructureKind::StaticVariable; } else { SN.Kind = SyntaxStructureKind::InstanceVariable; } } else { SN.Kind = SyntaxStructureKind::GlobalVariable; } SN.Attrs = VD->getAttrs(); pushStructureNode(SN, VD); } } else if (auto *ConfigD = dyn_cast(D)) { for (auto &Clause : ConfigD->getClauses()) { unsigned TokLen; if (&Clause == &*ConfigD->getClauses().begin()) TokLen = 3; // '#if' else if (Clause.Cond == nullptr) TokLen = 5; // '#else' else TokLen = 7; // '#elseif' if (!passNonTokenNode({SyntaxNodeKind::BuildConfigKeyword, CharSourceRange(Clause.Loc, TokLen) })) return false; if (Clause.Cond && !annotateIfConfigConditionIdentifiers(Clause.Cond)) return false; for (auto *D : Clause.Members) if (D->walk(*this)) return false; } if (!ConfigD->hadMissingEnd()) if (!passNonTokenNode({ SyntaxNodeKind::BuildConfigKeyword, CharSourceRange(ConfigD->getEndLoc(), 6/*'#endif'*/) })) return false; } else if (auto OperD = dyn_cast(D)) { // If the operator is infix operator, highlight specifiers like // "associativity" or "assignment" as keywords. if (auto IFO = dyn_cast(OperD)) { SmallVector KeywordsRanges; IFO->collectOperatorKeywordRanges(KeywordsRanges); for (auto &Range : KeywordsRanges) { passNonTokenNode({SyntaxNodeKind::Keyword, Range}); }; } if (!passNonTokenNode({ SyntaxNodeKind::Keyword, CharSourceRange(OperD->getOperatorLoc(), strlen("operator")) })) return false; } else if (auto *EnumCaseD = dyn_cast(D)) { SyntaxStructureNode SN; SN.Dcl = D; SN.Kind = SyntaxStructureKind::EnumCase; SN.Range = charSourceRangeFromSourceRange(SM, D->getSourceRange()); // We need to handle the special case where attributes semantically // attach to enum element decls while syntactically locate before enum case decl. for (auto *EnumElemD : EnumCaseD->getElements()) { for (auto *Att : EnumElemD->getAttrs()) { if (Att->isDeclModifier() && SM.isBeforeInBuffer(Att->getLocation(), D->getSourceRange().Start)) { passNonTokenNode({SyntaxNodeKind::AttributeBuiltin, charSourceRangeFromSourceRange(SM, Att->getLocation())}); } } } if (pushStructureNode(SN, D)) { // FIXME: ASTWalker walks enum elements as members of the enum decl, not // as members of the enum case decl. Walk them manually here so that they // end up as child nodes of enum case. for (auto *EnumElemD : EnumCaseD->getElements()) { if (EnumElemD->getName().empty()) continue; SyntaxStructureNode SN; SN.Dcl = EnumElemD; SN.Kind = SyntaxStructureKind::EnumElement; SN.Range = charSourceRangeFromSourceRange(SM, EnumElemD->getSourceRange()); SN.NameRange = CharSourceRange(EnumElemD->getNameLoc(), EnumElemD->getName().getLength()); if (auto *E = EnumElemD->getRawValueExpr()) { SourceRange ElemRange = E->getSourceRange(); SN.Elements.emplace_back(SyntaxStructureElementKind::InitExpr, charSourceRangeFromSourceRange(SM, ElemRange)); } pushStructureNode(SN, EnumElemD); popStructureNode(); } } } return true; } bool ModelASTWalker::walkToDeclPost(swift::Decl *D) { while (!SubStructureStack.empty() && SubStructureStack.back().ASTNode.getAsDecl() == D) popStructureNode(); return true; } bool ModelASTWalker::walkToTypeReprPre(TypeRepr *T) { if (auto AttrT = dyn_cast(T)) { if (!handleAttrs(AttrT->getAttrs())) return false; } else if (auto IdT = dyn_cast(T)) { if (!passNonTokenNode({ SyntaxNodeKind::TypeId, CharSourceRange(IdT->getIdLoc(), IdT->getIdentifier().getLength()) })) return false; } return true; } std::pair ModelASTWalker::walkToPatternPre(Pattern *P) { if (!P->isImplicit()) { if (auto TyPat = dyn_cast(P)) { if (auto InOutT = dyn_cast_or_null(TyPat->getTypeLoc().getTypeRepr())) { if (!passNonTokenNode({ SyntaxNodeKind::Keyword, CharSourceRange(InOutT->getInOutLoc(), /*'inout'*/5) })) return { false, nullptr }; } } } return { true, P }; } namespace { template class IdRefWalker : public ASTWalker { const FnTy &Fn; public: IdRefWalker(const FnTy &Fn) : Fn(Fn) {} std::pair walkToExprPre(Expr *E) override { if (auto DRE = dyn_cast(E)) { if (!DRE->hasName()) return { true, E }; if (DRE->getRefKind() != DeclRefKind::Ordinary) return { true, E }; if (!Fn(CharSourceRange(DRE->getSourceRange().Start, DRE->getName().getBaseName().getLength()))) return { false, nullptr }; } return { true, E }; } }; } bool ModelASTWalker::annotateIfConfigConditionIdentifiers(Expr *Cond) { if (!Cond) return true; auto passNode = [&](CharSourceRange R) { return passNonTokenNode({ SyntaxNodeKind::BuildConfigId, R }); }; IdRefWalker Walker(passNode); if (!Cond->walk(Walker)) return false; return true; } bool ModelASTWalker::handleSpecialDeclAttribute(const DeclAttribute *D, std::vector &Toks) { if (!D) return false; if (isa(D)) { std::vector PlatformLocs; for (auto T : Toks) { if (!SM.rangeContainsTokenLoc(D->getRangeWithAt(), T.getLoc())) continue; #define AVAILABILITY_PLATFORM(X, PrettyName) \ if (#X == T.getText()) { \ PlatformLocs.push_back(T.getLoc()); \ continue; \ } #include "swift/AST/PlatformKinds.def" } unsigned I = 0; for (; I < TokenNodes.size(); ++ I) { auto Node = TokenNodes[I]; if (SM.isBeforeInBuffer(D->getRange().End, Node.Range.getStart())) break; if (Node.Range.contains(D->AtLoc)) { if (!passNode({SyntaxNodeKind::AttributeBuiltin, Node.Range})) break; continue; } if (PlatformLocs.end() != std::find(PlatformLocs.begin(), PlatformLocs.end(), Node.Range.getStart())) { if (!passNode({SyntaxNodeKind::Keyword, Node.Range})) break; continue; } if (!passNode(Node)) break; } TokenNodes = TokenNodes.slice(I); return true; } return false; } bool ModelASTWalker::handleAttrs(const DeclAttributes &Attrs) { SmallVector DeclRanges; for (auto At : Attrs) { if (At->getRangeWithAt().isValid()) DeclRanges.push_back(std::make_pair(At, At->getRangeWithAt())); } return handleAttrRanges(DeclRanges); } bool ModelASTWalker::handleAttrs(const TypeAttributes &Attrs) { SmallVector Ranges; Attrs.getAttrRanges(Ranges); SmallVector DeclRanges; for (auto R : Ranges) { DeclRanges.push_back(std::make_pair(nullptr, R)); } return handleAttrRanges(DeclRanges); } bool ModelASTWalker::handleAttrRanges(ArrayRef DeclRanges) { if (DeclRanges.empty()) return true; SmallVector SortedRanges(DeclRanges.begin(), DeclRanges.end()); std::sort(SortedRanges.begin(), SortedRanges.end(), [&](DeclAttributeAndRange LHS, DeclAttributeAndRange RHS) { return SM.isBeforeInBuffer(LHS.second.Start, RHS.second.End); }); DeclRanges = SortedRanges; SourceLoc BeginLoc = DeclRanges.front().second.Start; std::vector Toks = swift::tokenize( LangOpts, SM, BufferID, SM.getLocOffsetInBuffer(BeginLoc, BufferID), SM.getLocOffsetInBuffer(DeclRanges.back().second.End, BufferID), /*KeepComments=*/true, /*TokenizeInterpolatedString=*/false); auto passAttrNode = [&](SourceRange AttrRange) -> bool { SourceRange Range = AttrRange; if (!passNonTokenNode({SyntaxNodeKind::AttributeBuiltin, charSourceRangeFromSourceRange(SM, Range)})) return false; while (!TokenNodes.empty() && SM.rangeContainsTokenLoc(AttrRange, TokenNodes.front().Range.getStart())) TokenNodes = TokenNodes.slice(1); return true; }; for (auto Tok : Toks) { if (DeclRanges.empty()) break; if (Tok.getLoc() == DeclRanges.front().second.Start) { auto R = DeclRanges.front().second; auto D = DeclRanges.front().first; DeclRanges = DeclRanges.slice(1); if (!handleSpecialDeclAttribute(D, Toks)) { if (!passAttrNode(R)) return false; } } } if (!DeclRanges.empty() && !handleSpecialDeclAttribute(DeclRanges.front().first, Toks)) { if (!passAttrNode(DeclRanges.front().second)) return false; } return true; } bool ModelASTWalker::shouldPassBraceStructureNode(BraceStmt *S) { return (!dyn_cast_or_null(Parent.getAsDecl()) && !dyn_cast_or_null(Parent.getAsDecl()) && !dyn_cast_or_null(Parent.getAsStmt()) && S->getSourceRange().isValid()); } bool ModelASTWalker::passTokenNodesUntil(SourceLoc Loc, PassNodesBehavior Behavior) { assert(Loc.isValid()); unsigned I = 0; for (unsigned E = TokenNodes.size(); I != E; ++I) { SourceLoc TokLoc = TokenNodes[I].Range.getStart(); if (SM.isBeforeInBuffer(Loc, TokLoc)) { break; } if (TokLoc == Loc && Behavior != IncludeNodeAtLocation) { if (Behavior == DisplaceNodeAtLocation) { // Skip past the node directly at the specified location, allowing the // caller to effectively replace it. ++I; } break; } if (!passNode(TokenNodes[I])) return false; } TokenNodes = TokenNodes.slice(I); return true; } bool ModelASTWalker::passNonTokenNode(const SyntaxNode &Node) { // Skip out of order non-token nodes. // Ideally this shouldn't happen, but the AST can contain overlapping nodes, // such as multiple PatternBindingDecl in code like: var a, b : Int. Which // would cause us to report the TypeRepr twice. if (!SM.isBeforeInBuffer(LastLoc, Node.Range.getStart())) return false; if (!passTokenNodesUntil(Node.Range.getStart(), DisplaceNodeAtLocation)) return false; if (!passNode(Node)) return false; return true; } bool ModelASTWalker::passNode(const SyntaxNode &Node) { assert(!SM.isBeforeInBuffer(Node.Range.getStart(), LastLoc)); LastLoc = Node.Range.getStart(); bool ShouldWalkSubTree = Walker.walkToNodePre(Node); if (ShouldWalkSubTree) { if (Node.isComment()) { if (!processComment(Node.Range)) return false; } else if (Node.Kind == SyntaxNodeKind::DocCommentLine) { if (!findFieldsInDocCommentLine(Node)) return false; } else if (Node.Kind == SyntaxNodeKind::DocCommentBlock) { if (!findFieldsInDocCommentBlock(Node)) return false; } else if (Node.Kind == SyntaxNodeKind::CommentMarker) { if (!searchForURL(Node.Range)) return false; } } if (!Walker.walkToNodePost(Node)) return false; return true; } bool ModelASTWalker::pushStructureNode(const SyntaxStructureNode &Node, const ASTNodeType& ASTNode) { SubStructureStack.emplace_back(Node, ASTNode); if (!passTokenNodesUntil(Node.Range.getStart(), ExcludeNodeAtLocation)) return false; if (!Walker.walkToSubStructurePre(Node)) return false; return true; } bool ModelASTWalker::popStructureNode() { assert(!SubStructureStack.empty()); SyntaxStructureNode Node = SubStructureStack.back().StructureNode; SubStructureStack.pop_back(); // VarDecls are popped before we see their TypeRepr, so if we pass the token // nodes now they will not change from identifier to a type-identifier. if (!Node.isVariable()) { if (!passTokenNodesUntil(Node.Range.getEnd(), IncludeNodeAtLocation)) return false; } if (!Walker.walkToSubStructurePost(Node)) return false; return true; } bool ModelASTWalker::isCurrentCallArgExpr(const Expr *E) { if (SubStructureStack.empty()) return false; auto Current = SubStructureStack.back(); if (Current.StructureNode.Kind == SyntaxStructureKind::ObjectLiteralExpression && cast(Current.ASTNode.getAsExpr())->getArg() == E) return true; return Current.StructureNode.Kind == SyntaxStructureKind::CallExpression && cast(Current.ASTNode.getAsExpr())->getArg() == E; } bool ModelASTWalker::processComment(CharSourceRange Range) { StringRef Text = SM.extractText(Range, BufferID); SourceLoc Loc = Range.getStart(); // Search for 'FIXME:' or 'TODO:'. while (1) { auto Pos = Text.find_first_of("FTM"); if (Pos == StringRef::npos) return searchForURL(Range); Text = Text.substr(Pos); Loc = Loc.getAdvancedLoc(Pos); if (Text.startswith("FIXME:") || Text.startswith("TODO:") || Text.startswith("MARK:")) break; Text = Text.substr(1); Loc = Loc.getAdvancedLoc(1); } auto NewLinePos = Text.find_first_of("\r\n"); if (NewLinePos != StringRef::npos) { Text = Text.substr(0, NewLinePos); } CharSourceRange BeforeMarker{ SM, Range.getStart(), Loc }; CharSourceRange Marker(Loc, Text.size()); CharSourceRange AfterMarker{ SM, Marker.getEnd(), Range.getEnd() }; if (!searchForURL(BeforeMarker)) return false; SyntaxNode Node{ SyntaxNodeKind::CommentMarker, Marker }; if (!passNode(Node)) return false; return searchForURL(AfterMarker); } bool ModelASTWalker::findUrlStartingLoc(StringRef Text, unsigned &Start, std::regex &Regex) { #ifdef SWIFT_HAVE_WORKING_STD_REGEX static const auto MailToPosition = std::find(URLProtocols.begin(), URLProtocols.end(), "mailto"); static const auto RadarPosition = std::find(URLProtocols.begin(), URLProtocols.end(), "radar"); auto Index = Text.find(":"); if (Index == StringRef::npos) return false; for (auto It = URLProtocols.begin(); It != URLProtocols.end(); ++ It) { if (Index >= It->size() && Text.substr(Index - It->size(), It->size()) == *It) { Start = Index - It->size(); if (It < MailToPosition) Regex = getURLRegex(0); else if (It < RadarPosition) Regex = getURLRegex(1); else Regex = getURLRegex(2); return true; } } #endif return false; } static CharSourceRange sanitizeUnpairedParenthesis(CharSourceRange Range) { auto Text = Range.str(); if (Text.back() != ')') { return Range; } unsigned Pairs = 0; unsigned TrimLen = 0; for (char C : Text) { if (C == '(') { Pairs ++; } else if (C == ')') { if (Pairs == 0) TrimLen ++; else Pairs --; } else { TrimLen = 0; } } return CharSourceRange(Range.getStart(), Text.size() - TrimLen); } bool ModelASTWalker::searchForURL(CharSourceRange Range) { StringRef OrigText = SM.extractText(Range, BufferID); SourceLoc OrigLoc = Range.getStart(); StringRef Text = OrigText; while (1) { std::match_results Matches; std::regex Regex; unsigned Start; if (findUrlStartingLoc(Text, Start, Regex) && std::regex_search(Text.substr(Start).begin(), Text.substr(Start).end(), Matches, Regex)) { auto &RxMatch = Matches[0]; StringRef Match(RxMatch.first, RxMatch.second - RxMatch.first); SourceLoc Loc = OrigLoc.getAdvancedLoc(Match.data() - OrigText.data()); CharSourceRange Range(Loc, Match.size()); SyntaxNode Node{ SyntaxNodeKind::CommentURL, sanitizeUnpairedParenthesis(Range) }; if (!passNode(Node)) return false; Text = Text.substr(Match.data() - Text.data() + Match.size()); } else { auto Index = Text.find(':'); if (Index == StringRef::npos) break; Text = Text.substr(Index + 1); } } return true; } Optional ModelASTWalker::parseFieldNode(StringRef Text, StringRef OrigText, SourceLoc OrigLoc) { Optional Node; #ifdef SWIFT_HAVE_WORKING_STD_REGEX std::match_results Matches; for (unsigned i = 0; i != 3; ++i) { auto &Rx = getDocCommentRegex(i); bool HadMatch = std::regex_search(Text.begin(), Text.end(), Matches, Rx); if (HadMatch) break; } if (Matches.empty()) return None; auto &Match = Matches[1]; StringRef MatchStr(Match.first, Match.second - Match.first); auto Loc = OrigLoc.getAdvancedLoc(MatchStr.data() - OrigText.data()); CharSourceRange Range(Loc, MatchStr.size()); Node = Optional({ SyntaxNodeKind::DocCommentField, Range }); #endif return Node; } bool ModelASTWalker::findFieldsInDocCommentLine(SyntaxNode Node) { auto OrigText = SM.extractText(Node.Range, BufferID); auto OrigLoc = Node.Range.getStart(); auto Text = OrigText.drop_front(3); // Drop "///" if (Text.empty()) return true; auto FieldNode = parseFieldNode(Text, OrigText, OrigLoc); if (FieldNode.hasValue()) passNode(FieldNode.getValue()); else searchForURL(Node.Range); return true; } bool ModelASTWalker::findFieldsInDocCommentBlock(SyntaxNode Node) { auto OrigText = SM.extractText(Node.Range, BufferID); auto OrigLoc = Node.Range.getStart(); if (!OrigText.startswith("/**") && !(LangOpts.Playground && OrigText.startswith("/*:"))) return true; auto Text = OrigText.drop_front(3); // Drop "^/**" or "/*:" if (!Text.endswith("*/")) return true; Text = Text.drop_back(2); // Drop "*/" if (Text.empty()) return true; auto FirstNewLine = Text.find('\n'); if (FirstNewLine == StringRef::npos) return true; Text = Text.substr(FirstNewLine + 1); if (Text.empty()) return true; size_t Indent = Text.ltrim().data() - Text.data(); SmallVector Lines; Text.split(Lines, "\n"); for (auto Line : Lines) { Line = Line.rtrim(); if (Line.size() < Indent) continue; auto FieldNode = parseFieldNode(Line.drop_front(Indent), OrigText, OrigLoc); if (FieldNode.hasValue()) passNode(FieldNode.getValue()); else searchForURL(CharSourceRange(Node.Range.getStart(). getAdvancedLoc(Line.data() - OrigText.data()), Line.size())); } std::match_results Matches; return true; }