//===--- Utils.h - Misc utilities -------------------------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// #ifndef SWIFT_IDE_UTILS_H #define SWIFT_IDE_UTILS_H #include "swift/AST/ASTNode.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/Effects.h" #include "swift/AST/Expr.h" #include "swift/AST/Module.h" #include "swift/Basic/LLVM.h" #include "swift/IDE/IDEBridging.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/Parse/Token.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VirtualFileSystem.h" #include #include #include #include namespace llvm { template class function_ref; class MemoryBuffer; } namespace clang { class Module; class NamedDecl; } namespace swift { class ValueDecl; class ASTContext; class SourceFile; class TypeDecl; class SourceLoc; class Type; class Decl; class DeclContext; class CallExpr; class ClangNode; class ClangImporter; class Token; namespace ide { struct SourceCompleteResult { // Set to true if the input source is fully formed, false otherwise. bool IsComplete; // The text to use as the indent string when auto indenting the next line. // This will contain the exactly what the client typed (any whitespaces and // tabs) and can be used to indent subsequent lines. It does not include // the current indent level, IDE clients should insert the correct indentation // with spaces or tabs to account for the current indent level. The indent // prefix will contain the leading space characters of the line that // contained the '{', '(' or '[' character that was unbalanced. std::string IndentPrefix; // Returns the indent level as an indentation count (number of indentations // to apply). Clients can translate this into the standard indentation that // is being used by the IDE (3 spaces? 1 tab?) and should use the indent // prefix string followed by the correct indentation. uint32_t IndentLevel; SourceCompleteResult() : IsComplete(false), IndentPrefix(), IndentLevel(0) {} }; SourceCompleteResult isSourceInputComplete(std::unique_ptr MemBuf, SourceFileKind SFKind, const LangOptions &LangOpts); SourceCompleteResult isSourceInputComplete(StringRef Text, SourceFileKind SFKind, const LangOptions &LangOpts); /// Visits all overridden declarations exhaustively from VD, including protocol /// conformances and clang declarations. void walkOverriddenDecls(const ValueDecl *VD, llvm::function_ref)> Fn); void collectModuleNames(StringRef SDKPath, std::vector &Modules); struct PlaceholderOccurrence { /// The complete placeholder string. StringRef FullPlaceholder; /// The inner string of the placeholder. StringRef PlaceholderContent; /// The dollar identifier that was used to replace the placeholder. StringRef IdentifierReplacement; }; /// Replaces Xcode editor placeholders (<#such as this#>) with dollar /// identifiers and returns a new memory buffer. /// /// The replacement identifier will be the same size as the placeholder so that /// the new buffer will have the same size as the input buffer. std::unique_ptr replacePlaceholders(std::unique_ptr InputBuf, llvm::function_ref Callback); std::unique_ptr replacePlaceholders(std::unique_ptr InputBuf, bool *HadPlaceholder = nullptr); std::optional> parseLineCol(StringRef LineCol); class XMLEscapingPrinter : public StreamPrinter { public: XMLEscapingPrinter(raw_ostream &OS) : StreamPrinter(OS){}; void printText(StringRef Text) override; void printXML(StringRef Text); }; enum class CursorInfoKind { Invalid, ValueRef, ModuleRef, ExprStart, StmtStart, }; /// Base class of more specialized \c ResolvedCursorInfos that also represents /// and \c Invalid cursor info. struct ResolvedCursorInfo : public llvm::RefCountedBase { protected: CursorInfoKind Kind = CursorInfoKind::Invalid; SourceFile *SF = nullptr; SourceLoc Loc; protected: ResolvedCursorInfo(CursorInfoKind Kind, SourceFile *SF, SourceLoc Loc) : Kind(Kind), SF(SF), Loc(Loc) {} public: ResolvedCursorInfo() = default; ResolvedCursorInfo(SourceFile *SF) : SF(SF) {} CursorInfoKind getKind() const { return Kind; } SourceFile *getSourceFile() const { return SF; } SourceLoc getLoc() const { return Loc; } void setLoc(SourceLoc Loc) { this->Loc = Loc; } friend bool operator==(const ResolvedCursorInfo &lhs, const ResolvedCursorInfo &rhs) { return lhs.SF == rhs.SF && lhs.Loc.getOpaquePointerValue() == rhs.Loc.getOpaquePointerValue(); } bool isValid() const { return !isInvalid(); } bool isInvalid() const { return Kind == CursorInfoKind::Invalid; } }; typedef llvm::IntrusiveRefCntPtr ResolvedCursorInfoPtr; struct ResolvedValueRefCursorInfo : public ResolvedCursorInfo { private: ValueDecl *ValueD = nullptr; TypeDecl *CtorTyRef = nullptr; ExtensionDecl *ExtTyRef = nullptr; bool IsRef = true; Type SolutionSpecificInterfaceType; Type ContainerType; std::optional> CustomAttrRef = std::nullopt; bool IsKeywordArgument = false; /// It this is a ref, whether it is "dynamic". See \c ide::isDynamicRef. bool IsDynamic = false; /// If this is a dynamic ref, the types of the base (multiple in the case of /// protocol composition). SmallVector ReceiverTypes; /// Declarations that were shadowed by \c ValueD using a shorthand syntax /// that names both the newly declared variable and the referenced variable /// by the same identifier in the source text. This includes shorthand /// closure captures (`[foo]`) and shorthand if captures /// (`if let foo {`). Ordered from innermost to outermost shadows. /// /// Decls that are shadowed using shorthand syntax should be reported as /// additional cursor info results. SmallVector ShorthandShadowedDecls; public: ResolvedValueRefCursorInfo() = default; explicit ResolvedValueRefCursorInfo( SourceFile *SF, SourceLoc Loc, ValueDecl *ValueD, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, bool IsRef, Type SolutionSpecificInterfaceType, Type ContainerType, std::optional> CustomAttrRef, bool IsKeywordArgument, bool IsDynamic, SmallVector ReceiverTypes, SmallVector ShorthandShadowedDecls) : ResolvedCursorInfo(CursorInfoKind::ValueRef, SF, Loc), ValueD(ValueD), CtorTyRef(CtorTyRef), ExtTyRef(ExtTyRef), IsRef(IsRef), SolutionSpecificInterfaceType(SolutionSpecificInterfaceType), ContainerType(ContainerType), CustomAttrRef(CustomAttrRef), IsKeywordArgument(IsKeywordArgument), IsDynamic(IsDynamic), ReceiverTypes(ReceiverTypes), ShorthandShadowedDecls(ShorthandShadowedDecls) {} ValueDecl *getValueD() const { return ValueD; } ExtensionDecl *getExtTyRef() const { return ExtTyRef; } TypeDecl *getCtorTyRef() const { return CtorTyRef; } bool isRef() const { return IsRef; } Type getSolutionSpecificInterfaceType() const { return SolutionSpecificInterfaceType; } Type getContainerType() const { return ContainerType; } bool isKeywordArgument() const { return IsKeywordArgument; } void setIsKeywordArgument(bool IsKeywordArgument) { this->IsKeywordArgument = IsKeywordArgument; } bool isDynamic() const { return this->IsDynamic; } ArrayRef getReceiverTypes() const { return this->ReceiverTypes; } ArrayRef getShorthandShadowedDecls() const { return this->ShorthandShadowedDecls; }; void setShorthandShadowedDecls( const SmallVector &ShorthandShadowedDecls) { this->ShorthandShadowedDecls = ShorthandShadowedDecls; }; ValueDecl *typeOrValue() { return CtorTyRef ? CtorTyRef : ValueD; } std::optional> getCustomAttrRef() const { return CustomAttrRef; } static bool classof(const ResolvedCursorInfo *Info) { return Info->getKind() == CursorInfoKind::ValueRef; } }; typedef llvm::IntrusiveRefCntPtr ResolvedValueRefCursorInfoPtr; struct ResolvedModuleRefCursorInfo : public ResolvedCursorInfo { private: ModuleEntity Mod; public: ResolvedModuleRefCursorInfo(SourceFile *SF, SourceLoc Loc, ModuleEntity Mod) : ResolvedCursorInfo(CursorInfoKind::ModuleRef, SF, Loc), Mod(Mod) {} ModuleEntity getMod() const { return Mod; } static bool classof(const ResolvedCursorInfo *Info) { return Info->getKind() == CursorInfoKind::ModuleRef; } }; struct ResolvedExprStartCursorInfo : public ResolvedCursorInfo { private: Expr *TrailingExpr = nullptr; public: ResolvedExprStartCursorInfo(SourceFile *SF, SourceLoc Loc, Expr *TrailingExpr) : ResolvedCursorInfo(CursorInfoKind::ExprStart, SF, Loc), TrailingExpr(TrailingExpr) {} Expr *getTrailingExpr() const { return TrailingExpr; } static bool classof(const ResolvedCursorInfo *Info) { return Info->getKind() == CursorInfoKind::ExprStart; } }; struct ResolvedStmtStartCursorInfo : public ResolvedCursorInfo { Stmt *TrailingStmt = nullptr; ResolvedStmtStartCursorInfo(SourceFile *SF, SourceLoc Loc, Stmt *TrailingStmt) : ResolvedCursorInfo(CursorInfoKind::StmtStart, SF, Loc), TrailingStmt(TrailingStmt) {} Stmt *getTrailingStmt() const { return TrailingStmt; } static bool classof(const ResolvedCursorInfo *Info) { return Info->getKind() == CursorInfoKind::StmtStart; } }; void simple_display(llvm::raw_ostream &out, ResolvedCursorInfoPtr info); /// Used by NameMatcher to track parent CallExprs when walking a checked AST. struct CallingParent { Expr *ApplicableTo; CallExpr *Call; }; enum class RangeKind : int8_t { Invalid = -1, SingleExpression, SingleStatement, SingleDecl, MultiStatement, PartOfExpression, MultiTypeMemberDecl, }; struct DeclaredDecl { ValueDecl *VD; bool ReferredAfterRange; DeclaredDecl(ValueDecl* VD) : VD(VD), ReferredAfterRange(false) {} DeclaredDecl(): DeclaredDecl(nullptr) {} bool operator==(const DeclaredDecl& other) const; }; struct ReferencedDecl { ValueDecl *VD; Type Ty; ReferencedDecl(ValueDecl* VD, Type Ty) : VD(VD), Ty(Ty) {} ReferencedDecl() : ReferencedDecl(nullptr, Type()) {} }; enum class OrphanKind : int8_t { None, Break, Continue, }; enum class ExitState: int8_t { Positive, Negative, Unsure, }; struct ReturnInfo { TypeBase* ReturnType; ExitState Exit; ReturnInfo(): ReturnInfo(nullptr, ExitState::Unsure) {} ReturnInfo(TypeBase* ReturnType, ExitState Exit): ReturnType(ReturnType), Exit(Exit) {} ReturnInfo(ASTContext &Ctx, ArrayRef Branches); }; struct ResolvedRangeInfo { RangeKind Kind; ReturnInfo ExitInfo; ArrayRef TokensInRange; CharSourceRange ContentRange; bool HasSingleEntry; PossibleEffects UnhandledEffects; OrphanKind Orphan; // The topmost ast nodes contained in the given range. ArrayRef ContainedNodes; ArrayRef DeclaredDecls; ArrayRef ReferencedDecls; DeclContext* RangeContext; Expr* CommonExprParent; ResolvedRangeInfo(RangeKind Kind, ReturnInfo ExitInfo, ArrayRef TokensInRange, DeclContext* RangeContext, Expr *CommonExprParent, bool HasSingleEntry, PossibleEffects UnhandledEffects, OrphanKind Orphan, ArrayRef ContainedNodes, ArrayRef DeclaredDecls, ArrayRef ReferencedDecls): Kind(Kind), ExitInfo(ExitInfo), TokensInRange(TokensInRange), ContentRange(calculateContentRange(TokensInRange)), HasSingleEntry(HasSingleEntry), UnhandledEffects(UnhandledEffects), Orphan(Orphan), ContainedNodes(ContainedNodes), DeclaredDecls(DeclaredDecls), ReferencedDecls(ReferencedDecls), RangeContext(RangeContext), CommonExprParent(CommonExprParent) {} ResolvedRangeInfo(ArrayRef TokensInRange) : ResolvedRangeInfo(RangeKind::Invalid, {nullptr, ExitState::Unsure}, TokensInRange, nullptr, /*Commom Expr Parent*/nullptr, /*Single entry*/true, /*UnhandledEffects*/{}, OrphanKind::None, {}, {}, {}) {} ResolvedRangeInfo(): ResolvedRangeInfo(ArrayRef()) {} void print(llvm::raw_ostream &OS) const; ExitState exit() const { return ExitInfo.Exit; } Type getType() const { return ExitInfo.ReturnType; } friend bool operator==(const ResolvedRangeInfo &lhs, const ResolvedRangeInfo &rhs) { if (lhs.TokensInRange.size() != rhs.TokensInRange.size()) return false; if (lhs.TokensInRange.empty()) return true; return lhs.TokensInRange.front().getLoc() == rhs.TokensInRange.front().getLoc(); } private: static CharSourceRange calculateContentRange(ArrayRef Tokens); }; void simple_display(llvm::raw_ostream &out, const ResolvedRangeInfo &info); /// This provides a utility to view a printed name by parsing the components /// of that name. The components include a base name and an array of argument /// labels. class DeclNameViewer { StringRef BaseName; SmallVector Labels; bool IsValid; bool HasParen; public: DeclNameViewer(StringRef Text); DeclNameViewer() : DeclNameViewer(StringRef()) {} operator bool() const { return !BaseName.empty(); } StringRef base() const { return BaseName; } llvm::ArrayRef args() const { return llvm::ArrayRef(Labels); } unsigned argSize() const { return Labels.size(); } unsigned partsCount() const { return 1 + Labels.size(); } unsigned commonPartsCount(DeclNameViewer &Other) const; bool isValid() const { return IsValid; } bool isFunction() const { return HasParen; } }; enum class RegionType { /// We could not match the rename location to a symbol to be renamed and the /// symbol was originally a text match result (has `RenameLocUsage::Unknown`). Unmatched, /// We could not match the rename location to a symbol to be renamed and the /// symbol came from the index (does not have `RenameLocUsage::Unknown`). Mismatch, /// We were able to match the result to a location in source code that's /// active with respect to the current compiler arguments. ActiveCode, /// We were able to match the result to a location in source code that's /// inactive with respect to the current compiler arguments. /// /// Currently, we don't evaluate #if so all occurrences inside #if blocks /// are considered inactive. InactiveCode, /// The location is inside a string literal. String, /// The location is inside a `#selector`. Selector, /// The location is inside a comment. Comment, }; enum class RefactoringRangeKind { /// `func [foo](a b: Int)` BaseName, /// `[init](a: Int)` KeywordBaseName, /// `func foo(a[ b]: Int)` ParameterName, /// `subscript(a[ a]: Int)` NoncollapsibleParameterName, /// `func foo([a] b: Int)` DeclArgumentLabel, /// `foo([a]: 1)` CallArgumentLabel, /// `foo(a[: ]1)` CallArgumentColon, /// `foo([]1) could expand to foo([a: ]1)` /// Also used for enum case declarations without a label, eg. /// `case foo([]String)` should expand to `case foo([a: ]String)`. CallArgumentCombined, /// `foo([a]:)` SelectorArgumentLabel, }; struct NoteRegion { RefactoringRangeKind Kind; // The below are relative to the containing Replacement's Text unsigned StartLine; unsigned StartColumn; unsigned EndLine; unsigned EndColumn; std::optional ArgIndex; }; struct Replacement { /// If the edit is outside of the originally request source file, the path /// to the file it is editing. StringRef Path; /// Range to apply the replacement to, zero-width if making an addition. CharSourceRange Range; /// If the edit is actually a file (which could be generated/from an /// expansion), the name (or path) of that buffer. StringRef BufferName; /// The text to replace \c Range with. StringRef Text; ArrayRef RegionsWorthNote; }; class SourceEditConsumer { public: virtual void accept(SourceManager &SM, RegionType RegionType, ArrayRef Replacements) = 0; virtual ~SourceEditConsumer() = default; void accept(SourceManager &SM, CharSourceRange Range, StringRef Text, ArrayRef SubRegions = {}); void accept(SourceManager &SM, SourceLoc Loc, StringRef Text, ArrayRef SubRegions = {}); void insertAfter(SourceManager &SM, SourceLoc Loc, StringRef Text, ArrayRef SubRegions = {}); void accept(SourceManager &SM, Replacement Replacement) { accept(SM, RegionType::ActiveCode, {Replacement}); } void remove(SourceManager &SM, CharSourceRange Range); void acceptMacroExpansionBuffer(SourceManager &SM, unsigned bufferID, SourceFile *containingSF, bool adjustExpansion, bool includeBufferName); }; /// This helper stream inserts text into a SourceLoc by calling functions in /// SourceEditorConsumer when it is destroyed. class EditorConsumerInsertStream: public raw_ostream { SourceEditConsumer &Consumer; SourceManager &SM; CharSourceRange Range; llvm::SmallString<64> Buffer; llvm::raw_svector_ostream OS; public: explicit EditorConsumerInsertStream(SourceEditConsumer &Consumer, SourceManager &SM, CharSourceRange Range): Consumer(Consumer), SM(SM), Range(Range), Buffer(), OS(Buffer) {} explicit EditorConsumerInsertStream(SourceEditConsumer &Consumer, SourceManager &SM, SourceLoc Loc): EditorConsumerInsertStream(Consumer, SM, CharSourceRange(Loc, 0)) {} ~EditorConsumerInsertStream() { Consumer.accept(SM, Range, OS.str()); } void write_impl(const char *ptr, size_t size) override { OS.write(ptr, size); } uint64_t current_pos() const override { return OS.tell(); } size_t preferred_buffer_size() const override { return 0; } }; /// Outputs replacements as JSON, see `writeEditsInJson` class SourceEditJsonConsumer : public SourceEditConsumer { struct Implementation; Implementation &Impl; public: SourceEditJsonConsumer(llvm::raw_ostream &OS); ~SourceEditJsonConsumer(); void accept(SourceManager &SM, RegionType RegionType, ArrayRef Replacements) override; }; /// Outputs replacements to `OS` in the form /// ``` /// // startLine:startCol -> endLine:endCol /// replacement /// text /// /// ``` class SourceEditTextConsumer : public SourceEditConsumer { llvm::raw_ostream &OS; public: SourceEditTextConsumer(llvm::raw_ostream &OS) : OS(OS) {} void accept(SourceManager &SM, RegionType RegionType, ArrayRef Replacements) override; }; /// Outputs the rewritten buffer to `OS` with RUN and CHECK lines removed class SourceEditOutputConsumer : public SourceEditConsumer { struct Implementation; Implementation &Impl; public: SourceEditOutputConsumer(SourceManager &SM, unsigned BufferId, llvm::raw_ostream &OS); ~SourceEditOutputConsumer(); void accept(SourceManager &SM, RegionType RegionType, ArrayRef Replacements) override; }; /// Broadcasts `accept` to all `Consumers` class BroadcastingSourceEditConsumer : public SourceEditConsumer { ArrayRef> Consumers; public: BroadcastingSourceEditConsumer( ArrayRef> Consumers) : Consumers(Consumers) {} void accept(SourceManager &SM, RegionType RegionType, ArrayRef Replacements) override; }; enum class LabelRangeEndAt: int8_t { BeforeElemStart, LabelNameOnly, }; struct CallArgInfo { Expr *ArgExp; CharSourceRange LabelRange; bool IsTrailingClosure; CharSourceRange getEntireCharRange(const SourceManager &SM) const; }; std::vector getCallArgInfo(SourceManager &SM, ArgumentList *Args, LabelRangeEndAt EndKind); // Get the ranges of argument labels from an Arg, either tuple or paren, and // the index of the first trailing closure argument, if any. This includes empty // ranges for any unlabelled arguments, including the first trailing closure. std::pair, std::optional> getCallArgLabelRanges(SourceManager &SM, ArgumentList *Args, LabelRangeEndAt EndKind); /// Whether a decl is defined from clang source. bool isFromClang(const Decl *D); /// Retrieve the effective Clang node for the given declaration, which /// copes with the odd case of imported Error enums. ClangNode getEffectiveClangNode(const Decl *decl); /// Retrieve the Clang node for the given extension, if it has one. ClangNode extensionGetClangNode(const ExtensionDecl *ext); /// Utility for finding the referenced declaration from a call, which might /// include a second level of function application for a 'self.' expression, /// or a curry thunk, etc. If \p semantic is true then the underlying semantic /// expression of \p expr is used. std::pair getReferencedDecl(Expr *expr, bool semantic = true); /// Whether the last expression in \p ExprStack is being called. bool isBeingCalled(ArrayRef ExprStack); /// The base of the last expression in \p ExprStack (which may look up the /// stack in eg. the case of a `DotSyntaxCallExpr`). Expr *getBase(ArrayRef ExprStack); /// Returns whether or not \p D could be overridden, eg. it's a member of a /// protocol, a non-final method in a class, etc. bool isDeclOverridable(ValueDecl *D); /// Given a reference to a member \p D and its \p Base expression, return /// whether that declaration could be dynamic, ie. may resolve to some other /// declaration. Note that while the decl itself itself may be overridable, a /// reference to it is not necessarily "dynamic". Furthermore, is *not* the /// `dynamic` keyword. /// /// A simple example is `SomeType.classMethod()`. `classMethod` /// is itself overridable, but that particular reference to it *has* to be the /// one in `SomeType`. Contrast that to `type(of: foo).classMethod()` where /// `classMethod` could be any `classMethod` up or down the hierarchy from the /// type of the \p Base expression. bool isDynamicRef(Expr *Base, ValueDecl *D, llvm::function_ref getType = [](Expr *E) { return E->getType(); }); /// Adds the resolved nominal types of \p Base to \p Types. void getReceiverType(Expr *Base, SmallVectorImpl &Types); #if SWIFT_BUILD_SWIFT_SYNTAX /// Entry point to run the NameMatcher written in swift-syntax. /// /// - Parameters: /// - sourceFile: The source file from which to load the SwiftSyntax tree /// - locations: The locations to resolve /// - Returns: A list of `ResolvedLoc` that have been resolved. This list might /// be shorteder than `locations` if some locations could not be resolved and /// the resolved locations might be in a different order than `locations`. std::vector runNameMatcher(const SourceFile &sourceFile, ArrayRef locations); #endif } // namespace ide } // namespace swift #endif // SWIFT_IDE_UTILS_H