diff --git a/include/swift/IDE/IDEBridging.h b/include/swift/IDE/IDEBridging.h index 84f2ae547da66..f9692f21ade19 100644 --- a/include/swift/IDE/IDEBridging.h +++ b/include/swift/IDE/IDEBridging.h @@ -23,19 +23,38 @@ #endif enum class LabelRangeType { + /// The matched location did not have any arguments. None, + /// The argument of a function/initializer/macro/... call + /// + /// ### Example /// `foo([a: ]2) or .foo([a: ]String)` CallArg, - /// `func([a b]: Int)` + /// The parameter of a function/initializer/macro/... declaration + /// + /// ### Example + /// `func foo([a b]: Int)` Param, + /// Parameters of a function that can't be collapsed if they are the same. + /// + /// This is the case for parameters of subscripts since subscripts have + /// unnamed + /// parameters by default. + /// + /// ### Example /// `subscript([a a]: Int)` NoncollapsibleParam, - /// `#selector(foo.func([a]:))` - Selector, + /// A reference to a function that also specifies argument labels to + /// disambiguate. + /// + /// ### Examples + /// - `#selector(foo.func([a]:))` + /// - `foo.func([a]:)` + CompoundName, }; enum class ResolvedLocContext { Default, Selector, Comment, StringLiteral }; diff --git a/include/swift/Refactoring/Refactoring.h b/include/swift/Refactoring/Refactoring.h index 9f23b4f21262b..6d34a34f2b7b2 100644 --- a/include/swift/Refactoring/Refactoring.h +++ b/include/swift/Refactoring/Refactoring.h @@ -10,14 +10,15 @@ // //===----------------------------------------------------------------------===// -#ifndef SWIFT_IDE_REFACTORING_H -#define SWIFT_IDE_REFACTORING_H +#ifndef SWIFT_REFACTORING_REFACTORING_H +#define SWIFT_REFACTORING_REFACTORING_H #include "swift/AST/DiagnosticConsumer.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/StringExtras.h" #include "swift/IDE/CancellableResult.h" #include "swift/IDE/Utils.h" +#include "swift/Refactoring/RenameLoc.h" #include "llvm/ADT/StringRef.h" namespace swift { @@ -61,21 +62,6 @@ struct RenameInfo { llvm::Optional getRenameInfo(ResolvedCursorInfoPtr cursorInfo); -enum class NameUsage { - Unknown, - Reference, - Definition, - Call -}; - -struct RenameLoc { - unsigned Line; - unsigned Column; - NameUsage Usage; - StringRef OldName; - const bool IsFunctionLike; -}; - /// An array of \c RenameLoc that also keeps the underlying string storage of /// the \c StringRef inside the \c RenameLoc alive. class RenameLocs { @@ -184,4 +170,4 @@ collectRefactorings(ResolvedCursorInfoPtr CursorInfo, bool ExcludeRename); } // namespace ide } // namespace swift -#endif // SWIFT_IDE_REFACTORING_H +#endif // SWIFT_REFACTORING_REFACTORING_H diff --git a/include/swift/Refactoring/RenameLoc.h b/include/swift/Refactoring/RenameLoc.h new file mode 100644 index 0000000000000..2f4d5c2b82f17 --- /dev/null +++ b/include/swift/Refactoring/RenameLoc.h @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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_REFACTORING_RENAMELOC_H +#define SWIFT_REFACTORING_RENAMELOC_H + +#include "swift/Basic/LLVM.h" + +namespace swift { +namespace ide { + +/// Describes how a `ResolvedLoc` is being used +enum class RenameLocUsage { + /// The definition of a function/subscript/variable/... + Definition, + + /// The symbol is being referenced. + /// + /// This includes + /// - References to variables + /// - Unapplied references to functions (`myStruct.memberFunc`) + /// - Calls to subscripts (`myArray[1]`, location is `[` here, length 1) + Reference, + + /// A function that is being called. + Call, + + /// Unknown name usage occurs if we don't have an entry in the index that + /// tells us whether the location is a call, reference or a definition. The + /// most common reasons why this happens is if the editor is adding syntactic + /// results (eg. from comments or string literals). + Unknown +}; + +/// The input to `findSyntacticRenameRanges`. +/// +/// Specifies the location of a base name for which `findSyntacticRenameRanges` +/// should find the argument labels as well as some semantic information needed +/// to resolve the rename ranges. +struct RenameLoc { + /// The line at which the base name starts (1-based). + unsigned Line; + + /// The column at which the base name (excluding trivia) starts (1-based). + unsigned Column; + + /// The offset at which the related name starts. + unsigned Offset; + + /// The length of the base name in the related identifier. For functions, + /// this does not include the parameters/arguments. + unsigned Length; + + /// How the identifier is being used (call, reference, definition, unknown). + /// + /// Used to decide whether a given occurance should be renamed and/or if its + /// argument labels should be renamed. + RenameLocUsage Usage; + + /// The old decl name being renamed. + /// + /// ### Examples + /// - `foo(a:b:)` for a function with two parameters. + /// - `foo` for a variable. + /// - `foo(_:)` for a function with a single unnamed parameter + /// - `foo()` for a function without parameters. + StringRef OldName; + + RenameLoc(unsigned Line, unsigned Column, RenameLocUsage Usage, + StringRef OldName) + : Line(Line), Column(Column), Usage(Usage), OldName(OldName) {} +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_REFACTORING_RENAMELOC_H diff --git a/lib/ASTGen/Sources/SwiftIDEUtilsBridging/NameMatcherBridging.swift b/lib/ASTGen/Sources/SwiftIDEUtilsBridging/NameMatcherBridging.swift index 68dcb0ab78982..204d2da9945ee 100644 --- a/lib/ASTGen/Sources/SwiftIDEUtilsBridging/NameMatcherBridging.swift +++ b/lib/ASTGen/Sources/SwiftIDEUtilsBridging/NameMatcherBridging.swift @@ -51,7 +51,7 @@ fileprivate extension IDEBridging.LabelRangeType { case .call: self = .CallArg case .parameters: self = .Param case .noncollapsibleParameters: self = .NoncollapsibleParam - case .selector: self = .Selector + case .selector: self = .CompoundName } } } diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index 79c37012efbe1..1347f6b058a04 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -612,8 +612,9 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, if (NameLoc.isCompound()) { auto Labels = getSelectorLabelRanges(getSourceMgr(), NameLoc); - bool Resolved = tryResolve(Node, NameLoc.getBaseNameLoc(), - LabelRangeType::Selector, Labels, llvm::None); + bool Resolved = + tryResolve(Node, NameLoc.getBaseNameLoc(), LabelRangeType::CompoundName, + Labels, llvm::None); if (!isDone()) { for (auto Label: Labels) { if (tryResolve(Node, Label.getStart())) { diff --git a/lib/Refactoring/ExtractFunction.cpp b/lib/Refactoring/ExtractFunction.cpp index ff66451460126..6b3f3a8ed2ef0 100644 --- a/lib/Refactoring/ExtractFunction.cpp +++ b/lib/Refactoring/ExtractFunction.cpp @@ -77,10 +77,8 @@ static SourceLoc getNewFuncInsertLoc(DeclContext *DC, return SourceLoc(); } -static std::vector getNotableRegions(StringRef SourceText, - unsigned NameOffset, - StringRef Name, - bool IsFunctionLike = false) { +static std::vector +getNotableRegions(StringRef SourceText, unsigned NameOffset, StringRef Name) { auto InputBuffer = llvm::MemoryBuffer::getMemBufferCopy(SourceText, ""); @@ -106,8 +104,7 @@ static std::vector getNotableRegions(StringRef SourceText, assert(!Resolved.empty() && "Failed to resolve generated func name loc"); RenameLoc RenameConfig = {LineAndCol.first, LineAndCol.second, - NameUsage::Definition, /*OldName=*/Name, - IsFunctionLike}; + RenameLocUsage::Definition, /*OldName=*/Name}; std::vector Ranges = getSyntacticRenameRangeDetails(SM, Name, Resolved.back(), RenameConfig) .Ranges; @@ -290,13 +287,11 @@ bool RefactoringActionExtractFunction::performChange() { StringRef DeclStr(Buffer.begin() + FuncBegin, FuncEnd - FuncBegin); auto NotableFuncRegions = - getNotableRegions(DeclStr, FuncNameOffset, ExtractedFuncName, - /*IsFunctionLike=*/true); + getNotableRegions(DeclStr, FuncNameOffset, ExtractedFuncName); StringRef CallStr(Buffer.begin() + ReplaceBegin, ReplaceEnd - ReplaceBegin); auto NotableCallRegions = - getNotableRegions(CallStr, CallNameOffset, ExtractedFuncName, - /*IsFunctionLike=*/true); + getNotableRegions(CallStr, CallNameOffset, ExtractedFuncName); // Insert the new function's declaration. EditConsumer.accept(SM, InsertLoc, DeclStr, NotableFuncRegions); diff --git a/lib/Refactoring/LocalRename.cpp b/lib/Refactoring/LocalRename.cpp index 239093162ba10..a6640b25a6829 100644 --- a/lib/Refactoring/LocalRename.cpp +++ b/lib/Refactoring/LocalRename.cpp @@ -207,22 +207,25 @@ class RenameRangeCollector : public IndexDataConsumer { bool finishDependency(bool isClangModule) override { return true; } Action startSourceEntity(const IndexSymbol &symbol) override { - if (symbol.USR == usr) { - if (auto loc = indexSymbolToRenameLoc(symbol)) { - // Inside capture lists like `{ [test] in }`, 'test' refers to both the - // newly declared, captured variable and the referenced variable it is - // initialized from. Make sure to only rename it once. - auto existingLoc = llvm::find_if(locations, [&](RenameLoc searchLoc) { - return searchLoc.Line == loc->Line && searchLoc.Column == loc->Column; - }); - if (existingLoc == locations.end()) { - locations.push_back(std::move(*loc)); - } else { - assert(existingLoc->OldName == loc->OldName && - existingLoc->IsFunctionLike == loc->IsFunctionLike && - "Asked to do a different rename for the same location?"); - } - } + if (symbol.USR != usr) { + return IndexDataConsumer::Continue; + } + auto loc = indexSymbolToRenameLoc(symbol); + if (!loc) { + return IndexDataConsumer::Continue; + } + + // Inside capture lists like `{ [test] in }`, 'test' refers to both the + // newly declared, captured variable and the referenced variable it is + // initialized from. Make sure to only rename it once. + auto existingLoc = llvm::find_if(locations, [&](RenameLoc searchLoc) { + return searchLoc.Line == loc->Line && searchLoc.Column == loc->Column; + }); + if (existingLoc == locations.end()) { + locations.push_back(std::move(*loc)); + } else { + assert(existingLoc->OldName == loc->OldName && + "Asked to do a different rename for the same location?"); } return IndexDataConsumer::Continue; } @@ -241,37 +244,19 @@ RenameRangeCollector::indexSymbolToRenameLoc(const index::IndexSymbol &symbol) { return llvm::None; } - NameUsage usage = NameUsage::Unknown; + RenameLocUsage usage = RenameLocUsage::Unknown; if (symbol.roles & (unsigned)index::SymbolRole::Call) { - usage = NameUsage::Call; + usage = RenameLocUsage::Call; } else if (symbol.roles & (unsigned)index::SymbolRole::Definition) { - usage = NameUsage::Definition; + usage = RenameLocUsage::Definition; } else if (symbol.roles & (unsigned)index::SymbolRole::Reference) { - usage = NameUsage::Reference; + usage = RenameLocUsage::Reference; } else { llvm_unreachable("unexpected role"); } - bool isFunctionLike = false; - - switch (symbol.symInfo.Kind) { - case index::SymbolKind::EnumConstant: - case index::SymbolKind::Function: - case index::SymbolKind::Constructor: - case index::SymbolKind::ConversionFunction: - case index::SymbolKind::InstanceMethod: - case index::SymbolKind::ClassMethod: - case index::SymbolKind::StaticMethod: - isFunctionLike = true; - break; - case index::SymbolKind::Class: - case index::SymbolKind::Enum: - case index::SymbolKind::Struct: - default: - break; - } StringRef oldName = stringStorage->copyString(symbol.name); - return RenameLoc{symbol.line, symbol.column, usage, oldName, isFunctionLike}; + return RenameLoc{symbol.line, symbol.column, usage, oldName}; } /// Get the decl context that we need to walk when renaming \p VD. diff --git a/lib/Refactoring/SyntacticRename.cpp b/lib/Refactoring/SyntacticRename.cpp index 1e64865993faf..66b2e47dea061 100644 --- a/lib/Refactoring/SyntacticRename.cpp +++ b/lib/Refactoring/SyntacticRename.cpp @@ -63,7 +63,7 @@ swift::ide::resolveRenameLocations(ArrayRef RenameLocs, return {}; } - if (RenameLoc.Usage == NameUsage::Call && !RenameLoc.IsFunctionLike) { + if (RenameLoc.Usage == RenameLocUsage::Call && !OldName.isFunction()) { Diags.diagnose(Location, diag::name_not_functionlike, NewName); return {}; } diff --git a/lib/Refactoring/SyntacticRenameRangeDetails.cpp b/lib/Refactoring/SyntacticRenameRangeDetails.cpp index 3dde5deb8d5d6..cb4e9d056dfd9 100644 --- a/lib/Refactoring/SyntacticRenameRangeDetails.cpp +++ b/lib/Refactoring/SyntacticRenameRangeDetails.cpp @@ -52,8 +52,6 @@ class RenameRangeDetailCollector { llvm::Optional FirstTrailingLabel, LabelRangeType RangeType, bool isCallSite); - bool isOperator() const { return Lexer::isOperator(Old.base()); } - private: /// Returns the range of the (possibly escaped) identifier at the start of /// \p Range and updates \p IsEscaped to indicate whether it's escaped or not. @@ -151,7 +149,7 @@ void RenameRangeDetailCollector::splitAndRenameLabel(CharSourceRange Range, case LabelRangeType::NoncollapsibleParam: return splitAndRenameParamLabel(Range, NameIndex, /*IsCollapsible=*/false); - case LabelRangeType::Selector: + case LabelRangeType::CompoundName: return addRenameRange(Range, RefactoringRangeKind::SelectorArgumentLabel, NameIndex); case LabelRangeType::None: @@ -250,7 +248,7 @@ bool RenameRangeDetailCollector::labelRangeMatches(CharSourceRange Range, LLVM_FALLTHROUGH; case LabelRangeType::CallArg: case LabelRangeType::Param: - case LabelRangeType::Selector: + case LabelRangeType::CompoundName: return ExistingLabel == (Expected.empty() ? "_" : Expected); case LabelRangeType::None: llvm_unreachable("Unhandled label range type"); @@ -277,12 +275,12 @@ bool RenameRangeDetailCollector::renameLabelsLenient( if (OldNames.empty()) return true; - while (!labelRangeMatches(Label, LabelRangeType::Selector, + while (!labelRangeMatches(Label, LabelRangeType::CompoundName, OldNames.back())) { if ((OldNames = OldNames.drop_back()).empty()) return true; } - splitAndRenameLabel(Label, LabelRangeType::Selector, + splitAndRenameLabel(Label, LabelRangeType::CompoundName, OldNames.size() - 1); OldNames = OldNames.drop_back(); continue; @@ -297,7 +295,7 @@ bool RenameRangeDetailCollector::renameLabelsLenient( if ((OldNames = OldNames.drop_back()).empty()) return true; } - splitAndRenameLabel(Label, LabelRangeType::Selector, + splitAndRenameLabel(Label, LabelRangeType::CompoundName, OldNames.size() - 1); OldNames = OldNames.drop_back(); continue; @@ -366,89 +364,154 @@ RegionType RenameRangeDetailCollector::getSyntacticRenameRegionType( } } -RegionType RenameRangeDetailCollector::addSyntacticRenameRanges( - const ResolvedLoc &Resolved, const RenameLoc &Config) { +enum class SpecialBaseName { + /// The function does not have one of the special base names. + None, + Subscript, + Init, + CallAsFunction +}; - if (!Resolved.range.isValid()) - return RegionType::Unmatched; +static SpecialBaseName specialBaseNameFor(const DeclNameViewer &declName) { + if (!declName.isFunction()) { + return SpecialBaseName::None; + } + if (declName.base() == "subscript") { + // FIXME: Don't handle as special name if it is backticked. + return SpecialBaseName::Subscript; + } else if (declName.base() == "init") { + // FIXME: Don't handle as special name if it is backticked. + return SpecialBaseName::Init; + } else if (declName.base() == "callAsFunction") { + // FIXME: this should only be treated specially for instance methods. + return SpecialBaseName::CallAsFunction; + } else { + return SpecialBaseName::None; + } +} - auto RegionKind = getSyntacticRenameRegionType(Resolved); - // Don't include unknown references coming from active code; if we don't - // have a semantic NameUsage for them, then they're likely unrelated symbols - // that happen to have the same name. - if (RegionKind == RegionType::ActiveCode && - Config.Usage == NameUsage::Unknown) - return RegionType::Unmatched; +RegionType RenameRangeDetailCollector::addSyntacticRenameRanges( + const ResolvedLoc &resolved, const RenameLoc &config) { - assert(Config.Usage != NameUsage::Call || Config.IsFunctionLike); + if (!resolved.range.isValid()) + return RegionType::Unmatched; - // FIXME: handle escaped keyword names `init` - bool IsSubscript = Old.base() == "subscript" && Config.IsFunctionLike; - bool IsInit = Old.base() == "init" && Config.IsFunctionLike; + RenameLocUsage usage = config.Usage; - // FIXME: this should only be treated specially for instance methods. - bool IsCallAsFunction = - Old.base() == "callAsFunction" && Config.IsFunctionLike; + auto regionKind = getSyntacticRenameRegionType(resolved); - bool IsSpecialBase = IsInit || IsSubscript || IsCallAsFunction; + SpecialBaseName specialBaseName = specialBaseNameFor(Old); - // Filter out non-semantic special basename locations with no labels. - // We've already filtered out those in active code, so these are - // any appearance of just 'init', 'subscript', or 'callAsFunction' in - // strings, comments, and inactive code. - if (IsSpecialBase && (Config.Usage == NameUsage::Unknown && - Resolved.labelType == LabelRangeType::None)) - return RegionType::Unmatched; + if (usage == RenameLocUsage::Unknown) { + // Unknown name usage occurs if we don't have an entry in the index that + // tells us whether the location is a call, reference or a definition. The + // most common reasons why this happens is if the editor is adding syntactic + // results (eg. from comments or string literals). + // + // Determine whether we should include them. + if (regionKind == RegionType::ActiveCode) { + // If the reference is in active code, we should have had a name usage + // from the index. Since we don't, they are likely unrelated symbols that + // happen to have the same name. Don't return them as matching ranges. + return RegionType::Unmatched; + } + if (specialBaseName != SpecialBaseName::None && + resolved.labelType == LabelRangeType::None) { + // Filter out non-semantic special basename locations with no labels. + // We've already filtered out those in active code, so these are + // any appearance of just 'init', 'subscript', or 'callAsFunction' in + // strings, comments, and inactive code. + return RegionType::Unmatched; + } + } - if (!Config.IsFunctionLike || !IsSpecialBase) { - if (renameBase(Resolved.range, RefactoringRangeKind::BaseName)) + switch (specialBaseName) { + case SpecialBaseName::None: + // If we don't have a special base name, we can just rename it. + if (renameBase(resolved.range, RefactoringRangeKind::BaseName)) { return RegionType::Mismatch; - - } else if (IsInit || IsCallAsFunction) { - if (renameBase(Resolved.range, RefactoringRangeKind::KeywordBaseName)) { - // The base name doesn't need to match (but may) for calls, but - // it should for definitions and references. - if (Config.Usage == NameUsage::Definition || - Config.Usage == NameUsage::Reference) { + } + break; + case SpecialBaseName::Init: + case SpecialBaseName::CallAsFunction: + if (renameBase(resolved.range, RefactoringRangeKind::KeywordBaseName)) { + // The base name doesn't need to match for calls, for example because + // an initializer can be called as `MyType()` and `callAsFunction` can + // be called as `myStruct()`, so even if the base fails to be renamed, + // continue. + // But the names do need to match for definitions and references. + if (usage == RenameLocUsage::Definition || + usage == RenameLocUsage::Reference) { return RegionType::Mismatch; } } - } else if (IsSubscript && Config.Usage == NameUsage::Definition) { - if (renameBase(Resolved.range, RefactoringRangeKind::KeywordBaseName)) - return RegionType::Mismatch; + break; + case SpecialBaseName::Subscript: + // Only try renaming the base for definitions of the subscript. + // Accesses to the subscript are modelled as references with `[` as the + // base name, which does not match. Subscripts are never called in the + // index. + if (usage == RenameLocUsage::Definition) { + if (renameBase(resolved.range, RefactoringRangeKind::KeywordBaseName)) { + return RegionType::Mismatch; + } + } + break; } - bool HandleLabels = false; - if (Config.IsFunctionLike) { - switch (Config.Usage) { - case NameUsage::Call: - HandleLabels = !isOperator(); + bool handleLabels = false; + bool isCallSite = false; + if (Old.isFunction()) { + switch (usage) { + case RenameLocUsage::Call: + // All calls except for operators have argument labels that should be + // renamed. + handleLabels = !Lexer::isOperator(Old.base()); + isCallSite = true; break; - case NameUsage::Definition: - HandleLabels = true; + case RenameLocUsage::Definition: + // All function definitions have argument labels that should be renamed. + handleLabels = true; + isCallSite = false; break; - case NameUsage::Reference: - HandleLabels = - Resolved.labelType == LabelRangeType::Selector || IsSubscript; + case RenameLocUsage::Reference: + if (resolved.labelType == LabelRangeType::CompoundName) { + // If we have a compound name that specifies argument labels to + // disambiguate functions with the same base name, we always need to + // rename the labels. + handleLabels = true; + isCallSite = false; + } else if (specialBaseName == SpecialBaseName::Subscript) { + // Accesses to a subscript are modeled using a reference to the + // subscript (with base name `[`). We always need to rename argument + // labels here. + handleLabels = true; + isCallSite = true; + } else { + handleLabels = false; + isCallSite = false; + } break; - case NameUsage::Unknown: - HandleLabels = Resolved.labelType != LabelRangeType::None; + case RenameLocUsage::Unknown: + // If we don't know where the function is used, fall back to trying to + // rename labels if there are some. + handleLabels = resolved.labelType != LabelRangeType::None; + isCallSite = resolved.labelType == LabelRangeType::CallArg; break; } } - if (HandleLabels) { - bool isCallSite = Config.Usage != NameUsage::Definition && - (Config.Usage != NameUsage::Reference || IsSubscript) && - Resolved.labelType == LabelRangeType::CallArg; - - if (renameLabels(Resolved.labelRanges, Resolved.firstTrailingLabel, - Resolved.labelType, isCallSite)) - return Config.Usage == NameUsage::Unknown ? RegionType::Unmatched - : RegionType::Mismatch; + if (handleLabels) { + bool renameLabelsFailed = + renameLabels(resolved.labelRanges, resolved.firstTrailingLabel, + resolved.labelType, isCallSite); + if (renameLabelsFailed) { + return usage == RenameLocUsage::Unknown ? RegionType::Unmatched + : RegionType::Mismatch; + } } - return RegionKind; + return regionKind; } } // end anonymous namespace diff --git a/test/refactoring/SyntacticRename/functions.swift b/test/refactoring/SyntacticRename/functions.swift index 3f9bf745b0cf4..ae6d880cf4ed2 100644 --- a/test/refactoring/SyntacticRename/functions.swift +++ b/test/refactoring/SyntacticRename/functions.swift @@ -89,7 +89,7 @@ _ = memberwise . /*memberwise-x:ref*/x // RUN: diff -u %S/Outputs/functions/import.swift.expected %t.ranges/functions_import.swift // RUN: %refactor -find-rename-ranges -source-filename %s -pos="bar" -is-function-like -old-name "bar(_:)" >> %t.ranges/functions_bar.swift // RUN: diff -u %S/Outputs/functions/bar.swift.expected %t.ranges/functions_bar.swift -// RUN: %refactor -find-rename-ranges -source-filename %s -pos="no-args" -is-function-like -old-name "aFunc" >> %t.ranges/functions_no-args.swift +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="no-args" -is-function-like -old-name "aFunc()" >> %t.ranges/functions_no-args.swift // RUN: diff -u %S/Outputs/functions/no-args.swift.expected %t.ranges/functions_no-args.swift // RUN: %refactor -find-rename-ranges -source-filename %s -pos="param-label" -is-function-like -old-name "aFunc(a:)" >> %t.ranges/functions_param-label.swift // RUN: diff -u %S/Outputs/functions/param-label.swift.expected %t.ranges/functions_param-label.swift diff --git a/test/refactoring/SyntacticRename/textual.swift b/test/refactoring/SyntacticRename/textual.swift index d94c6997d4e44..42490104c18af 100644 --- a/test/refactoring/SyntacticRename/textual.swift +++ b/test/refactoring/SyntacticRename/textual.swift @@ -38,7 +38,7 @@ _ = /*MyClass:unknown*/MyClass() // REQUIRES: swift_swift_parser // RUN: %empty-directory(%t.ranges) -// RUN: %refactor -find-rename-ranges -source-filename %s -pos="foo" -is-function-like -old-name "foo" >> %t.ranges/textual_foo.swift +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="foo" -is-function-like -old-name "foo()" >> %t.ranges/textual_foo.swift // RUN: diff -u %S/Outputs/textual/foo.swift.expected %t.ranges/textual_foo.swift // RUN: %refactor -find-rename-ranges -source-filename %s -pos="MyClass" -is-non-protocol-type -old-name "MyClass" -new-name "YourClass" >> %t.ranges/textual_MyClass.swift // RUN: diff -u %S/Outputs/textual/MyClass.swift.expected %t.ranges/textual_MyClass.swift diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 2a258784606e1..0f84cc9a74d79 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -19,6 +19,7 @@ #include "swift/AST/Type.h" #include "swift/IDE/CancellableResult.h" #include "swift/IDE/CodeCompletionResult.h" +#include "swift/Refactoring/RenameLoc.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/Optional.h" @@ -37,6 +38,7 @@ namespace llvm { namespace SourceKit { class GlobalConfig; using swift::ide::CancellableResult; +using swift::ide::RenameLoc; struct EntityInfo { UIdent Kind; @@ -781,9 +783,9 @@ struct SemanticRefactoringInfo { StringRef PreferredName; }; -struct RelatedIdentsInfo { - /// (Offset,Length) pairs. - ArrayRef> Ranges; +struct RelatedIdentInfo { + unsigned Offset; + unsigned Length; }; /// Represent one branch of an if config. @@ -893,25 +895,6 @@ struct CategorizedRenameRanges { std::vector Ranges; }; -enum class RenameType { - Unknown, - Definition, - Reference, - Call -}; - -struct RenameLocation { - unsigned Line; - unsigned Column; - RenameType Type; -}; - -struct RenameLocations { - StringRef OldName; - const bool IsFunctionLike; - std::vector LineColumnLocs; -}; - struct IndexStoreOptions { std::string IndexStorePath; std::string IndexUnitOutputPath; @@ -1170,7 +1153,7 @@ class LangSupport { StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, bool CancelOnSubsequentRequest, ArrayRef Args, SourceKitCancellationToken CancellationToken, - std::function &)> + std::function> &)> Receiver) = 0; virtual void findActiveRegionsInFile( @@ -1192,7 +1175,7 @@ class LangSupport { virtual CancellableResult> findRenameRanges(llvm::MemoryBuffer *InputBuf, - ArrayRef RenameLocations, + ArrayRef RenameLocations, ArrayRef Args) = 0; virtual void findLocalRenameRanges(StringRef Filename, unsigned Line, unsigned Column, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index ede366882da2a..eabbac6ed4121 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -1344,22 +1344,6 @@ void RequestRefactoringEditConsumer::handleDiagnostic( Impl.DiagConsumer.handleDiagnostic(SM, Info); } -static NameUsage getNameUsage(RenameType Type) { - switch (Type) { - case RenameType::Definition: - return NameUsage::Definition; - case RenameType::Reference: - return NameUsage::Reference; - case RenameType::Call: - return NameUsage::Call; - case RenameType::Unknown: - return NameUsage::Unknown; - } -} - -static std::vector -getSyntacticRenameLocs(ArrayRef RenameLocations); - /// Translates a vector of \c SyntacticRenameRangeDetails to a vector of /// \c CategorizedRenameRanges. static std::vector getCategorizedRenameRanges( @@ -1390,7 +1374,7 @@ static std::vector getCategorizedRenameRanges( CancellableResult> SwiftLangSupport::findRenameRanges(llvm::MemoryBuffer *InputBuf, - ArrayRef RenameLocations, + ArrayRef RenameLocs, ArrayRef Args) { using ResultType = CancellableResult>; std::string Error; @@ -1402,7 +1386,6 @@ SwiftLangSupport::findRenameRanges(llvm::MemoryBuffer *InputBuf, return ResultType::failure(Error); } - auto RenameLocs = getSyntacticRenameLocs(RenameLocations); auto SyntacticRenameRanges = swift::ide::findSyntacticRenameRanges(SF, RenameLocs, /*NewName=*/StringRef()); @@ -1503,19 +1486,6 @@ SourceFile *SwiftLangSupport::getSyntacticSourceFile( return SF; } -static std::vector -getSyntacticRenameLocs(ArrayRef RenameLocations) { - std::vector RenameLocs; - for(const auto &Locations: RenameLocations) { - for(const auto &Location: Locations.LineColumnLocs) { - RenameLocs.push_back({Location.Line, Location.Column, - getNameUsage(Location.Type), Locations.OldName, - Locations.IsFunctionLike}); - } - } - return RenameLocs; -} - void SwiftLangSupport::getDocInfo(llvm::MemoryBuffer *InputBuf, StringRef ModuleName, ArrayRef Args, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 33eec283593b8..5f7aa1efe96c9 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -684,8 +684,8 @@ class SwiftLangSupport : public LangSupport { StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, bool CancelOnSubsequentRequest, ArrayRef Args, SourceKitCancellationToken CancellationToken, - std::function &)> Receiver) - override; + std::function> &)> + Receiver) override; void findActiveRegionsInFile( StringRef PrimaryFilePath, StringRef InputBufferName, @@ -695,7 +695,7 @@ class SwiftLangSupport : public LangSupport { CancellableResult> findRenameRanges(llvm::MemoryBuffer *InputBuf, - ArrayRef RenameLocations, + ArrayRef RenameLocations, ArrayRef Args) override; void findLocalRenameRanges(StringRef Filename, unsigned Line, unsigned Column, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 71bb44cb969df..5af9e32369533 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -2493,27 +2493,30 @@ void SwiftLangSupport::findRelatedIdentifiersInFile( StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, bool CancelOnSubsequentRequest, ArrayRef Args, SourceKitCancellationToken CancellationToken, - std::function &)> Receiver) { + std::function> &)> + Receiver) { std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); - Receiver(RequestResult::fromError(Error)); + Receiver(RequestResult>::fromError(Error)); return; } class RelatedIdConsumer : public SwiftASTConsumer { std::string InputFile; unsigned Offset; - std::function &)> Receiver; + std::function> &)> + Receiver; SwiftInvocationRef Invok; public: RelatedIdConsumer( StringRef InputFile, unsigned Offset, - std::function &)> Receiver, + std::function> &)> + Receiver, SwiftInvocationRef Invok) : InputFile(InputFile.str()), Offset(Offset), Receiver(std::move(Receiver)), Invok(Invok) {} @@ -2528,12 +2531,12 @@ void SwiftLangSupport::findRelatedIdentifiersInFile( auto *SrcFile = retrieveInputFile(InputFile, CompInst); if (!SrcFile) { - Receiver(RequestResult::fromError( + Receiver(RequestResult>::fromError( "Unable to find input file")); return; } - SmallVector, 8> Ranges; + SmallVector Ranges; auto Action = [&]() { unsigned BufferID = SrcFile->getBufferID().value(); @@ -2583,29 +2586,28 @@ void SwiftLangSupport::findRelatedIdentifiersInFile( std::vector ResolvedLocs = resolveRenameLocations( Locs.getLocations(), /*NewName=*/StringRef(), *SrcFile, Diags); + assert(ResolvedLocs.size() == Locs.getLocations().size()); for (auto ResolvedLoc : ResolvedLocs) { if (ResolvedLoc.range.isInvalid()) { continue; } unsigned Offset = SrcMgr.getLocOffsetInBuffer( ResolvedLoc.range.getStart(), BufferID); - Ranges.emplace_back(Offset, ResolvedLoc.range.getByteLength()); + Ranges.push_back({Offset, ResolvedLoc.range.getByteLength()}); } }; Action(); - RelatedIdentsInfo Info; - Info.Ranges = Ranges; - Receiver(RequestResult::fromResult(Info)); + Receiver(RequestResult>::fromResult(Ranges)); #endif } void cancelled() override { - Receiver(RequestResult::cancelled()); + Receiver(RequestResult>::cancelled()); } void failed(StringRef Error) override { LOG_WARN_FUNC("related idents failed: " << Error); - Receiver(RequestResult::fromError(Error)); + Receiver(RequestResult>::fromError(Error)); } static CaseStmt *getCaseStmtOfCanonicalVar(Decl *D) { diff --git a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp index 8dd98fd9a38fb..5ca26ea6aa076 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp @@ -311,7 +311,7 @@ editorFindModuleGroups(StringRef ModuleName, ArrayRef Args); static bool buildRenameLocationsFromDict(const RequestDict &Req, - std::vector &RenameLocations, + std::vector &RenameLocations, llvm::SmallString<64> &Error); static sourcekitd_response_t @@ -323,7 +323,7 @@ static sourcekitd_response_t createCategorizedRenameRangesResponse( static sourcekitd_response_t findRenameRanges(llvm::MemoryBuffer *InputBuf, - ArrayRef RenameLocations, + ArrayRef RenameLocations, ArrayRef Args); static bool isSemanticEditorDisabled(); @@ -1069,7 +1069,7 @@ handleRequestFindRenameRanges(const RequestDict &Req, return; SmallString<64> ErrBuf; - std::vector RenameLocations; + std::vector RenameLocations; if (buildRenameLocationsFromDict(Req, RenameLocations, ErrBuf)) return Rec(createErrorRequestFailed(ErrBuf.c_str())); return Rec(findRenameRanges(InputBuf.get(), RenameLocations, Args)); @@ -2867,20 +2867,21 @@ static void findRelatedIdents(StringRef PrimaryFilePath, LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.findRelatedIdentifiersInFile( PrimaryFilePath, InputBufferName, Offset, CancelOnSubsequentRequest, Args, - CancellationToken, [Rec](const RequestResult &Result) { + CancellationToken, + [Rec](const RequestResult> &Result) { if (Result.isCancelled()) return Rec(createErrorRequestCancelled()); if (Result.isError()) return Rec(createErrorRequestFailed(Result.getError())); - const RelatedIdentsInfo &Info = Result.value(); + const ArrayRef &Info = Result.value(); ResponseBuilder RespBuilder; auto Arr = RespBuilder.getDictionary().setArray(KeyResults); - for (auto R : Info.Ranges) { + for (auto R : Info) { auto Elem = Arr.appendDictionary(); - Elem.set(KeyOffset, R.first); - Elem.set(KeyLength, R.second); + Elem.set(KeyOffset, R.Offset); + Elem.set(KeyLength, R.Length); } Rec(RespBuilder.createResponse()); @@ -3995,25 +3996,16 @@ editorFindModuleGroups(StringRef ModuleName, ArrayRef Args) { static bool buildRenameLocationsFromDict(const RequestDict &Req, - std::vector &RenameLocations, + std::vector &RenameLocations, llvm::SmallString<64> &Error) { bool Failed = Req.dictionaryArrayApply(KeyRenameLocations, [&](RequestDict RenameLocation) { - int64_t IsFunctionLike = false; - if (RenameLocation.getInt64(KeyIsFunctionLike, IsFunctionLike, false)) { - Error = "missing key.is_function_like"; - return true; - } - Optional OldName = RenameLocation.getString(KeyName); if (!OldName.has_value()) { Error = "missing key.name"; return true; } - RenameLocations.push_back( - {*OldName, static_cast(IsFunctionLike), {}}); - auto &LineCols = RenameLocations.back().LineColumnLocs; bool Failed = RenameLocation.dictionaryArrayApply(KeyLocations, [&](RequestDict LineAndCol) { int64_t Line = 0; @@ -4033,19 +4025,21 @@ buildRenameLocationsFromDict(const RequestDict &Req, Error = "missing key.nametype"; return true; } - RenameType RenameType = RenameType::Unknown; + using swift::ide::RenameLocUsage; + RenameLocUsage RenameType = RenameLocUsage::Unknown; if (NameType == KindDefinition) { - RenameType = RenameType::Definition; + RenameType = RenameLocUsage::Definition; } else if (NameType == KindReference) { - RenameType = RenameType::Reference; + RenameType = RenameLocUsage::Reference; } else if (NameType == KindCall) { - RenameType = RenameType::Call; + RenameType = RenameLocUsage::Call; } else if (NameType != KindUnknown) { Error = "invalid value for 'key.nametype'"; return true; } - LineCols.push_back({static_cast(Line), - static_cast(Column), RenameType}); + RenameLocations.emplace_back( + static_cast(Line), static_cast(Column), + RenameType, *OldName); return false; }); if (Failed && Error.empty()) { @@ -4144,7 +4138,7 @@ static sourcekitd_response_t createCategorizedRenameRangesResponse( static sourcekitd_response_t findRenameRanges(llvm::MemoryBuffer *InputBuf, - ArrayRef RenameLocations, + ArrayRef RenameLocations, ArrayRef Args) { LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); CancellableResult> ReqResult = diff --git a/tools/swift-refactor/swift-refactor.cpp b/tools/swift-refactor/swift-refactor.cpp index 06e9fb3bd7c82..92e9c1c1ad507 100644 --- a/tools/swift-refactor/swift-refactor.cpp +++ b/tools/swift-refactor/swift-refactor.cpp @@ -176,18 +176,18 @@ bool doesFileExist(StringRef Path) { struct RefactorLoc { unsigned Line; unsigned Column; - NameUsage Usage; + RenameLocUsage Usage; }; -NameUsage convertToNameUsage(StringRef RoleString) { +RenameLocUsage convertToNameUsage(StringRef RoleString) { if (RoleString == "unknown") - return NameUsage::Unknown; + return RenameLocUsage::Unknown; if (RoleString == "def") - return NameUsage::Definition; + return RenameLocUsage::Definition; if (RoleString == "ref") - return NameUsage::Reference; + return RenameLocUsage::Reference; if (RoleString == "call") - return NameUsage::Call; + return RenameLocUsage::Call; llvm_unreachable("unhandled role string"); } @@ -201,8 +201,8 @@ std::vector getLocsByLabelOrPosition(StringRef LabelOrLineCol, if (LabelOrLineCol.contains(':')) { auto LineCol = parseLineCol(LabelOrLineCol); if (LineCol.has_value()) { - LocResults.push_back({LineCol.value().first,LineCol.value().second, - NameUsage::Unknown}); + LocResults.push_back({LineCol.value().first, LineCol.value().second, + RenameLocUsage::Unknown}); } else { llvm::errs() << "cannot parse position pair."; } @@ -231,7 +231,7 @@ std::vector getLocsByLabelOrPosition(StringRef LabelOrLineCol, unsigned ColumnOffset = 0; if (Matches[2].length() > 0 && !llvm::to_integer(Matches[2].str(), ColumnOffset)) continue; // bad column offset - auto Usage = NameUsage::Reference; + auto Usage = RenameLocUsage::Reference; if (Matches[3].length() > 0) Usage = convertToNameUsage(Matches[3].str()); LocResults.push_back({Line, Column + ColumnOffset, Usage}); @@ -242,14 +242,12 @@ std::vector getLocsByLabelOrPosition(StringRef LabelOrLineCol, std::vector getRenameLocs(unsigned BufferID, SourceManager &SM, ArrayRef Locs, - StringRef OldName, StringRef NewName, - bool IsFunctionLike) { + StringRef OldName, StringRef NewName) { std::vector Renames; - llvm::transform( - Locs, std::back_inserter(Renames), - [&](const RefactorLoc &Loc) -> RenameLoc { - return {Loc.Line, Loc.Column, Loc.Usage, OldName, IsFunctionLike}; - }); + llvm::transform(Locs, std::back_inserter(Renames), + [&](const RefactorLoc &Loc) -> RenameLoc { + return {Loc.Line, Loc.Column, Loc.Usage, OldName}; + }); return Renames; } @@ -451,8 +449,7 @@ int main(int argc, char *argv[]) { } std::vector RenameLocs = - getRenameLocs(BufferID, SM, Start, options::OldName, NewName, - options::IsFunctionLike.getNumOccurrences()); + getRenameLocs(BufferID, SM, Start, options::OldName, NewName); if (options::Action != RefactoringKind::FindGlobalRenameRanges) { llvm_unreachable("unexpected refactoring kind");