//===--- 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/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(); } typeCheckASTNodeAtLoc( TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()), VD->getLoc()); if (auto VarD = dyn_cast(VD)) { // 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 }; 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; } }; // 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; } } 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: - 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; } 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; } 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); }