//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/NameLookup.h" #include "swift/Basic/SourceManager.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CommentConversion.h" #include "swift/IDE/Utils.h" #include "swift/Markup/XMLUtils.h" #include "swift/Subsystems.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/Module.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Lexer.h" #include "clang/Basic/CharInfo.h" #include "llvm/Support/MemoryBuffer.h" #include using namespace swift; using namespace swift::ide; Optional> swift::ide::parseLineCol(StringRef LineCol) { unsigned Line, Col; size_t ColonIdx = LineCol.find(':'); if (ColonIdx == StringRef::npos) { llvm::errs() << "wrong pos format, it should be ':'\n"; return None; } if (LineCol.substr(0, ColonIdx).getAsInteger(10, Line)) { llvm::errs() << "wrong pos format, it should be ':'\n"; return None; } if (LineCol.substr(ColonIdx+1).getAsInteger(10, Col)) { llvm::errs() << "wrong pos format, it should be ':'\n"; return None; } if (Line == 0 || Col == 0) { llvm::errs() << "wrong pos format, line/col should start from 1\n"; return None; } return std::make_pair(Line, Col); } void XMLEscapingPrinter::printText(StringRef Text) { swift::markup::appendWithXMLEscaping(OS, Text); } void XMLEscapingPrinter::printXML(StringRef Text) { OS << Text; } SourceManager &CursorInfoResolver::getSourceMgr() const { return SrcFile.getASTContext().SourceMgr; } bool CursorInfoResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, SourceLoc Loc, bool IsRef, Type Ty) { if (!D->hasName()) return false; if (Loc == LocToResolve) { CursorInfo = { D, CtorTyRef, ExtTyRef, &SrcFile, Loc, IsRef, Ty, ContainerType }; return true; } return false; } bool CursorInfoResolver::tryResolve(ModuleEntity Mod, SourceLoc Loc) { if (Loc == LocToResolve) { CursorInfo = { Mod, &SrcFile, Loc }; return true; } return false; } bool CursorInfoResolver::tryResolve(Stmt *St) { if (auto *LST = dyn_cast(St)) { if (LST->getStartLoc() == LocToResolve) { CursorInfo = { St, &SrcFile }; return true; } } if (auto *CS = dyn_cast(St)) { if (CS->getStartLoc() == LocToResolve) { CursorInfo = { St, &SrcFile }; return true; } } return false; } bool CursorInfoResolver::visitSubscriptReference(ValueDecl *D, CharSourceRange Range, bool IsOpenBracket) { // We should treat both open and close brackets equally return visitDeclReference(D, Range, nullptr, nullptr, Type(), ReferenceMetaData(SemaReferenceKind::SubscriptRef, None)); } ResolvedCursorInfo CursorInfoResolver::resolve(SourceLoc Loc) { assert(Loc.isValid()); LocToResolve = Loc; CursorInfo = ResolvedCursorInfo(); walk(SrcFile); return CursorInfo; } bool CursorInfoResolver::walkToDeclPre(Decl *D, CharSourceRange Range) { if (!rangeContainsLoc(D->getSourceRange())) return false; if (isa(D)) return true; if (auto *VD = dyn_cast(D)) return !tryResolve(VD, /*CtorTyRef=*/nullptr, /*ExtTyRef=*/nullptr, Range.getStart(), /*IsRef=*/false); return true; } bool CursorInfoResolver::walkToDeclPost(Decl *D) { if (isDone()) return false; if (getSourceMgr().isBeforeInBuffer(LocToResolve, D->getStartLoc())) return false; return true; } bool CursorInfoResolver::walkToStmtPre(Stmt *S) { // FIXME: Even implicit Stmts should have proper ranges that include any // non-implicit Stmts (fix Stmts created for lazy vars). if (!S->isImplicit() && !rangeContainsLoc(S->getSourceRange())) return false; return !tryResolve(S); } bool CursorInfoResolver::walkToStmtPost(Stmt *S) { if (isDone()) return false; // FIXME: Even implicit Stmts should have proper ranges that include any // non-implicit Stmts (fix Stmts created for lazy vars). if (!S->isImplicit() && getSourceMgr().isBeforeInBuffer(LocToResolve, S->getStartLoc())) return false; return true; } bool CursorInfoResolver::visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T, ReferenceMetaData Data) { if (isDone()) return false; return !tryResolve(D, CtorTyRef, ExtTyRef, Range.getStart(), /*IsRef=*/true, T); } bool CursorInfoResolver::walkToExprPre(Expr *E) { if (!isDone()) { if (auto SAE = dyn_cast(E)) { if (SAE->getFn()->getStartLoc() == LocToResolve) { ContainerType = SAE->getBase()->getType(); } } else if (auto ME = dyn_cast(E)) { SourceLoc DotLoc = ME->getDotLoc(); if (DotLoc.isValid() && DotLoc.getAdvancedLoc(1) == LocToResolve) { ContainerType = ME->getBase()->getType(); } } auto IsProperCursorLocation = E->getStartLoc() == LocToResolve; // Handle cursor placement after `try` in ForceTry and OptionalTry Expr. auto CheckLocation = [&IsProperCursorLocation, this](SourceLoc Loc) { IsProperCursorLocation = Loc == LocToResolve || IsProperCursorLocation; }; if (auto *FTE = dyn_cast(E)) { CheckLocation(FTE->getExclaimLoc()); } if (auto *OTE = dyn_cast(E)) { CheckLocation(OTE->getQuestionLoc()); } // Keep track of trailing expressions. if (!E->isImplicit() && IsProperCursorLocation) TrailingExprStack.push_back(E); } return true; } bool CursorInfoResolver::walkToExprPost(Expr *E) { if (isDone()) return false; if (!TrailingExprStack.empty() && TrailingExprStack.back() == E) { // We return the outtermost expression in the token info. CursorInfo = { TrailingExprStack.front(), &SrcFile }; return false; } return true; } bool CursorInfoResolver::visitCallArgName(Identifier Name, CharSourceRange Range, ValueDecl *D) { if (isDone()) return false; bool Found = tryResolve(D, nullptr, nullptr, Range.getStart(), /*IsRef=*/true); if (Found) CursorInfo.IsKeywordArgument = true; return !Found; } bool CursorInfoResolver:: visitDeclarationArgumentName(Identifier Name, SourceLoc StartLoc, ValueDecl *D) { if (isDone()) return false; return !tryResolve(D, nullptr, nullptr, StartLoc, /*IsRef=*/false); } bool CursorInfoResolver::visitModuleReference(ModuleEntity Mod, CharSourceRange Range) { if (isDone()) return false; if (Mod.isBuiltinModule()) return true; // Ignore. return !tryResolve(Mod, Range.getStart()); } SourceManager &NameMatcher::getSourceMgr() const { return SrcFile.getASTContext().SourceMgr; } std::vector NameMatcher::resolve(ArrayRef Locs, ArrayRef Tokens) { // Note the original indices and sort them in reverse source order std::vector MapToOriginalIndex(Locs.size()); std::iota(MapToOriginalIndex.begin(), MapToOriginalIndex.end(), 0); std::sort(MapToOriginalIndex.begin(), MapToOriginalIndex.end(), [this, Locs](size_t first, size_t second) { return first != second && !getSourceMgr() .isBeforeInBuffer(Locs[first].Loc, Locs[second].Loc); }); // Add the locs themselves LocsToResolve.clear(); std::transform(MapToOriginalIndex.begin(), MapToOriginalIndex.end(), std::back_inserter(LocsToResolve), [&](size_t index){ return Locs[index]; }); InactiveConfigRegionNestings = 0; SelectorNestings = 0; TokensToCheck = Tokens; ResolvedLocs.clear(); SrcFile.walk(*this); checkComments(); // handle any unresolved locs past the end of the last AST node or comment std::vector Remaining(Locs.size() - ResolvedLocs.size(), { ASTWalker::ParentTy(), CharSourceRange(), {}, LabelRangeType::None, /*isActice*/true, /*isInSelector*/false}); ResolvedLocs.insert(ResolvedLocs.end(), Remaining.begin(), Remaining.end()); // return in the original order std::vector Ordered(ResolvedLocs.size()); for(size_t Index = 0; Index < ResolvedLocs.size(); ++Index) { size_t Flipped = ResolvedLocs.size() - 1 - Index; Ordered[MapToOriginalIndex[Flipped]] = ResolvedLocs[Index]; } return Ordered; } static std::vector getLabelRanges(const ParameterList* List, const SourceManager &SM) { std::vector LabelRanges; for (ParamDecl *Param: *List) { if (Param->isImplicit()) continue; SourceLoc NameLoc = Param->getArgumentNameLoc(); SourceLoc ParamLoc = Param->getNameLoc(); size_t NameLength; if (NameLoc.isValid()) { LabelRanges.push_back(Lexer::getCharSourceRangeFromSourceRange(SM, SourceRange(NameLoc, ParamLoc))); } else { NameLoc = ParamLoc; NameLength = Param->getNameStr().size(); LabelRanges.push_back(CharSourceRange(NameLoc, NameLength)); } } return LabelRanges; } bool NameMatcher::walkToDeclPre(Decl *D) { // Handle occurrences in any preceding doc comments RawComment R = D->getRawComment(); if (!R.isEmpty()) { for(SingleRawComment C: R.Comments) { while(!shouldSkip(C.Range)) tryResolve(ASTWalker::ParentTy(), nextLoc()); } } if (shouldSkip(D->getSourceRange())) return false; if (auto *ICD = dyn_cast(D)) { for (auto Clause : ICD->getClauses()) { if (!Clause.isActive) ++InactiveConfigRegionNestings; for (auto Member : Clause.Elements) { Member.walk(*this); } if (!Clause.isActive) { assert(InactiveConfigRegionNestings > 0); --InactiveConfigRegionNestings; } } return false; } else if (AbstractFunctionDecl *AFD = dyn_cast(D)) { std::vector LabelRanges; if (AFD->getNameLoc() == nextLoc()) { for(auto ParamList: AFD->getParameterLists()) { LabelRanges = getLabelRanges(ParamList, getSourceMgr()); if (LabelRanges.size() == ParamList->size()) break; } } tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::Param, LabelRanges); } else if (SubscriptDecl *SD = dyn_cast(D)) { tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::Param, getLabelRanges(SD->getIndices(), getSourceMgr())); } else if (EnumElementDecl *EED = dyn_cast(D)) { if (TupleTypeRepr *TTR = dyn_cast_or_null(EED->getArgumentTypeLoc().getTypeRepr())) { size_t ElemIndex = 0; std::vector LabelRanges; for(const TupleTypeReprElement &Elem: TTR->getElements()) { SourceLoc LabelStart(Elem.Type->getStartLoc()); SourceLoc LabelEnd(LabelStart); auto NameIdentifier = TTR->getElementName(ElemIndex); if (!NameIdentifier.empty()) { LabelStart = TTR->getElementNameLoc(ElemIndex); } LabelRanges.push_back(CharSourceRange(getSourceMgr(), LabelStart, LabelEnd)); ++ElemIndex; } tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::CallArg, LabelRanges); } else { tryResolve(ASTWalker::ParentTy(D), D->getLoc()); } } else if (ImportDecl *ID = dyn_cast(D)) { for(const ImportDecl::AccessPathElement &Element: ID->getFullAccessPath()) { tryResolve(ASTWalker::ParentTy(D), Element.second); if (isDone()) break; } } else if (isa(D) || isa(D) || isa(D)) { tryResolve(ASTWalker::ParentTy(D), D->getLoc()); } return !isDone(); } bool NameMatcher::walkToDeclPost(Decl *D) { return !isDone(); } std::pair NameMatcher::walkToStmtPre(Stmt *S) { // FIXME: Even implicit Stmts should have proper ranges that include any // non-implicit Stmts (fix Stmts created for lazy vars). if (!S->isImplicit() && shouldSkip(S->getSourceRange())) return std::make_pair(false, isDone()? nullptr : S); return std::make_pair(true, S); } Stmt *NameMatcher::walkToStmtPost(Stmt *S) { if (isDone()) return nullptr; return S; } std::pair NameMatcher::walkToExprPre(Expr *E) { if (shouldSkip(E)) return std::make_pair(false, isDone()? nullptr : E); if (isa(E)) { ++SelectorNestings; } // only match name locations of expressions apparent in the original source if (!E->isImplicit()) { // Try to resolve against the below kinds *before* their children are // visited to ensure visitation happens in source order. switch (E->getKind()) { case ExprKind::UnresolvedMember: { auto UME = cast(E); tryResolve(ASTWalker::ParentTy(E), UME->getNameLoc(), UME->getArgument(), !UME->getArgument()); } break; case ExprKind::DeclRef: { auto DRE = cast(E); tryResolve(ASTWalker::ParentTy(E), DRE->getNameLoc(), nullptr, true); break; } case ExprKind::UnresolvedDeclRef: { auto UDRE = cast(E); tryResolve(ASTWalker::ParentTy(E), UDRE->getNameLoc(), nullptr, true); break; } case ExprKind::StringLiteral: // Handle multple locations in a single string literal do { tryResolve(ASTWalker::ParentTy(E), nextLoc()); } while (!shouldSkip(E)); break; case ExprKind::Tuple: { TupleExpr *T = cast(E); // Handle arg label locations (the index reports property occurrences // on them for memberwise inits) for (unsigned i = 0, e = T->getNumElements(); i != e; ++i) { auto Name = T->getElementName(i); if (!Name.empty()) { tryResolve(ASTWalker::ParentTy(E), T->getElementNameLoc(i)); if (isDone()) break; } if (auto *Elem = T->getElement(i)) { if (!Elem->walk(*this)) return {false, nullptr}; } } // We already visited the children. if (!walkToExprPost(E)) return {false, nullptr}; return {false, E}; } case ExprKind::Binary: { BinaryExpr *BinE = cast(E); // Visit in source order. if (!BinE->getArg()->getElement(0)->walk(*this)) return {false, nullptr}; if (!BinE->getFn()->walk(*this)) return {false, nullptr}; if (!BinE->getArg()->getElement(1)->walk(*this)) return {false, nullptr}; // We already visited the children. if (!walkToExprPost(E)) return {false, nullptr}; return {false, E}; } default: // ignored break; } } return std::make_pair(!isDone(), isDone()? nullptr : E); } Expr *NameMatcher::walkToExprPost(Expr *E) { if (isDone()) return nullptr; if (!E->isImplicit()) { // Try to resolve against the below kinds *after* their children have been // visited to ensure visitation happens in source order. switch (E->getKind()) { case ExprKind::MemberRef: tryResolve(ASTWalker::ParentTy(E), E->getLoc()); break; case ExprKind::UnresolvedDot: { auto UDE = cast(E); tryResolve(ASTWalker::ParentTy(E), UDE->getNameLoc(), nullptr, true); break; } default: break; } } if (isa(E)) { assert(SelectorNestings > 0); --SelectorNestings; } return E; } bool NameMatcher::walkToTypeLocPre(TypeLoc &TL) { if (isDone() || shouldSkip(TL.getSourceRange())) return false; return true; } bool NameMatcher::walkToTypeLocPost(TypeLoc &TL) { return !isDone(); } bool NameMatcher::walkToTypeReprPre(TypeRepr *T) { if (isDone() || shouldSkip(T->getSourceRange())) return false; if (isa(T)) tryResolve(ASTWalker::ParentTy(T), T->getLoc()); return !isDone(); } bool NameMatcher::walkToTypeReprPost(TypeRepr *T) { return !isDone(); } std::pair NameMatcher::walkToPatternPre(Pattern *P) { if (isDone() || shouldSkip(P->getSourceRange())) return std::make_pair(false, P); tryResolve(ASTWalker::ParentTy(P), P->getLoc()); return std::make_pair(!isDone(), P); } bool NameMatcher::checkComments() { if (isDone()) return false; TokensToCheck = TokensToCheck.drop_while([this](const Token &tok) -> bool { return getSourceMgr().isBeforeInBuffer(tok.getRange().getEnd(), nextLoc()); }); if (TokensToCheck.empty()) return false; const Token &next = TokensToCheck.front(); if (next.is(swift::tok::comment) && next.getRange().contains(nextLoc()) && !next.getText().startswith("///")) return tryResolve(ASTWalker::ParentTy(), nextLoc()); return false; } void NameMatcher::skipLocsBefore(SourceLoc Start) { while (!isDone() && getSourceMgr().isBeforeInBuffer(nextLoc(), Start)) { if (!checkComments()) { LocsToResolve.pop_back(); ResolvedLocs.push_back({ASTWalker::ParentTy(), CharSourceRange(), {}, LabelRangeType::None, isActive(), isInSelector()}); } } } bool NameMatcher::shouldSkip(Expr *E) { if (!isa(E) || !Parent.getAsExpr() || !isa(Parent.getAsExpr())) return shouldSkip(E->getSourceRange()); // The lexer treats interpolated strings as a single token when computing the // CharSourceRange, so when we try to get the CharSourceRange of its first // child StringLiteralExpr (at the same SourceLoc) it goes beyond any // interpolated values. Use the StartLoc of the next sibling to bound it. StringLiteralExpr *SL = cast(E); InterpolatedStringLiteralExpr *ISL = cast(Parent.getAsExpr()); SourceLoc Start = SL->getStartLoc(); ArrayRef Segments = ISL->getSegments(); Segments = Segments.drop_until([&](Expr *Item){ return Item == SL; }) .drop_front(); CharSourceRange Range; if (Segments.empty()) { Range = Lexer::getCharSourceRangeFromSourceRange(getSourceMgr(), SourceRange(Start)); } else { SourceLoc NextSiblingLoc = Segments.front()->getStartLoc(); unsigned Length = getSourceMgr().getByteDistance(Start, NextSiblingLoc); Range = CharSourceRange(Start, Length); } return shouldSkip(Range); } bool NameMatcher::shouldSkip(SourceRange Range) { return shouldSkip(Lexer::getCharSourceRangeFromSourceRange(getSourceMgr(), Range)); } bool NameMatcher::shouldSkip(CharSourceRange Range) { if (isDone()) return true; if (Range.isInvalid()) return false; skipLocsBefore(Range.getStart()); return isDone() || !Range.contains(nextLoc()); } SourceLoc NameMatcher::nextLoc() const { assert(!LocsToResolve.empty()); return LocsToResolve.back().Loc; } std::vector getSelectorLabelRanges(SourceManager &SM, DeclNameLoc NameLoc) { SourceLoc Loc; std::vector Ranges; size_t index = 0; while((Loc = NameLoc.getArgumentLabelLoc(index++)).isValid()) { CharSourceRange Range = Lexer::getCharSourceRangeFromSourceRange(SM, SourceRange(Loc)); Ranges.push_back(Range); } return Ranges; } bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, Expr *Arg, bool checkParentForLabels) { if (NameLoc.isInvalid()) return false; if (NameLoc.isCompound()) { auto Labels = getSelectorLabelRanges(getSourceMgr(), NameLoc); bool Resolved = tryResolve(Node, NameLoc.getBaseNameLoc(), LabelRangeType::Selector, Labels); if (!isDone()) { for (auto Label: Labels) { if (tryResolve(Node, Label.getStart())) { Resolved = true; if (isDone()) break; } } } return Resolved; } if (LocsToResolve.back().ResolveArgLocs) { if (Arg) return tryResolve(Node, NameLoc.getBaseNameLoc(), LabelRangeType::CallArg, getCallArgLabelRanges(getSourceMgr(), Arg, LabelRangeEndAt::BeforeElemStart)); if (checkParentForLabels) { if (auto P = dyn_cast_or_null(Parent.getAsExpr())) { if (P->getFn() == Node.getAsExpr()) return tryResolve(Node, NameLoc.getBaseNameLoc(), LabelRangeType::CallArg, getCallArgLabelRanges(getSourceMgr(), P->getArg(), LabelRangeEndAt::BeforeElemStart)); } } } return tryResolve(Node, NameLoc.getBaseNameLoc()); } bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc) { assert(!isDone()); return tryResolve(Node, NameLoc, LabelRangeType::None, None); } bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc, LabelRangeType RangeType, ArrayRef LabelRanges) { skipLocsBefore(NameLoc); if (isDone()) return false; CharSourceRange Range = Lexer::getCharSourceRangeFromSourceRange(getSourceMgr(), NameLoc); UnresolvedLoc &Next = LocsToResolve.back(); if (Range.isValid() && NameLoc == Next.Loc) { LocsToResolve.pop_back(); ResolvedLocs.push_back({Node, Range, LabelRanges, RangeType, isActive(), isInSelector()}); return true; } return false; }; void ResolvedRangeInfo::print(llvm::raw_ostream &OS) { OS << ""; switch (Kind) { case RangeKind::SingleExpression: OS << "SingleExpression"; break; case RangeKind::SingleDecl: OS << "SingleDecl"; break; case RangeKind::MultiStatement: OS << "MultiStatement"; break; case RangeKind::PartOfExpression: OS << "PartOfExpression"; break; case RangeKind::SingleStatement: OS << "SingleStatement"; break; case RangeKind::Invalid: OS << "Invalid"; break; } OS << "\n"; OS << "" << ContentRange.str() << "\n"; if (auto Ty = getType()) { OS << ""; Ty->print(OS); OS << ""; switch(exit()) { case ExitState::Positive: OS << "true"; break; case ExitState::Unsure: OS << "unsure"; break; case ExitState::Negative: OS << "false"; break; } OS << "\n"; } if (RangeContext) { OS << ""; printContext(OS, RangeContext); OS << "\n"; } if (CommonExprParent) { OS << ""; OS << Expr::getKindName(CommonExprParent->getKind()); OS << "\n"; } if (!HasSingleEntry) { OS << "Multi\n"; } if (ThrowingUnhandledError) { OS << "Throwing\n"; } if (Orphan != OrphanKind::None) { OS << ""; switch (Orphan) { case OrphanKind::Continue: OS << "Continue"; break; case OrphanKind::Break: OS << "Break"; break; case OrphanKind::None: llvm_unreachable("cannot enter here."); } OS << ""; } for (auto &VD : DeclaredDecls) { OS << "" << VD.VD->getBaseName() << ""; OS << ""; if (VD.ReferredAfterRange) OS << "true"; else OS << "false"; OS << "\n"; } for (auto &RD : ReferencedDecls) { OS << "" << RD.VD->getBaseName() << ""; OS << ""; RD.Ty->print(OS); OS << "\n"; } OS << "" << ContainedNodes.size() << "\n"; OS << "\n"; } CharSourceRange ResolvedRangeInfo:: calculateContentRange(ArrayRef Tokens) { if (Tokens.empty()) return CharSourceRange(); auto StartTok = Tokens.front(); auto EndTok = Tokens.back(); auto StartLoc = StartTok.hasComment() ? StartTok.getCommentStart() : StartTok.getLoc(); auto EndLoc = EndTok.getRange().getEnd(); auto Length = static_cast(EndLoc.getOpaquePointerValue()) - static_cast(StartLoc.getOpaquePointerValue()); return CharSourceRange(StartLoc, Length); } bool DeclaredDecl::operator==(const DeclaredDecl& Other) { return VD == Other.VD; } static bool hasUnhandledError(ArrayRef Nodes) { class ThrowingEntityAnalyzer : public SourceEntityWalker { bool Throwing; public: ThrowingEntityAnalyzer(): Throwing(false) {} bool walkToStmtPre(Stmt *S) override { if (auto DCS = dyn_cast(S)) { if (DCS->isSyntacticallyExhaustive()) return false; Throwing = true; } else if (isa(S)) { Throwing = true; } return !Throwing; } bool walkToExprPre(Expr *E) override { if (isa(E)) { Throwing = true; } return !Throwing; } bool walkToDeclPre(Decl *D, CharSourceRange Range) override { return false; } bool walkToDeclPost(Decl *D) override { return !Throwing; } bool walkToStmtPost(Stmt *S) override { return !Throwing; } bool walkToExprPost(Expr *E) override { return !Throwing; } bool isThrowing() { return Throwing; } }; return Nodes.end() != std::find_if(Nodes.begin(), Nodes.end(), [](ASTNode N) { ThrowingEntityAnalyzer Analyzer; N.walk(Analyzer); return Analyzer.isThrowing(); }); } ReturnInfo:: ReturnInfo(ASTContext &Ctx, ArrayRef Branches): ReturnType(Ctx.TheErrorType.getPointer()), Exit(ExitState::Unsure) { std::set AllTypes; std::set AllExitStates; for (auto I : Branches) { AllTypes.insert(I.ReturnType); AllExitStates.insert(I.Exit); } if (AllTypes.size() == 1) { ReturnType = *AllTypes.begin(); } if (AllExitStates.size() == 1) { Exit = *AllExitStates.begin(); } } struct RangeResolver::Implementation { SourceFile &File; ASTContext &Ctx; SourceManager &SM; private: enum class RangeMatchKind : int8_t { NoneMatch, StartMatch, EndMatch, RangeMatch, }; struct ContextInfo { ASTNode Parent; // Whether the context is entirely contained in the given range under // scrutiny. bool ContainedInRange; std::vector StartMatches; std::vector EndMatches; ContextInfo(ASTNode Parent, bool ContainedInRange) : Parent(Parent), ContainedInRange(ContainedInRange) {} bool isMultiStatement() { if (StartMatches.empty() || EndMatches.empty()) return false; // Multi-statement should have a common parent of brace statement, this // can be implicit brace statement, e.g. in case statement. if (Parent.isStmt(StmtKind::Brace)) return true; // Explicitly allow the selection of multiple case statements. auto IsCase = [](ASTNode N) { return N.isStmt(StmtKind::Case); }; return llvm::any_of(StartMatches, IsCase) && llvm::any_of(EndMatches, IsCase); } }; ArrayRef TokensInRange; SourceLoc Start; SourceLoc End; Optional Result; std::vector ContextStack; ContextInfo &getCurrentDC() { assert(!ContextStack.empty()); return ContextStack.back(); } std::vector DeclaredDecls; std::vector ReferencedDecls; // Keep track of the AST nodes contained in the range under question. std::vector ContainedASTNodes; /// Collect the type that an ASTNode should be evaluated to. ReturnInfo resolveNodeType(ASTNode N, RangeKind Kind) { auto *VoidTy = Ctx.getVoidDecl()->getDeclaredInterfaceType().getPointer(); if (N.isNull()) return {VoidTy, ExitState::Negative}; switch(Kind) { case RangeKind::Invalid: case RangeKind::SingleDecl: case RangeKind::PartOfExpression: llvm_unreachable("cannot get type."); // For a single expression, its type is apparent. case RangeKind::SingleExpression: return {N.get()->getType().getPointer(), ExitState::Negative}; // For statements, we either resolve to the returning type or Void. case RangeKind::SingleStatement: case RangeKind::MultiStatement: { if (N.is()) { if (auto RS = dyn_cast(N.get())) { return { resolveNodeType(RS->hasResult() ? RS->getResult() : nullptr, RangeKind::SingleExpression).ReturnType, ExitState::Positive }; } // Unbox the brace statement to find its type. if (auto BS = dyn_cast(N.get())) { if (!BS->getElements().empty()) { return resolveNodeType(BS->getElements().back(), RangeKind::SingleStatement); } } // Unbox the if statement to find its type. if (auto *IS = dyn_cast(N.get())) { llvm::SmallVector Branches; Branches.push_back(resolveNodeType(IS->getThenStmt(), RangeKind::SingleStatement)); Branches.push_back(resolveNodeType(IS->getElseStmt(), RangeKind::SingleStatement)); return {Ctx, Branches}; } // Unbox switch statement to find return information. if (auto *SWS = dyn_cast(N.get())) { llvm::SmallVector Branches; for (auto *CS : SWS->getCases()) { Branches.push_back(resolveNodeType(CS->getBody(), RangeKind::SingleStatement)); } return {Ctx, Branches}; } } // For other statements, the type should be void. return {VoidTy, ExitState::Negative}; } } } ResolvedRangeInfo getSingleNodeKind(ASTNode Node) { assert(!Node.isNull()); assert(ContainedASTNodes.size() == 1); // Single node implies single entry point, or is it? bool SingleEntry = true; bool UnhandledError = hasUnhandledError({Node}); OrphanKind Kind = getOrphanKind(ContainedASTNodes); if (Node.is()) return ResolvedRangeInfo(RangeKind::SingleExpression, resolveNodeType(Node, RangeKind::SingleExpression), TokensInRange, getImmediateContext(), /*Common Parent Expr*/nullptr, SingleEntry, UnhandledError, Kind, llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls)); else if (Node.is()) return ResolvedRangeInfo(RangeKind::SingleStatement, resolveNodeType(Node, RangeKind::SingleStatement), TokensInRange, getImmediateContext(), /*Common Parent Expr*/nullptr, SingleEntry, UnhandledError, Kind, llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls)); else { assert(Node.is()); return ResolvedRangeInfo(RangeKind::SingleDecl, ReturnInfo(), TokensInRange, getImmediateContext(), /*Common Parent Expr*/nullptr, SingleEntry, UnhandledError, Kind, llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls)); } } bool isContainedInSelection(CharSourceRange Range) { if (SM.isBeforeInBuffer(Range.getStart(), Start)) return false; if (SM.isBeforeInBuffer(End, Range.getEnd())) return false; return true; } DeclContext *getImmediateContext() { for (auto It = ContextStack.rbegin(); It != ContextStack.rend(); It ++) { if (auto *DC = It->Parent.getAsDeclContext()) return DC; } return static_cast(&File); } Implementation(SourceFile &File, ArrayRef TokensInRange) : File(File), Ctx(File.getASTContext()), SM(Ctx.SourceMgr), TokensInRange(TokensInRange), Start(TokensInRange.front().getLoc()), End(TokensInRange.back().getLoc()) { assert(Start.isValid() && End.isValid()); } public: bool hasResult() { return Result.hasValue(); } void enter(ASTNode Node) { bool ContainedInRange; if (!Node.getOpaqueValue()) { // If the node is the root, it's not contained for sure. ContainedInRange = false; } else if (ContextStack.back().ContainedInRange) { // If the node's parent is contained in the range, so is the node. ContainedInRange = true; } else { // If the node's parent is not contained in the range, check if this node is. ContainedInRange = isContainedInSelection(CharSourceRange(SM, Node.getStartLoc(), Node.getEndLoc())); } ContextStack.emplace_back(Node, ContainedInRange); } void leave(ASTNode Node) { if (!hasResult() && !Node.isImplicit() && nodeContainSelection(Node)) { if (auto Parent = Node.is() ? Node.get() : nullptr) { Result = { RangeKind::PartOfExpression, ReturnInfo(), TokensInRange, getImmediateContext(), Parent, hasSingleEntryPoint(ContainedASTNodes), hasUnhandledError(ContainedASTNodes), getOrphanKind(ContainedASTNodes), llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls) }; } } assert(ContextStack.back().Parent.getOpaqueValue() == Node.getOpaqueValue()); ContextStack.pop_back(); } static std::unique_ptr createInstance(SourceFile &File, unsigned StartOff, unsigned Length) { SourceManager &SM = File.getASTContext().SourceMgr; unsigned BufferId = File.getBufferID().getValue(); auto AllTokens = File.getAllTokens(); SourceLoc StartRaw = SM.getLocForOffset(BufferId, StartOff); SourceLoc EndRaw = SM.getLocForOffset(BufferId, StartOff + Length); // This points to the first token after or on the start loc. auto StartIt = token_lower_bound(AllTokens, StartRaw); // Skip all the comments. while(StartIt != AllTokens.end()) { if (StartIt->getKind() != tok::comment) break; StartIt ++; } // Erroneous case. if (StartIt == AllTokens.end()) return nullptr; // This points to the first token after or on the end loc; auto EndIt = token_lower_bound(AllTokens, EndRaw); // Adjust end token to skip comments. while (EndIt != AllTokens.begin()) { EndIt --; if (EndIt->getKind() != tok::comment) break; } // Erroneous case. if (EndIt < StartIt) return nullptr; unsigned StartIdx = StartIt - AllTokens.begin(); return std::unique_ptr(new Implementation(File, AllTokens.slice(StartIdx, EndIt - StartIt + 1))); } static std::unique_ptr createInstance(SourceFile &File, SourceLoc Start, SourceLoc End) { if (Start.isInvalid() || End.isInvalid()) return nullptr; SourceManager &SM = File.getASTContext().SourceMgr; unsigned BufferId = File.getBufferID().getValue(); unsigned StartOff = SM.getLocOffsetInBuffer(Start, BufferId); unsigned EndOff = SM.getLocOffsetInBuffer(End, BufferId); return createInstance(File, StartOff, EndOff - StartOff); } void analyzeDecl(Decl *D) { // Collect declared decls in the range. if (auto *VD = dyn_cast_or_null(D)) { if (isContainedInSelection(CharSourceRange(SM, VD->getStartLoc(), VD->getEndLoc()))) if (std::find(DeclaredDecls.begin(), DeclaredDecls.end(), DeclaredDecl(VD)) == DeclaredDecls.end()) DeclaredDecls.push_back(VD); } } class CompleteWalker : public SourceEntityWalker { Implementation *Impl; bool walkToDeclPre(Decl *D, CharSourceRange Range) override { if (D->isImplicit()) return false; Impl->analyzeDecl(D); return true; } bool visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T, ReferenceMetaData Data) override { Impl->analyzeDeclRef(D, Range.getStart(), T, Data); return true; } public: CompleteWalker(Implementation *Impl) : Impl(Impl) {} }; /// This walker walk the current decl context and analyze whether declared /// decls in the range is referenced after it. class FurtherReferenceWalker : public SourceEntityWalker { Implementation *Impl; bool visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T, ReferenceMetaData Data) override { // If the reference is after the given range, continue logic. if (!Impl->SM.isBeforeInBuffer(Impl->End, Range.getStart())) return true; // If the referenced decl is declared in the range, than the declared decl // is referenced out of scope/range. auto It = std::find(Impl->DeclaredDecls.begin(), Impl->DeclaredDecls.end(), D); if (It != Impl->DeclaredDecls.end()) { It->ReferredAfterRange = true; } return true; } public: FurtherReferenceWalker(Implementation *Impl) : Impl(Impl) {} }; void postAnalysis(ASTNode EndNode) { // Visit the content of this node thoroughly, because the walker may // abort early. EndNode.walk(CompleteWalker(this)); // Analyze whether declared decls in the range is referenced outside of it. FurtherReferenceWalker(this).walk(getImmediateContext()); } bool hasSingleEntryPoint(ArrayRef Nodes) { unsigned CaseCount = 0; // Count the number of case/default statements. for (auto N : Nodes) { if (Stmt *S = N.is() ? N.get() : nullptr) { if (S->getKind() == StmtKind::Case) CaseCount++; } } // If there are more than one case/default statements, there are more than // one entry point. return CaseCount == 0; } OrphanKind getOrphanKind(ArrayRef Nodes) { if (Nodes.empty()) return OrphanKind::None; // Prepare the entire range. SourceRange WholeRange(Nodes.front().getStartLoc(), Nodes.back().getEndLoc()); struct ControlFlowStmtSelector : public SourceEntityWalker { std::vector> Ranges; bool walkToStmtPre(Stmt *S) override { // For each continue/break statement, record its target's range and the // orphan kind. if (auto *CS = dyn_cast(S)) { if (auto *Target = CS->getTarget()) { Ranges.emplace_back(Target->getSourceRange(), OrphanKind::Continue); } } else if (auto *BS = dyn_cast(S)) { if (auto *Target = BS->getTarget()) { Ranges.emplace_back(Target->getSourceRange(), OrphanKind::Break); } } return true; } }; for (auto N : Nodes) { ControlFlowStmtSelector TheWalker; N.walk(TheWalker); for (auto Pair : TheWalker.Ranges) { // If the entire range does not include the target's range, we find // an orphan. if (!SM.rangeContains(WholeRange, Pair.first)) return Pair.second; } } // We find no orphan. return OrphanKind::None; } void analyze(ASTNode Node) { if (!shouldAnalyze(Node)) return; Decl *D = Node.is() ? Node.get() : nullptr; analyzeDecl(D); auto &DCInfo = getCurrentDC(); switch (getRangeMatchKind(Node.getSourceRange())) { case RangeMatchKind::NoneMatch: { // PatternBindingDecl is not visited; we need to explicitly analyze here. if (auto *VA = dyn_cast_or_null(D)) if (auto PBD = VA->getParentPatternBinding()) analyze(PBD); break; } case RangeMatchKind::RangeMatch: { postAnalysis(Node); // The node is contained in the given range. ContainedASTNodes.push_back(Node); Result = getSingleNodeKind(Node); return; } case RangeMatchKind::StartMatch: DCInfo.StartMatches.emplace_back(Node); break; case RangeMatchKind::EndMatch: DCInfo.EndMatches.emplace_back(Node); break; } // If no parent is considered as a contained node; this node should be // a top-level contained node. if (std::none_of(ContainedASTNodes.begin(), ContainedASTNodes.end(), [&](ASTNode N) { return SM.rangeContains(N.getSourceRange(), Node.getSourceRange()); })) { ContainedASTNodes.push_back(Node); } if (DCInfo.isMultiStatement()) { postAnalysis(DCInfo.EndMatches.back()); Result = {RangeKind::MultiStatement, /* Last node has the type */ resolveNodeType(DCInfo.EndMatches.back(), RangeKind::MultiStatement), TokensInRange, getImmediateContext(), nullptr, hasSingleEntryPoint(ContainedASTNodes), hasUnhandledError(ContainedASTNodes), getOrphanKind(ContainedASTNodes), llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls)}; } } bool shouldEnter(ASTNode Node) { if (hasResult()) return false; if (SM.isBeforeInBuffer(End, Node.getSourceRange().Start)) return false; if (SM.isBeforeInBuffer(Node.getSourceRange().End, Start)) return false; return true; } bool nodeContainSelection(ASTNode Node) { // If the selection starts before the node, return false. if (SM.isBeforeInBuffer(Start, Node.getStartLoc())) return false; // If the node ends before the selection, return false. if (SM.isBeforeInBuffer(Lexer::getLocForEndOfToken(SM, Node.getEndLoc()), End)) return false; // Contained. return true; } bool shouldAnalyze(ASTNode Node) { // Avoid analyzing implicit nodes. if (Node.isImplicit()) return false; // Avoid analyzing nodes that are not enclosed. if (SM.isBeforeInBuffer(End, Node.getEndLoc())) return false; if (SM.isBeforeInBuffer(Node.getStartLoc(), Start)) return false; return true; } ResolvedRangeInfo getResult() { if (Result.hasValue()) return Result.getValue(); return ResolvedRangeInfo(TokensInRange); } void analyzeDeclRef(ValueDecl *VD, SourceLoc Start, Type Ty, ReferenceMetaData Data) { // Add defensive check in case the given type is null. // FIXME: we should receive error type instead of null type. if (Ty.isNull()) return; // Only collect decl ref. if (Data.Kind != SemaReferenceKind::DeclRef) return; if (!isContainedInSelection(CharSourceRange(Start, 0))) return; // If the VD is declared outside of current file, exclude such decl. if (VD->getDeclContext()->getParentSourceFile() != &File) return; // Down-grade LValue type to RValue type if it's read-only. if (auto Access = Data.AccKind) { switch (Access.getValue()) { case AccessKind::Read: Ty = Ty->getRValueType(); break; case AccessKind::Write: case AccessKind::ReadWrite: break; } } auto It = llvm::find_if(ReferencedDecls, [&](ReferencedDecl D) { return D.VD == VD; }); if (It == ReferencedDecls.end()) { ReferencedDecls.emplace_back(VD, Ty); } else { // LValue type should take precedence. if (!It->Ty->hasLValueType() && Ty->hasLValueType()) { It->Ty = Ty; } } } private: RangeMatchKind getRangeMatchKind(SourceRange Input) { bool StartMatch = Input.Start == Start; bool EndMatch = Input.End == End; if (StartMatch && EndMatch) return RangeMatchKind::RangeMatch; else if (StartMatch) return RangeMatchKind::StartMatch; else if (EndMatch) return RangeMatchKind::EndMatch; else return RangeMatchKind::NoneMatch; } }; RangeResolver::RangeResolver(SourceFile &File, SourceLoc Start, SourceLoc End) : Impl(Implementation::createInstance(File, Start, End)) {} RangeResolver::RangeResolver(SourceFile &File, unsigned Offset, unsigned Length) : Impl(Implementation::createInstance(File, Offset, Length)) {} RangeResolver::~RangeResolver() = default; bool RangeResolver::walkToExprPre(Expr *E) { if (!Impl->shouldEnter(E)) return false; Impl->analyze(E); Impl->enter(E); return true; } bool RangeResolver::walkToStmtPre(Stmt *S) { if (!Impl->shouldEnter(S)) return false; Impl->analyze(S); Impl->enter(S); return true; }; bool RangeResolver::walkToDeclPre(Decl *D, CharSourceRange Range) { if (D->isImplicit()) return false; if (!Impl->shouldEnter(D)) return false; Impl->analyze(D); Impl->enter(D); return true; } bool RangeResolver::walkToExprPost(Expr *E) { Impl->leave(E); return !Impl->hasResult(); } bool RangeResolver::walkToStmtPost(Stmt *S) { Impl->leave(S); return !Impl->hasResult(); }; bool RangeResolver::walkToDeclPost(Decl *D) { Impl->leave(D); return !Impl->hasResult(); } bool RangeResolver:: visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T, ReferenceMetaData Data) { Impl->analyzeDeclRef(D, Range.getStart(), T, Data); return true; } ResolvedRangeInfo RangeResolver::resolve() { if (!Impl) return ResolvedRangeInfo({}); Impl->enter(ASTNode()); walk(Impl->File); return Impl->getResult(); } void swift::ide::getLocationInfoForClangNode(ClangNode ClangNode, ClangImporter *Importer, llvm::Optional> &DeclarationLoc, StringRef &Filename) { clang::ASTContext &ClangCtx = Importer->getClangASTContext(); clang::SourceManager &ClangSM = ClangCtx.getSourceManager(); clang::SourceRange SR = ClangNode.getLocation(); if (auto MD = dyn_cast_or_null(ClangNode.getAsDecl())) { SR = clang::SourceRange(MD->getSelectorStartLoc(), MD->getDeclaratorEndLoc()); } clang::CharSourceRange CharRange = clang::Lexer::makeFileCharRange(clang::CharSourceRange::getTokenRange(SR), ClangSM, ClangCtx.getLangOpts()); if (CharRange.isInvalid()) return; std::pair Decomp = ClangSM.getDecomposedLoc(CharRange.getBegin()); if (!Decomp.first.isInvalid()) { if (auto FE = ClangSM.getFileEntryForID(Decomp.first)) { Filename = FE->getName(); std::pair EndDecomp = ClangSM.getDecomposedLoc(CharRange.getEnd()); DeclarationLoc = { Decomp.second, EndDecomp.second-Decomp.second }; } } } static unsigned getCharLength(SourceManager &SM, SourceRange TokenRange) { SourceLoc CharEndLoc = Lexer::getLocForEndOfToken(SM, TokenRange.End); return SM.getByteDistance(TokenRange.Start, CharEndLoc); } void swift::ide::getLocationInfo(const ValueDecl *VD, llvm::Optional> &DeclarationLoc, StringRef &Filename) { ASTContext &Ctx = VD->getASTContext(); SourceManager &SM = Ctx.SourceMgr; auto ClangNode = VD->getClangNode(); if (VD->getLoc().isValid()) { unsigned NameLen; if (auto FD = dyn_cast(VD)) { SourceRange R = FD->getSignatureSourceRange(); if (R.isInvalid()) return; NameLen = getCharLength(SM, R); } else { if (VD->hasName()) { NameLen = VD->getBaseName().userFacingName().size(); } else { NameLen = getCharLength(SM, VD->getLoc()); } } unsigned DeclBufID = SM.findBufferContainingLoc(VD->getLoc()); DeclarationLoc = { SM.getLocOffsetInBuffer(VD->getLoc(), DeclBufID), NameLen }; Filename = SM.getIdentifierForBuffer(DeclBufID); } else if (ClangNode) { ClangImporter *Importer = static_cast(Ctx.getClangModuleLoader()); return getLocationInfoForClangNode(ClangNode, Importer, DeclarationLoc, Filename); } } CharSourceRange CallArgInfo::getEntireCharRange(const SourceManager &SM) const { return CharSourceRange(SM, LabelRange.getStart(), Lexer::getLocForEndOfToken(SM, ArgExp->getEndLoc())); } static Expr* getSingleNonImplicitChild(Expr *Parent) { // If this expr is non-implicit, we are done. if (!Parent->isImplicit()) return Parent; // Collect all immediate children. llvm::SmallVector Children; Parent->forEachImmediateChildExpr([&](Expr *E) { Children.push_back(E); return E; }); // If more than one children are found, we are not sure the non-implicit node. if (Children.size() != 1) return Parent; // Dig deeper if necessary. return getSingleNonImplicitChild(Children[0]); } std::vector swift::ide:: getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) { std::vector InfoVec; if (auto *TE = dyn_cast(Arg)) { size_t ElemIndex = 0; for (Expr *Elem : TE->getElements()) { SourceLoc LabelStart(Elem->getStartLoc()); SourceLoc LabelEnd(LabelStart); auto NameIdentifier = TE->getElementName(ElemIndex); if (!NameIdentifier.empty()) { LabelStart = TE->getElementNameLoc(ElemIndex); if (EndKind == LabelRangeEndAt::LabelNameOnly) LabelEnd = LabelStart.getAdvancedLoc(NameIdentifier.getLength()); } InfoVec.push_back({getSingleNonImplicitChild(Elem), CharSourceRange(SM, LabelStart, LabelEnd)}); ++ElemIndex; } } else if (auto *PE = dyn_cast(Arg)) { if (auto Sub = PE->getSubExpr()) InfoVec.push_back({getSingleNonImplicitChild(Sub), CharSourceRange(Sub->getStartLoc(), 0)}); } return InfoVec; } std::vector swift::ide:: getCallArgLabelRanges(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) { std::vector Ranges; auto InfoVec = getCallArgInfo(SM, Arg, EndKind); std::transform(InfoVec.begin(), InfoVec.end(), std::back_inserter(Ranges), [](CallArgInfo &Info) { return Info.LabelRange; }); return Ranges; }