//===--- CursorInfo.cpp ---------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2022 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/IDE/CursorInfo.h" #include "ExprContextAnalysis.h" #include "swift/AST/ASTDemangler.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" #include "swift/AST/USRGeneration.h" #include "swift/IDE/SelectedOverloadInfo.h" #include "swift/IDE/TypeCheckCompletionCallback.h" #include "swift/Parse/IDEInspectionCallbacks.h" #include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/Basic/Module.h" using namespace swift; using namespace swift::constraints; using namespace ide; namespace { // MARK: - Utilities void typeCheckDeclAndParentClosures(ValueDecl *VD) { // We need to type check any parent closures because their types are // encoded in the USR of ParentContexts in the cursor info response. auto DC = VD->getDeclContext(); while (DC->getParent()) { if (auto Closure = dyn_cast(DC)) { if (Closure->getType().isNull()) { typeCheckASTNodeAtLoc( TypeCheckASTNodeAtLocContext::declContext(DC->getParent()), Closure->getLoc()); } } DC = DC->getParent(); } if (!VD->getInterfaceType()) { // The decl has an interface time if it came from another module. In that // case, there's nothing to do. Otherwise, type check the decl to get its // type. typeCheckASTNodeAtLoc( TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()), VD->getLoc()); } if (auto VarD = dyn_cast(VD)) { if (VarD->hasAttachedPropertyWrapper()) { // Type check any attached property wrappers so the annotated declaration // can refer to their USRs. (void)VarD->getPropertyWrapperBackingPropertyType(); } // Visit emitted accessors so we generated accessors from property wrappers. VarD->visitEmittedAccessors([&](AccessorDecl *accessor) {}); } } // MARK: - NodeFinderResults enum class NodeFinderResultKind { Decl, Expr }; class NodeFinderResult { NodeFinderResultKind Kind; protected: NodeFinderResult(NodeFinderResultKind Kind) : Kind(Kind) {} public: NodeFinderResultKind getKind() const { return Kind; } }; class NodeFinderDeclResult : public NodeFinderResult { ValueDecl *ValueD; public: NodeFinderDeclResult(ValueDecl *ValueD) : NodeFinderResult(NodeFinderResultKind::Decl), ValueD(ValueD) {} ValueDecl *getDecl() const { return ValueD; } static bool classof(const NodeFinderResult *Res) { return Res->getKind() == NodeFinderResultKind::Decl; } }; class NodeFinderExprResult : public NodeFinderResult { Expr *E; /// The \c DeclContext in which \c E occurs. DeclContext *DC; public: NodeFinderExprResult(Expr *E, DeclContext *DC) : NodeFinderResult(NodeFinderResultKind::Expr), E(E), DC(DC) {} Expr *getExpr() const { return E; } DeclContext *getDeclContext() const { return DC; } static bool classof(const NodeFinderResult *Res) { return Res->getKind() == NodeFinderResultKind::Expr; } }; // MARK: - NodeFinder /// Walks the AST, looking for a node at \c LocToResolve. While walking the /// AST, also gathers information about shorthand shadows. class NodeFinder : ASTWalker { SourceFile &SrcFile; SourceLoc LocToResolve; /// As we are walking the tree, this variable is updated to the last seen /// DeclContext. SmallVector DeclContextStack; /// The found node. std::unique_ptr Result; /// If a decl shadows another decl using shorthand syntax (`[foo]` or /// `if let foo {`), this maps the re-declared variable to the one that is /// being shadowed. /// The transitive closure of shorthand shadowed decls should be reported as /// additional results in cursor info. llvm::DenseMap ShorthandShadowedDecls; public: NodeFinder(SourceFile &SrcFile, SourceLoc LocToResolve) : SrcFile(SrcFile), LocToResolve(LocToResolve), DeclContextStack({&SrcFile}) {} void resolve() { SrcFile.walk(*this); } std::unique_ptr takeResult() { return std::move(Result); } /// Get the declarations that \p ShadowingDecl shadows using shorthand shadow /// syntax. Ordered from innermost to outermost shadows. SmallVector getShorthandShadowedDecls(ValueDecl *ShadowingDecl) { SmallVector Result; auto ShorthandShadowedDecl = ShorthandShadowedDecls[ShadowingDecl]; while (ShorthandShadowedDecl) { Result.push_back(ShorthandShadowedDecl); ShorthandShadowedDecl = ShorthandShadowedDecls[ShorthandShadowedDecl]; } return Result; } private: SourceManager &getSourceMgr() const { return SrcFile.getASTContext().SourceMgr; } /// The decl context that is currently being walked. DeclContext *getCurrentDeclContext() { return DeclContextStack.back(); } bool rangeContainsLocToResolve(SourceRange Range) const { return getSourceMgr().containsRespectingReplacedRanges(Range, LocToResolve); } PreWalkAction walkToDeclPre(Decl *D) override { if (!rangeContainsLocToResolve(D->getSourceRangeIncludingAttrs())) { return PreWalkAction::SkipChildren; } if (auto *newDC = dyn_cast(D)) { DeclContextStack.push_back(newDC); } if (D->getLoc() != LocToResolve) { return Action::Continue(); } if (auto VD = dyn_cast(D)) { // FIXME: ParamDecls might be closure parameters that can have ambiguous // types. The current infrastructure of just asking for the VD's type // doesn't work here. We need to inspect the constraints system solution. if (VD->hasName() && !isa(D)) { assert(Result == nullptr); Result = std::make_unique(VD); return Action::Stop(); } } return Action::Continue(); } PostWalkAction walkToDeclPost(Decl *D) override { if (auto *newDC = dyn_cast(D)) { assert(DeclContextStack.back() == newDC); DeclContextStack.pop_back(); } return Action::Continue(); } PreWalkResult walkToExprPre(Expr *E) override { if (auto closure = dyn_cast(E)) { DeclContextStack.push_back(closure); } if (auto CaptureList = dyn_cast(E)) { for (auto ShorthandShadows : getShorthandShadows(CaptureList, getCurrentDeclContext())) { assert(ShorthandShadowedDecls.count(ShorthandShadows.first) == 0); ShorthandShadowedDecls[ShorthandShadows.first] = ShorthandShadows.second; } } if (E->getLoc() != LocToResolve) { return Action::Continue(E); } switch (E->getKind()) { case ExprKind::DeclRef: case ExprKind::UnresolvedDot: case ExprKind::UnresolvedDeclRef: { assert(Result == nullptr); Result = std::make_unique(E, getCurrentDeclContext()); return Action::Stop(); } default: break; } return Action::Continue(E); } PostWalkResult walkToExprPost(Expr *E) override { if (auto *closure = dyn_cast(E)) { assert(DeclContextStack.back() == closure); DeclContextStack.pop_back(); } return Action::Continue(E); } PreWalkResult walkToStmtPre(Stmt *S) override { if (auto CondStmt = dyn_cast(S)) { for (auto ShorthandShadow : getShorthandShadows(CondStmt, getCurrentDeclContext())) { assert(ShorthandShadowedDecls.count(ShorthandShadow.first) == 0); ShorthandShadowedDecls[ShorthandShadow.first] = ShorthandShadow.second; } } return Action::Continue(S); } }; // MARK: - Solver-based expression analysis class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback { public: struct CursorInfoDeclReference { /// If the referenced declaration is a member reference, the type of the /// member's base, otherwise \c null. Type BaseType; /// Whether the reference is dynamic (see \c ide::isDynamicRef) bool IsDynamicRef; /// The declaration that is being referenced. Will never be \c nullptr. ValueDecl *ReferencedDecl; }; private: /// The expression for which we want to provide cursor info results. Expr *ResolveExpr; SmallVector Results; void sawSolutionImpl(const Solution &S) override { auto &CS = S.getConstraintSystem(); auto Locator = CS.getConstraintLocator(ResolveExpr); auto CalleeLocator = S.getCalleeLocator(Locator); auto OverloadInfo = getSelectedOverloadInfo(S, CalleeLocator); if (!OverloadInfo.Value) { // We could not resolve the referenced declaration. Skip the solution. return; } bool IsDynamicRef = false; auto BaseLocator = CS.getConstraintLocator(Locator, ConstraintLocator::MemberRefBase); if (auto BaseExpr = simplifyLocatorToAnchor(BaseLocator).dyn_cast()) { IsDynamicRef = ide::isDynamicRef(BaseExpr, OverloadInfo.Value, [&S](Expr *E) { return S.getResolvedType(E); }); } Results.push_back({OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.Value}); } public: CursorInfoTypeCheckSolutionCallback(Expr *ResolveExpr) : ResolveExpr(ResolveExpr) {} ArrayRef getResults() const { return Results; } }; // MARK: - CursorInfoDoneParsingCallback class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks { CursorInfoConsumer &Consumer; SourceLoc RequestedLoc; public: CursorInfoDoneParsingCallback(Parser &P, CursorInfoConsumer &Consumer, SourceLoc RequestedLoc) : IDEInspectionCallbacks(P), Consumer(Consumer), RequestedLoc(RequestedLoc) {} std::unique_ptr getDeclResult(NodeFinderDeclResult *DeclResult, SourceFile *SrcFile, NodeFinder &Finder) const { typeCheckDeclAndParentClosures(DeclResult->getDecl()); auto CursorInfo = std::make_unique( ResolvedCursorInfo(SrcFile), DeclResult->getDecl(), /*CtorTyRef=*/nullptr, /*ExtTyRef=*/nullptr, /*IsRef=*/false, /*Ty=*/Type(), /*ContainerType=*/Type()); CursorInfo->setLoc(RequestedLoc); CursorInfo->setShorthandShadowedDecls( Finder.getShorthandShadowedDecls(DeclResult->getDecl())); return CursorInfo; } std::unique_ptr getExprResult(NodeFinderExprResult *ExprResult, SourceFile *SrcFile, NodeFinder &Finder) const { Expr *E = ExprResult->getExpr(); DeclContext *DC = ExprResult->getDeclContext(); // Type check the statemnt containing E and listen for solutions. CursorInfoTypeCheckSolutionCallback Callback(E); llvm::SaveAndRestore CompletionCollector( DC->getASTContext().SolutionCallback, &Callback); typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC), E->getLoc()); if (Callback.getResults().empty()) { // No results. return nullptr; } for (auto Info : Callback.getResults()) { // Type check the referenced decls so that all their parent closures are // type-checked (see comment in typeCheckDeclAndParentClosures). typeCheckDeclAndParentClosures(Info.ReferencedDecl); } if (Callback.getResults().size() != 1) { // FIXME: We need to be able to report multiple results. return nullptr; } // Deliver results auto Res = Callback.getResults()[0]; auto CursorInfo = std::make_unique( ResolvedCursorInfo(SrcFile), Res.ReferencedDecl, /*CtorTyRef=*/nullptr, /*ExtTyRef=*/nullptr, /*IsRef=*/true, /*Ty=*/Type(), /*ContainerType=*/Res.BaseType); CursorInfo->setLoc(RequestedLoc); CursorInfo->setIsDynamic(Res.IsDynamicRef); if (Res.IsDynamicRef && Res.BaseType) { if (auto ReceiverType = Res.BaseType->getAnyNominal()) { CursorInfo->setReceiverTypes({ReceiverType}); } else if (auto MT = Res.BaseType->getAs()) { // Look through metatypes to get the nominal type decl. if (auto ReceiverType = MT->getInstanceType()->getAnyNominal()) { CursorInfo->setReceiverTypes({ReceiverType}); } } } CursorInfo->setShorthandShadowedDecls( Finder.getShorthandShadowedDecls(Res.ReferencedDecl)); return CursorInfo; } void doneParsing(SourceFile *SrcFile) override { if (!SrcFile) { return; } NodeFinder Finder(*SrcFile, RequestedLoc); Finder.resolve(); auto Result = Finder.takeResult(); if (!Result) { return; } std::unique_ptr CursorInfo; switch (Result->getKind()) { case NodeFinderResultKind::Decl: CursorInfo = getDeclResult(cast(Result.get()), SrcFile, Finder); break; case NodeFinderResultKind::Expr: CursorInfo = getExprResult(cast(Result.get()), SrcFile, Finder); break; } if (Result) { Consumer.handleResults(*CursorInfo); } } }; } // anonymous namespace. IDEInspectionCallbacksFactory * swift::ide::makeCursorInfoCallbacksFactory(CursorInfoConsumer &Consumer, SourceLoc RequestedLoc) { class CursorInfoCallbacksFactoryImpl : public IDEInspectionCallbacksFactory { CursorInfoConsumer &Consumer; SourceLoc RequestedLoc; public: CursorInfoCallbacksFactoryImpl(CursorInfoConsumer &Consumer, SourceLoc RequestedLoc) : Consumer(Consumer), RequestedLoc(RequestedLoc) {} IDEInspectionCallbacks *createIDEInspectionCallbacks(Parser &P) override { return new CursorInfoDoneParsingCallback(P, Consumer, RequestedLoc); } }; return new CursorInfoCallbacksFactoryImpl(Consumer, RequestedLoc); }