diff --git a/include/swift/IDE/Refactoring.h b/include/swift/IDE/Refactoring.h index 35c39d9ba4521..0f609fd648335 100644 --- a/include/swift/IDE/Refactoring.h +++ b/include/swift/IDE/Refactoring.h @@ -141,8 +141,17 @@ collectAvailableRefactorings(SourceFile *SF, ResolvedCursorInfo CursorInfo, std::vector &Scratch, bool ExcludeRename); +/// Stores information about the reference that rename availability is being +/// queried on. +struct RenameRefInfo { + SourceFile *SF; ///< The source file containing the reference. + SourceLoc Loc; ///< The reference's source location. + bool IsArgLabel; ///< Whether Loc is on an arg label, rather than base name. +}; + ArrayRef collectRenameAvailabilityInfo(const ValueDecl *VD, + Optional RefInfo, std::vector &Scratch); } // namespace ide diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 89936350ed971..87a2b71978e60 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -48,6 +48,7 @@ namespace swift { class Type; class Decl; class DeclContext; + class CallExpr; class ClangNode; class ClangImporter; class Token; @@ -226,6 +227,12 @@ struct ResolvedLoc { bool IsInSelector; }; +/// Used by NameMatcher to track parent CallExprs when walking a checked AST. +struct CallingParent { + Expr *ApplicableTo; + CallExpr *Call; +}; + /// Finds the parse-only AST nodes and corresponding name and param/argument /// label ranges for a given list of input name start locations @@ -244,6 +251,9 @@ class NameMatcher: public ASTWalker { unsigned InactiveConfigRegionNestings = 0; unsigned SelectorNestings = 0; + /// The stack of parent CallExprs and the innermost expression they apply to. + std::vector ParentCalls; + SourceManager &getSourceMgr() const; SourceLoc nextLoc() const; @@ -256,11 +266,11 @@ class NameMatcher: public ASTWalker { bool shouldSkip(SourceRange Range); bool shouldSkip(CharSourceRange Range); bool tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc); - bool tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, Expr *Arg, - bool checkParentForLabels = false); + bool tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, Expr *Arg); bool tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc, LabelRangeType RangeType, ArrayRef LabelLocs); bool handleCustomAttrs(Decl *D); + Expr *getApplicableArgFor(Expr* E); std::pair walkToExprPre(Expr *E) override; Expr* walkToExprPost(Expr *E) override; @@ -281,6 +291,7 @@ class NameMatcher: public ASTWalker { public: explicit NameMatcher(SourceFile &SrcFile) : SrcFile(SrcFile) { } std::vector resolve(ArrayRef Locs, ArrayRef Tokens); + ResolvedLoc resolve(UnresolvedLoc Loc); }; enum class RangeKind : int8_t { diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 5d45953d944c7..6c3650bd28542 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -316,21 +316,35 @@ class Renamer { // FIXME: handle escaped keyword names `init` bool IsSubscript = Old.base() == "subscript" && Config.IsFunctionLike; bool IsInit = Old.base() == "init" && Config.IsFunctionLike; - bool IsKeywordBase = IsInit || IsSubscript; + + // FIXME: this should only be treated specially for instance methods. + bool IsCallAsFunction = Old.base() == "callAsFunction" && + Config.IsFunctionLike; + + bool IsSpecialBase = IsInit || IsSubscript || IsCallAsFunction; - // Filter out non-semantic keyword basename locations with no labels. + // 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' or 'subscript' in strings, comments, and - // inactive code. - if (IsKeywordBase && (Config.Usage == NameUsage::Unknown && - Resolved.LabelType == LabelRangeType::None)) + // 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 (!Config.IsFunctionLike || !IsKeywordBase) { + if (!Config.IsFunctionLike || !IsSpecialBase) { if (renameBase(Resolved.Range, RefactoringRangeKind::BaseName)) return RegionType::Mismatch; - } else if (IsKeywordBase && Config.Usage == NameUsage::Definition) { + } 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) { + return RegionType::Mismatch; + } + } + } else if (IsSubscript && Config.Usage == NameUsage::Definition) { if (renameBase(Resolved.Range, RefactoringRangeKind::KeywordBaseName)) return RegionType::Mismatch; } @@ -528,9 +542,11 @@ static const ValueDecl *getRelatedSystemDecl(const ValueDecl *VD) { return nullptr; } -static Optional getAvailableRenameForDecl(const ValueDecl *VD) { +static Optional +getAvailableRenameForDecl(const ValueDecl *VD, + Optional RefInfo) { std::vector Scratch; - for (auto &Info : collectRenameAvailabilityInfo(VD, Scratch)) { + for (auto &Info : collectRenameAvailabilityInfo(VD, RefInfo, Scratch)) { if (Info.AvailableKind == RenameAvailableKind::Available) return Info.Kind; } @@ -744,15 +760,21 @@ bool RefactoringActionLocalRename:: isApplicable(ResolvedCursorInfo CursorInfo, DiagnosticEngine &Diag) { if (CursorInfo.Kind != CursorInfoKind::ValueRef) return false; - auto RenameOp = getAvailableRenameForDecl(CursorInfo.ValueD); + + Optional RefInfo; + if (CursorInfo.IsRef) + RefInfo = {CursorInfo.SF, CursorInfo.Loc, CursorInfo.IsKeywordArgument}; + + auto RenameOp = getAvailableRenameForDecl(CursorInfo.ValueD, RefInfo); return RenameOp.hasValue() && RenameOp.getValue() == RefactoringKind::LocalRename; } -static void analyzeRenameScope(ValueDecl *VD, DiagnosticEngine &Diags, +static void analyzeRenameScope(ValueDecl *VD, Optional RefInfo, + DiagnosticEngine &Diags, llvm::SmallVectorImpl &Scopes) { Scopes.clear(); - if (!getAvailableRenameForDecl(VD).hasValue()) { + if (!getAvailableRenameForDecl(VD, RefInfo).hasValue()) { Diags.diagnose(SourceLoc(), diag::value_decl_no_loc, VD->getFullName()); return; } @@ -786,7 +808,12 @@ bool RefactoringActionLocalRename::performChange() { if (CursorInfo.isValid() && CursorInfo.ValueD) { ValueDecl *VD = CursorInfo.CtorTyRef ? CursorInfo.CtorTyRef : CursorInfo.ValueD; llvm::SmallVector Scopes; - analyzeRenameScope(VD, DiagEngine, Scopes); + + Optional RefInfo; + if (CursorInfo.IsRef) + RefInfo = {CursorInfo.SF, CursorInfo.Loc, CursorInfo.IsKeywordArgument}; + + analyzeRenameScope(VD, RefInfo, DiagEngine, Scopes); if (Scopes.empty()) return true; RenameRangeCollector rangeCollector(VD, PreferredName); @@ -3623,9 +3650,11 @@ accept(SourceManager &SM, RegionType RegionType, Impl.accept(SM, Range); } } + ArrayRef swift::ide::collectRenameAvailabilityInfo(const ValueDecl *VD, - std::vector &Scratch) { + Optional RefInfo, + std::vector &Scratch) { RenameAvailableKind AvailKind = RenameAvailableKind::Available; if (getRelatedSystemDecl(VD)){ AvailKind = RenameAvailableKind::Unavailable_system_symbol; @@ -3650,6 +3679,30 @@ swift::ide::collectRenameAvailabilityInfo(const ValueDecl *VD, if (auto CD = dyn_cast(VD)) { if (!CD->getParameters()->size()) return Scratch; + + if (RefInfo && !RefInfo->IsArgLabel) { + NameMatcher Matcher(*(RefInfo->SF)); + auto Resolved = Matcher.resolve({RefInfo->Loc, /*ResolveArgs*/true}); + if (Resolved.LabelRanges.empty()) + return Scratch; + } + } + + // Disallow renaming 'callAsFunction' method with no arguments. + if (auto FD = dyn_cast(VD)) { + // FIXME: syntactic rename can only decide by checking the spelling, not + // whether it's an instance method, so we do the same here for now. + if (FD->getBaseIdentifier() == FD->getASTContext().Id_callAsFunction) { + if (!FD->getParameters()->size()) + return Scratch; + + if (RefInfo && !RefInfo->IsArgLabel) { + NameMatcher Matcher(*(RefInfo->SF)); + auto Resolved = Matcher.resolve({RefInfo->Loc, /*ResolveArgs*/true}); + if (Resolved.LabelRanges.empty()) + return Scratch; + } + } } } @@ -3682,7 +3735,10 @@ collectAvailableRefactorings(SourceFile *SF, case CursorInfoKind::ExprStart: break; case CursorInfoKind::ValueRef: { - auto RenameOp = getAvailableRenameForDecl(CursorInfo.ValueD); + Optional RefInfo; + if (CursorInfo.IsRef) + RefInfo = {CursorInfo.SF, CursorInfo.Loc, CursorInfo.IsKeywordArgument}; + auto RenameOp = getAvailableRenameForDecl(CursorInfo.ValueD, RefInfo); if (RenameOp.hasValue() && RenameOp.getValue() == RefactoringKind::GlobalRename) AllKinds.push_back(RenameOp.getValue()); @@ -3919,8 +3975,12 @@ int swift::ide::findLocalRenameRanges( return true; } ValueDecl *VD = CursorInfo.CtorTyRef ? CursorInfo.CtorTyRef : CursorInfo.ValueD; + Optional RefInfo; + if (CursorInfo.IsRef) + RefInfo = {CursorInfo.SF, CursorInfo.Loc, CursorInfo.IsKeywordArgument}; + llvm::SmallVector Scopes; - analyzeRenameScope(VD, Diags, Scopes); + analyzeRenameScope(VD, RefInfo, Diags, Scopes); if (Scopes.empty()) return true; RenameRangeCollector RangeCollector(VD, StringRef()); diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 1e8a8b3194efe..3e8e5cf0ad25d 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -86,10 +86,15 @@ class SemaAnnotator : public ASTWalker { bool shouldIgnore(Decl *D, bool &ShouldVisitChildren); ValueDecl *extractDecl(Expr *Fn) const { + Fn = Fn->getSemanticsProvidingExpr(); if (auto *DRE = dyn_cast(Fn)) return DRE->getDecl(); if (auto ApplyE = dyn_cast(Fn)) return extractDecl(ApplyE->getFn()); + if (auto *ACE = dyn_cast(Fn)) { + if (auto *Unwrapped = ACE->getUnwrappedCurryThunkExpr()) + return extractDecl(Unwrapped); + } return nullptr; } }; diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index a60e6ab70e276..cd9b70d1331a8 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -72,6 +72,10 @@ SourceManager &NameMatcher::getSourceMgr() const { return SrcFile.getASTContext().SourceMgr; } +ResolvedLoc NameMatcher::resolve(UnresolvedLoc Loc) { + return resolve({Loc}, {}).front(); +} + std::vector NameMatcher::resolve(ArrayRef Locs, ArrayRef Tokens) { // Note the original indices and sort them in reverse source order @@ -275,6 +279,36 @@ Stmt *NameMatcher::walkToStmtPost(Stmt *S) { return S; } +Expr *NameMatcher::getApplicableArgFor(Expr *E) { + if (auto *UME = dyn_cast(E)) { + if (auto *Arg = UME->getArgument()) + return Arg; + } + if (ParentCalls.empty()) + return nullptr; + auto &Last = ParentCalls.back(); + return Last.ApplicableTo == E ? Last.Call->getArg() : nullptr; +} + +static Expr *extractNameExpr(Expr *Fn) { + Fn = Fn->getSemanticsProvidingExpr(); + switch (Fn->getKind()) { + case ExprKind::DeclRef: + case ExprKind::UnresolvedDeclRef: + case ExprKind::UnresolvedMember: + case ExprKind::UnresolvedDot: + return Fn; + default: + break; + } + if (auto *SAE = dyn_cast(Fn)) + return extractNameExpr(SAE->getFn()); + if (auto *ACE = dyn_cast(Fn)) + if (auto *Unwrapped = ACE->getUnwrappedCurryThunkExpr()) + return extractNameExpr(Unwrapped); + return nullptr; +} + std::pair NameMatcher::walkToExprPre(Expr *E) { if (shouldSkip(E)) return std::make_pair(false, isDone()? nullptr : E); @@ -285,21 +319,31 @@ std::pair NameMatcher::walkToExprPre(Expr *E) { // only match name locations of expressions apparent in the original source if (!E->isImplicit()) { + + if (auto *CE = dyn_cast(E)) { + // Keep a stack of parent CallExprs along with the expression their + // arguments belong to. + if (!CE->isImplicit()) { + if (auto *ApplicableExpr = extractNameExpr(CE->getFn())) + ParentCalls.push_back({ApplicableExpr, CE}); + } + } + // 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()); + tryResolve(ASTWalker::ParentTy(E), UME->getNameLoc(), getApplicableArgFor(E)); } break; case ExprKind::DeclRef: { auto DRE = cast(E); - tryResolve(ASTWalker::ParentTy(E), DRE->getNameLoc(), nullptr, true); + tryResolve(ASTWalker::ParentTy(E), DRE->getNameLoc(), getApplicableArgFor(E)); break; } case ExprKind::UnresolvedDeclRef: { auto UDRE = cast(E); - tryResolve(ASTWalker::ParentTy(E), UDRE->getNameLoc(), nullptr, true); + tryResolve(ASTWalker::ParentTy(E), UDRE->getNameLoc(), getApplicableArgFor(E)); break; } case ExprKind::StringLiteral: @@ -327,8 +371,25 @@ std::pair NameMatcher::walkToExprPre(Expr *E) { return {false, nullptr}; return {false, E}; } + case ExprKind::Paren: { + ParenExpr *P = cast(E); + // Handle implicit callAsFunction reference on opening paren + auto Labels = getCallArgLabelRanges(getSourceMgr(), P, + LabelRangeEndAt::BeforeElemStart); + tryResolve(ASTWalker::ParentTy(E), P->getLParenLoc(), + LabelRangeType::CallArg, Labels); + break; + } case ExprKind::Tuple: { TupleExpr *T = cast(E); + // Handle implicit callAsFunction reference on opening paren + auto Labels = getCallArgLabelRanges(getSourceMgr(), T, + LabelRangeEndAt::BeforeElemStart); + tryResolve(ASTWalker::ParentTy(E), T->getLParenLoc(), + LabelRangeType::CallArg, Labels); + if (isDone()) + break; + // Handle arg label locations (the index reports property occurrences // on them for memberwise inits) for (unsigned i = 0, e = T->getNumElements(); i != e; ++i) { @@ -383,12 +444,17 @@ Expr *NameMatcher::walkToExprPost(Expr *E) { break; case ExprKind::UnresolvedDot: { auto UDE = cast(E); - tryResolve(ASTWalker::ParentTy(E), UDE->getNameLoc(), nullptr, true); + tryResolve(ASTWalker::ParentTy(E), UDE->getNameLoc(), getApplicableArgFor(E)); break; } default: break; } + + if (auto *CE = dyn_cast(E)) { + if (!ParentCalls.empty() && ParentCalls.back().Call == CE) + ParentCalls.pop_back(); + } } if (isa(E)) { @@ -517,7 +583,7 @@ std::vector getSelectorLabelRanges(SourceManager &SM, } bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, - Expr *Arg, bool checkParentForLabels) { + Expr *Arg) { if (NameLoc.isInvalid()) return false; @@ -542,16 +608,6 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, 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()); @@ -823,8 +879,11 @@ 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()) { + for (size_t ElemIndex: range(TE->getNumElements())) { + Expr *Elem = TE->getElement(ElemIndex); + if (isa(Elem)) + continue; + SourceLoc LabelStart(Elem->getStartLoc()); SourceLoc LabelEnd(LabelStart); @@ -838,10 +897,10 @@ getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) { ElemIndex == TE->getNumElements() - 1; InfoVec.push_back({getSingleNonImplicitChild(Elem), CharSourceRange(SM, LabelStart, LabelEnd), IsTrailingClosure}); - ++ElemIndex; } } else if (auto *PE = dyn_cast(Arg)) { - if (auto Sub = PE->getSubExpr()) + Expr *Sub = PE->getSubExpr(); + if (Sub && !isa(Sub)) InfoVec.push_back({getSingleNonImplicitChild(Sub), CharSourceRange(Sub->getStartLoc(), 0), PE->hasTrailingClosure() diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index 6a11f56c249fd..0ba2768827de8 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -1345,15 +1345,36 @@ static bool isDynamicCall(Expr *BaseE, ValueDecl *D) { return true; } -static bool isBeingCalled(Expr *Target, Expr *Parent, Expr *GrandParent) { - if (!Target || !Parent || !isa(Parent)) - return false; +static Expr *getUnderlyingFunc(Expr *Fn) { + Fn = Fn->getSemanticsProvidingExpr(); + if (auto *DRE = dyn_cast(Fn)) + return DRE; + if (auto ApplyE = dyn_cast(Fn)) + return getUnderlyingFunc(ApplyE->getFn()); + if (auto *ACE = dyn_cast(Fn)) { + if (auto *Unwrapped = ACE->getUnwrappedCurryThunkExpr()) + return getUnderlyingFunc(Unwrapped); + } + return Fn; +} - if (!isa(Parent)) - return cast(Parent)->getFn() == Target; +static bool isBeingCalled(Expr *Target, ArrayRef ExprStack) { + if (!Target) + return false; + Target = getUnderlyingFunc(Target); - return GrandParent && isa(GrandParent) && - cast(GrandParent)->getFn() == Parent; + for (Expr *E: reverse(ExprStack)) { + auto *AE = dyn_cast(E); + if (!AE || AE->isImplicit()) + continue; + if (isa(AE) && AE->getArg() == Target) + return true; + if (isa(AE)) + continue; + if (getUnderlyingFunc(AE->getFn()) == Target) + return true; + } + return false; } bool IndexSwiftASTWalker::initFuncRefIndexSymbol(ValueDecl *D, SourceLoc Loc, @@ -1368,8 +1389,7 @@ bool IndexSwiftASTWalker::initFuncRefIndexSymbol(ValueDecl *D, SourceLoc Loc, Expr *ParentE = getParentExpr(); - if (!isa(D) && - !isBeingCalled(CurrentE, ParentE, getContainingExpr(2))) + if (!isa(D) && !isBeingCalled(CurrentE, ExprStack)) return false; Info.roles |= (unsigned)SymbolRole::Call; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 7520fb1f8ae6c..950e2773f4db1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7087,8 +7087,9 @@ static Expr *buildCallAsFunctionMethodRef( // Create direct reference to `callAsFunction` method. auto *fn = apply->getFn(); + auto *arg = apply->getArg(); auto *declRef = rewriter.buildMemberRef( - fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(fn->getEndLoc()), + fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(arg->getStartLoc()), calleeLoc, calleeLoc, /*implicit*/ true, /*extraUncurryLevel=*/true, AccessSemantics::Ordinary); if (!declRef) diff --git a/test/Index/index_callasfunction.swift b/test/Index/index_callasfunction.swift index ad2beb6fb2973..7bc34ce2574c8 100644 --- a/test/Index/index_callasfunction.swift +++ b/test/Index/index_callasfunction.swift @@ -6,7 +6,7 @@ struct Adder { // CHECK: [[@LINE-1]]:10 | instance-method/Swift | callAsFunction(_:) | [[callAsFunc1:.*]] | Def return base + x } - func callAsFunction(x: Int, y: Int) -> Int { + func callAsFunction(x: Int, y: Int) -> Adder { // CHECK: [[@LINE-1]]:10 | instance-method/Swift | callAsFunction(x:y:) | [[callAsFunc2:.*]] | Def return base + x + y } @@ -18,11 +18,30 @@ let global = 1 add3(global) // CHECK: [[@LINE-1]]:1 | variable/Swift | add3 | [[add3]] | Ref,Read | -// CHECK: [[@LINE-2]]:1 | instance-method/Swift | callAsFunction(_:) | [[callAsFunc1]] | Ref,Call,RelRec | rel: 1 +// CHECK: [[@LINE-2]]:5 | instance-method/Swift | callAsFunction(_:) | [[callAsFunc1]] | Ref,Call,RelRec | rel: 1 // CHECK: RelRec | struct/Swift | Adder | // CHECK: [[@LINE-4]]:6 | variable/Swift | global | {{.*}} | Ref,Read | add3(x: 10, y: 11) // CHECK: [[@LINE-1]]:1 | variable/Swift | add3 | [[add3]] | Ref,Read | -// CHECK: [[@LINE-2]]:1 | instance-method/Swift | callAsFunction(x:y:) | [[callAsFunc2]] | Ref,Call,RelRec | rel: 1 +// CHECK: [[@LINE-2]]:5 | instance-method/Swift | callAsFunction(x:y:) | [[callAsFunc2]] | Ref,Call,RelRec | rel: 1 +// CHECK: RelRec | struct/Swift | Adder | + +func getAdder(_ base: Int) -> Adder { return Adder(base: base) } +// CHECK: [[@LINE-1]]:6 | function/Swift | getAdder(_:) | [[getAdder:.*]] | Def | rel: 0 + +getAdder(5)(10) +// CHECK: [[@LINE-1]]:1 | function/Swift | getAdder(_:) | [[getAdder]] | Ref,Call | rel: 0 +// CHECK: [[@LINE-2]]:12 | instance-method/Swift | callAsFunction(_:) | [[callAsFunc1]] | Ref,Call,RelRec | rel: 1 +// CHECK: RelRec | struct/Swift | Adder | + +getAdder(5)(x: 1, y: 42) +// CHECK: [[@LINE-1]]:1 | function/Swift | getAdder(_:) | [[getAdder]] | Ref,Call | rel: 0 +// CHECK: [[@LINE-2]]:12 | instance-method/Swift | callAsFunction(x:y:) | [[callAsFunc2]] | Ref,Call,RelRec | rel: 1 +// CHECK: RelRec | struct/Swift | Adder | + +((add3.callAsFunction)(x: 5, y: 10))(x: 1, y: 42) +// CHECK: [[@LINE-1]]:8 | instance-method/Swift | callAsFunction(x:y:) | [[callAsFunc2]] | Ref,Call,RelRec | rel: 1 +// CHECK: RelRec | struct/Swift | Adder | +// CHECK: [[@LINE-3]]:37 | instance-method/Swift | callAsFunction(x:y:) | [[callAsFunc2]] | Ref,Call,RelRec | rel: 1 // CHECK: RelRec | struct/Swift | Adder | diff --git a/test/Index/roles.swift b/test/Index/roles.swift index f35988d72062e..55a5ef83b6858 100644 --- a/test/Index/roles.swift +++ b/test/Index/roles.swift @@ -16,19 +16,23 @@ let x = 2 var y = x + 1 // CHECK: [[@LINE-1]]:5 | variable/Swift | y | s:14swift_ide_test1ySivp | Def | rel: 0 // CHECK: [[@LINE-2]]:9 | variable/Swift | x | s:14swift_ide_test1xSivp | Ref,Read | rel: 0 -// CHECK: [[@LINE-3]]:11 | static-method/infix-operator/Swift | +(_:_:) | s:Si1poiyS2i_SitFZ | Ref | rel: 0 +// CHECK: [[@LINE-3]]:11 | static-method/infix-operator/Swift | +(_:_:) | s:Si1poiyS2i_SitFZ | Ref,Call,RelRec | rel: 1 +// CHECK-NEXT: RelRec | struct/Swift | Int | s:Si // Read of x + Write of y y = x + 1 // CHECK: [[@LINE-1]]:1 | variable/Swift | y | s:14swift_ide_test1ySivp | Ref,Writ | rel: 0 // CHECK: [[@LINE-2]]:5 | variable/Swift | x | s:14swift_ide_test1xSivp | Ref,Read | rel: 0 -// CHECK: [[@LINE-3]]:7 | static-method/infix-operator/Swift | +(_:_:) | s:Si1poiyS2i_SitFZ | Ref | rel: 0 +// CHECK: [[@LINE-3]]:7 | static-method/infix-operator/Swift | +(_:_:) | s:Si1poiyS2i_SitFZ | Ref,Call,RelRec | rel: 1 +// CHECK-NEXT: RelRec | struct/Swift | Int | s:Si + // Read of y + Write of y y += x // CHECK: [[@LINE-1]]:1 | variable/Swift | y | s:14swift_ide_test1ySivp | Ref,Read,Writ | rel: 0 -// CHECK: [[@LINE-2]]:3 | static-method/infix-operator/Swift | +=(_:_:) | s:Si2peoiyySiz_SitFZ | Ref | rel: 0 -// CHECK: [[@LINE-3]]:6 | variable/Swift | x | s:14swift_ide_test1xSivp | Ref,Read | rel: 0 +// CHECK: [[@LINE-2]]:3 | static-method/infix-operator/Swift | +=(_:_:) | s:Si2peoiyySiz_SitFZ | Ref,Call,RelRec | rel: 1 +// CHECK-NEXT: RelRec | struct/Swift | Int | s:Si +// CHECK: [[@LINE-4]]:6 | variable/Swift | x | s:14swift_ide_test1xSivp | Ref,Read | rel: 0 var z: Int { // CHECK: [[@LINE-1]]:5 | variable/Swift | z | s:14swift_ide_test1zSivp | Def | rel: 0 @@ -118,8 +122,9 @@ struct AStruct { // CHECK: [[@LINE-6]]:5 | instance-method/acc-set/Swift | setter:x | s:14swift_ide_test7AStructV1xSivs | Ref,Call,Impl,RelRec,RelCall,RelCont | rel: 2 // CHECK-NEXT: RelCall,RelCont | instance-method/Swift | aMethod() | s:14swift_ide_test7AStructV7aMethodyyF // CHECK-NEXT: RelRec | struct/Swift | AStruct | [[AStruct_USR]] - // CHECK: [[@LINE-9]]:7 | static-method/infix-operator/Swift | +=(_:_:) | s:Si2peoiyySiz_SitFZ | Ref,RelCont | rel: 1 - // CHECK-NEXT: RelCont | instance-method/Swift | aMethod() | s:14swift_ide_test7AStructV7aMethodyyF + // CHECK: [[@LINE-9]]:7 | static-method/infix-operator/Swift | +=(_:_:) | s:Si2peoiyySiz_SitFZ | Ref,Call,RelRec,RelCall,RelCont | rel: 2 + // CHECK-NEXT: RelCall,RelCont | instance-method/Swift | aMethod() | s:14swift_ide_test7AStructV7aMethodyyF + // CHECK-NEXT: RelRec | struct/Swift | Int | s:Si } // RelationChildOf, RelationAccessorOf diff --git a/test/Sema/call_as_function_simple.swift b/test/Sema/call_as_function_simple.swift index 153b838447b74..db69685e8d1ac 100644 --- a/test/Sema/call_as_function_simple.swift +++ b/test/Sema/call_as_function_simple.swift @@ -61,10 +61,18 @@ extension Extended { func callAsFunction() -> Extended { return self } + + func callAsFunction(_: Int) -> Extended { + return self + } } var extended = Extended() extended()().callAsFunction()() +// Test diagnostic location +extended()().callAsFunction()(1) // expected-warning@:30 {{result of call to 'callAsFunction' is unused}} +extended()().callAsFunction(1) // expected-warning@:14 {{result of call to 'callAsFunction' is unused}} + struct TakesTrailingClosure { func callAsFunction(_ fn: () -> Void) { fn() diff --git a/test/SourceKit/Indexing/index.swift.response b/test/SourceKit/Indexing/index.swift.response index 02bdd9f9d044b..6efdf4b3a75ff 100644 --- a/test/SourceKit/Indexing/index.swift.response +++ b/test/SourceKit/Indexing/index.swift.response @@ -808,7 +808,8 @@ key.name: "+(_:_:)", key.usr: , key.line: 86, - key.column: 8 + key.column: 8, + key.receiver_usr: "s:Si" } ] }, diff --git a/test/SourceKit/Indexing/index_operators.swift.response b/test/SourceKit/Indexing/index_operators.swift.response index be3484a83274c..20027550b270d 100644 --- a/test/SourceKit/Indexing/index_operators.swift.response +++ b/test/SourceKit/Indexing/index_operators.swift.response @@ -108,7 +108,8 @@ key.name: "-(_:_:)", key.usr: "s:15index_operators7StructBV1soiyA2C_ACtFZ", key.line: 17, - key.column: 19 + key.column: 19, + key.receiver_usr: "s:15index_operators7StructBV" } ] }, diff --git a/test/SourceKit/Refactoring/basic.swift b/test/SourceKit/Refactoring/basic.swift index 2a0f0d439a3ae..aa7826a840a46 100644 --- a/test/SourceKit/Refactoring/basic.swift +++ b/test/SourceKit/Refactoring/basic.swift @@ -78,6 +78,34 @@ func foo7() -> String { foo6() } +struct Test { + init(x: Int = 42) {} + func callAsFunction(x: Int = 42) {} +} + +Test.init +Test.init() +Test.init(x:) +Test.init(x: 3) + +let callable = Test(); +callable(x: 89) +callable.callAsFunction +callable.callAsFunction() +callable.callAsFunction(x:) +callable.callAsFunction(x: 78) +(callable.callAsFunction)(x: 78) +(callable.callAsFunction)() + +func foo(_ x: Int) -> Test { fatalError() } +foo(39)(x: 90) + +struct TestDefaultedParen { + init(_: Int = 42) {} +} + +TestDefaultedParen.init() + // RUN: %sourcekitd-test -req=cursor -pos=3:1 -end-pos=5:13 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK1 // CHECK1: ACTIONS BEGIN @@ -92,6 +120,25 @@ func foo7() -> String { // RUN: %sourcekitd-test -req=cursor -pos=26:20 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL // RUN: %sourcekitd-test -req=cursor -pos=27:11 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-LOCAL +// RUN: %sourcekitd-test -req=cursor -pos=83:8 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=86:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NORENAME +// RUN: %sourcekitd-test -req=cursor -pos=87:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NORENAME +// RUN: %sourcekitd-test -req=cursor -pos=88:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=89:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=89:11 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL + +// RUN: %sourcekitd-test -req=cursor -pos=92:10 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=93:10 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NORENAME +// RUN: %sourcekitd-test -req=cursor -pos=94:10 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NORENAME +// RUN: %sourcekitd-test -req=cursor -pos=95:10 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=96:10 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=96:25 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=97:11 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=97:27 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=98:11 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NORENAME + +// RUN: %sourcekitd-test -req=cursor -pos=107:20 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NORENAME + // RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT // RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT @@ -106,6 +153,9 @@ func foo7() -> String { // RUN: %sourcekitd-test -req=cursor -pos=72:5 -end-pos=72:11 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT // RUN: %sourcekitd-test -req=cursor -pos=78:3 -end-pos=78:9 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT +// CHECK-NORENAME-NOT: Global Rename +// CHECK-NORENAME-NOT: Local Rename + // CHECK2: ACTIONS BEGIN // CHECK2-NEXT: source.refactoring.kind.rename.global // CHECK2-NEXT: Global Rename diff --git a/test/SourceKit/Refactoring/find-rename-ranges/keywordbase.expected b/test/SourceKit/Refactoring/find-rename-ranges/keywordbase.expected index 136b2c6c5ddd1..172298be67de0 100644 --- a/test/SourceKit/Refactoring/find-rename-ranges/keywordbase.expected +++ b/test/SourceKit/Refactoring/find-rename-ranges/keywordbase.expected @@ -2,6 +2,7 @@ source.edit.kind.active: 95:17-95:18 source.refactoring.range.kind.call-argument-label arg-index=0 95:18-95:20 source.refactoring.range.kind.call-argument-colon arg-index=0 source.edit.kind.active: + 96:17-96:21 source.refactoring.range.kind.keyword-basename 96:22-96:23 source.refactoring.range.kind.call-argument-label arg-index=0 96:23-96:25 source.refactoring.range.kind.call-argument-colon arg-index=0 source.edit.kind.unknown: @@ -16,6 +17,8 @@ source.edit.kind.unknown: source.edit.kind.unknown: source.edit.kind.unknown: source.edit.kind.inactive: + 103:17-103:21 source.refactoring.range.kind.keyword-basename 103:22-103:23 source.refactoring.range.kind.call-argument-label arg-index=0 103:23-103:25 source.refactoring.range.kind.call-argument-colon arg-index=0 source.edit.kind.unknown: + 104:17-104:21 source.refactoring.range.kind.keyword-basename diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function-base.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function-base.swift.expected new file mode 100644 index 0000000000000..dbdecfa7f422f --- /dev/null +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function-base.swift.expected @@ -0,0 +1,15 @@ +struct Adder { + var base: Int + + func callAsFunction(x: Int, y: Int) -> Int { + return base + x + y + } +} + +let /*test:def*/add3 = Adder(base: 3) +/*test:ref*/add3(x: 10, y: 11) +let blah = /*test:ref*/add3.callAsFunction(x:y:) + + + + diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function-paren-arg.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function-paren-arg.swift.expected new file mode 100644 index 0000000000000..602848b020351 --- /dev/null +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function-paren-arg.swift.expected @@ -0,0 +1,13 @@ +struct Adder { + var base: Int + func /*test:def*/callAsFunction(_ x: Int) -> Int { + return base + x + } +} + +let add3 = Adder(base: 3) +_ = add3/*test:call*/(10) +_ = add3 . /*test:call*/callAsFunction(10) +_ = add3 . /*test:ref*/callAsFunction(_:) +_ = add3 . /*test:ref*/callAsFunction + diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function.swift.expected new file mode 100644 index 0000000000000..a7bfef6a2710b --- /dev/null +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/call-as-function.swift.expected @@ -0,0 +1,14 @@ +struct Adder { + var base: Int + func /*test:def*/callAsFunction(x: Int, y: Int) -> Adder { + return self + } +} + +let add3 = Adder(base: 3) +_ = add3/*test:call*/(x: 10, y: 11) +_ = add3 . /*test:call*/callAsFunction(x: 10, y: 11) +_ = add3 . /*test:ref*/callAsFunction(x:y:) +_ = add3 . /*test:ref*/callAsFunction +_ = (add3 . /*test:call*/callAsFunction())/*test:call*/(x: 10, y: 11) + diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/functions/init.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/functions/init.swift.expected index e52236950898a..4bfc76a6d0897 100644 --- a/test/refactoring/SyntacticRename/FindRangeOutputs/functions/init.swift.expected +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/functions/init.swift.expected @@ -63,7 +63,7 @@ class SomeClass { let someClass = SomeClass(); let _ = /*init:call*/SomeClass(a:1, b:1, c:1) -let _ = SomeClass . /*init*/init(a:b:c:) +let _ = SomeClass . /*init*/init(a:b:c:) _ = someClass/*sub:ref*/[1, y: 2] someClass/*sub:ref*/[1, y: 2] = 2 diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/init.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/init.swift.expected new file mode 100644 index 0000000000000..30009af2da949 --- /dev/null +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/init.swift.expected @@ -0,0 +1,12 @@ +struct Test { + var base: Int + /*test:def*/init(base: Int) {} +} + +_ = /*test:call*/Test(base: 3) +_ = Test . /*test:call*/init(base: 3) +_ = Test . /*test:ref*/init +_ = Test . /*test:ref*/init(base:) + + + diff --git a/test/refactoring/SyntacticRename/call-as-function-base.swift b/test/refactoring/SyntacticRename/call-as-function-base.swift new file mode 100644 index 0000000000000..bb5b7e5b66048 --- /dev/null +++ b/test/refactoring/SyntacticRename/call-as-function-base.swift @@ -0,0 +1,18 @@ +struct Adder { + var base: Int + + func callAsFunction(x: Int, y: Int) -> Int { + return base + x + y + } +} + +let /*test:def*/add3 = Adder(base: 3) +/*test:ref*/add3(x: 10, y: 11) +let blah = /*test:ref*/add3.callAsFunction(x:y:) + +// RUN: %empty-directory(%t.ranges) +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -old-name "add3" >> %t.ranges/call-as-function-base.swift.expected +// RUN: diff -u %S/FindRangeOutputs/call-as-function-base.swift.expected %t.ranges/call-as-function-base.swift.expected + + + diff --git a/test/refactoring/SyntacticRename/call-as-function-paren-arg.swift b/test/refactoring/SyntacticRename/call-as-function-paren-arg.swift new file mode 100644 index 0000000000000..4b2822cd28669 --- /dev/null +++ b/test/refactoring/SyntacticRename/call-as-function-paren-arg.swift @@ -0,0 +1,16 @@ +struct Adder { + var base: Int + func /*test:def*/callAsFunction(_ x: Int) -> Int { + return base + x + } +} + +let add3 = Adder(base: 3) +_ = add3/*test:call*/(10) +_ = add3 . /*test:call*/callAsFunction(10) +_ = add3 . /*test:ref*/callAsFunction(_:) +_ = add3 . /*test:ref*/callAsFunction + +// RUN: %empty-directory(%t.ranges) +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "callAsFunction(_:)" >> %t.ranges/call-as-function-paren-arg.swift.expected +// RUN: diff -u %S/FindRangeOutputs/call-as-function-paren-arg.swift.expected %t.ranges/call-as-function-paren-arg.swift.expected diff --git a/test/refactoring/SyntacticRename/call-as-function.swift b/test/refactoring/SyntacticRename/call-as-function.swift new file mode 100644 index 0000000000000..93df29f360159 --- /dev/null +++ b/test/refactoring/SyntacticRename/call-as-function.swift @@ -0,0 +1,17 @@ +struct Adder { + var base: Int + func /*test:def*/callAsFunction(x: Int, y: Int) -> Adder { + return self + } +} + +let add3 = Adder(base: 3) +_ = add3/*test:call*/(x: 10, y: 11) +_ = add3 . /*test:call*/callAsFunction(x: 10, y: 11) +_ = add3 . /*test:ref*/callAsFunction(x:y:) +_ = add3 . /*test:ref*/callAsFunction +_ = (add3 . /*test:call*/callAsFunction())/*test:call*/(x: 10, y: 11) + +// RUN: %empty-directory(%t.ranges) +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "callAsFunction(x:y:)" >> %t.ranges/call-as-function.swift.expected +// RUN: diff -u %S/FindRangeOutputs/call-as-function.swift.expected %t.ranges/call-as-function.swift.expected diff --git a/test/refactoring/SyntacticRename/init.swift b/test/refactoring/SyntacticRename/init.swift new file mode 100644 index 0000000000000..74bcb347dbcd1 --- /dev/null +++ b/test/refactoring/SyntacticRename/init.swift @@ -0,0 +1,15 @@ +struct Test { + var base: Int + /*test:def*/init(base: Int) {} +} + +_ = /*test:call*/Test(base: 3) +_ = Test . /*test:call*/init(base: 3) +_ = Test . /*test:ref*/init +_ = Test . /*test:ref*/init(base:) + + +// RUN: %empty-directory(%t.ranges) +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "init(base:)" >> %t.ranges/init.swift.expected +// RUN: diff -u %S/FindRangeOutputs/init.swift.expected %t.ranges/init.swift.expected + diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index ffea417b24e82..e2c47a53884cd 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -641,11 +641,13 @@ static bool passCursorInfoForModule(ModuleEntity Mod, static void collectAvailableRenameInfo(const ValueDecl *VD, + Optional RefInfo, std::vector &RefactoringIds, DelayedStringRetriever &RefactroingNameOS, DelayedStringRetriever &RefactoringReasonOS) { std::vector Scratch; - for (auto Info : ide::collectRenameAvailabilityInfo(VD, Scratch)) { + for (auto Info : ide::collectRenameAvailabilityInfo(VD, RefInfo, + Scratch)){ RefactoringIds.push_back(SwiftLangSupport:: getUIDForRefactoringKind(Info.Kind)); RefactroingNameOS.startPiece(); @@ -837,8 +839,13 @@ static bool passCursorInfoForDecl(SourceFile* SF, std::vector RefactoringIds; DelayedStringRetriever RefactoringNameOS(SS); DelayedStringRetriever RefactoringReasonOS(SS); + if (RetrieveRefactoring) { - collectAvailableRenameInfo(VD, RefactoringIds, RefactoringNameOS, + Optional RefInfo; + if (TheTok.IsRef) + RefInfo = {TheTok.SF, TheTok.Loc, TheTok.IsKeywordArgument}; + collectAvailableRenameInfo(VD, RefInfo, + RefactoringIds, RefactoringNameOS, RefactoringReasonOS); collectAvailableRefactoringsOtherThanRename(SF, TheTok, RefactoringIds, RefactoringNameOS, RefactoringReasonOS);