Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Resolve
open JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Util
open JetBrains.ReSharper.Psi
Expand Down Expand Up @@ -118,6 +119,7 @@ type IFSharpQuickFixUtilComponent =
[<Language(typeof<FSharpLanguage>)>]
type FSharpQuickFixUtilComponent() =
let [<Literal>] FcsOpName = "FSharpQuickFixUtilComponent.BindTo"
let [<Literal>] FcsImportExtensionOpName = "FSharpQuickFixUtilComponent.AddImportsForExtensionMember"

member x.BindTo(reference: FSharpSymbolReference, typeElement: ITypeElement) =
let referenceOwner = reference.GetElement()
Expand All @@ -132,7 +134,16 @@ type FSharpQuickFixUtilComponent() =
member x.BindTo(reference, typeElement, _, _) =
x.BindTo(reference :?> _, typeElement) :> _

member x.AddImportsForExtensionMember(reference, _) = reference
member x.AddImportsForExtensionMember(reference, extensionMembers) =
match reference with
| :? FSharpSymbolReference as reference ->
let referenceOwner = reference.GetElement()
use writeCookie = WriteLockCookie.Create(referenceOwner.IsPhysical())

FSharpBindUtil.bindDeclaredElementToReference referenceOwner reference extensionMembers[0] FcsImportExtensionOpName
reference

| _ -> null

member this.BindTo(reference, typeElement) =
this.BindTo(reference :?> _, typeElement)
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes

open System.Collections.Generic
open System.Linq
open JetBrains.Application
open JetBrains.Application.UI.Controls.BulbMenu.Anchors
open JetBrains.ReSharper.Feature.Services.BulbActions
open JetBrains.ReSharper.Feature.Services.Bulbs
open JetBrains.ReSharper.Feature.Services.Intentions
open JetBrains.ReSharper.Feature.Services.Intentions.Scoped
open JetBrains.ReSharper.Feature.Services.Intentions.Scoped.Actions
open JetBrains.ReSharper.Intentions.QuickFixes
open JetBrains.ReSharper.Intentions.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
Expand All @@ -20,6 +25,24 @@ open JetBrains.ReSharper.Psi.Resolve
open JetBrains.ReSharper.Psi.Tree
open JetBrains.ReSharper.Resources.Shell

[<Language(typeof<FSharpLanguage>)>]
type FSharpExtensionMemberImportUtil() =
inherit ExtensionMemberImportUtilBase()

override this.CollectApplicableCandidates(applicableCandidates, reference, acceptableErrorTypes) =
let refExpr = reference.GetTreeNode().As<IReferenceExpr>()
if isNull refExpr then () else

let name = reference.GetName()
FSharpExtensionMemberUtil.getNonImportedExtensionMembers refExpr (Some name) refExpr
|> FSharpExtensionMemberUtil.groupByNameAndNs
|> Seq.map (snd >> Seq.tryHead)
|> Seq.choose id
|> applicableCandidates.AddRange

override this.LanguageType = FSharpLanguage.Instance


[<AbstractClass>]
type FSharpImportMemberActionBase<'T when 'T :> IClrDeclaredElement>(reference: FSharpSymbolReference) =
inherit ModernBulbActionBase()
Expand Down Expand Up @@ -53,18 +76,6 @@ type FSharpImportModuleMemberAction(typeElement: ITypeElement, reference: FSharp
reference.SetRequiredQualifiersForContainingType(typeElement, referenceOwner)


type FSharpImportExtensionMemberAction(typeMember: ITypeMember, reference) =
inherit FSharpImportMemberActionBase<ITypeMember>(reference)

override this.Bind() =
let referenceOwner = reference.GetElement()
FSharpBindUtil.bindDeclaredElementToReference referenceOwner reference typeMember "bind"

override this.Text =
let containingTypeShortName = typeMember.ContainingType.ShortName
$"Use {containingTypeShortName}.{reference.GetName()}"


[<AbstractClass>]
type FSharpImportMemberFixBase<'T when 'T :> IClrDeclaredElement>(reference: IReference) =
inherit FSharpQuickFixBase()
Expand Down Expand Up @@ -98,23 +109,6 @@ type FSharpImportMemberFixBase<'T when 'T :> IClrDeclaredElement>(reference: IRe
importActions.ToQuickFixIntentions(anchor)


type FSharpImportExtensionMemberFix(reference: IReference) =
inherit FSharpImportMemberFixBase<ITypeMember>(reference)

override this.FindMembers(reference) =
let refExpr = reference.GetTreeNode().As<IReferenceExpr>()
if isNull refExpr then [] else

let name = reference.GetName()
FSharpExtensionMemberUtil.getNonImportedExtensionMembers refExpr (Some name) refExpr
|> FSharpExtensionMemberUtil.groupByNameAndNs
|> Seq.map (snd >> Seq.tryHead)
|> Seq.choose id

override this.CreateAction(typeMember, reference) =
FSharpImportExtensionMemberAction(typeMember, reference)


type FSharpImportModuleMemberFix(reference: IReference) =
inherit FSharpImportMemberFixBase<ITypeElement>(reference)

Expand Down Expand Up @@ -172,3 +166,44 @@ type FSharpImportModuleMemberFix(reference: IReference) =

override this.CreateAction(typeElement, reference) =
FSharpImportModuleMemberAction(typeElement, reference)


type FSharpImportExtensionMemberAction(typeMember: ITypeMember, reference: FSharpSymbolReference) =
inherit ModernBulbActionBase()

override this.Text =
let containingTypeShortName = typeMember.ContainingType.ShortName
$"Use {containingTypeShortName}.{reference.GetName()}"

override this.ExecutePsiTransaction(_, _) =
let reference = QuickFixUtil.BindTo(reference, [|typeMember|])

if isNull reference then BulbActionCommands.ShowTooltip("Failed to import extension member")
else null

interface IModernManualScopedAction with
member this.ExecuteAction(solution, _, _, progress) =
this.ExecutePsiTransaction(solution, progress)

member this.FileCollectorInfo = FileCollectorInfo.Empty
member this.ScopedText = this.Text

type FSharpImportExtensionMemberFix(reference: IReference) =
inherit ScopedImportQuickFixBase(reference)

let reference = reference.As<FSharpSymbolReference>()

override this.CreateBulbActions() =
let extensionMembers = this.EnumeratePossibleExtensionMembers(reference)
[| for extensionMember in extensionMembers.Values -> FSharpImportExtensionMemberAction(extensionMember, reference) |]

override this.CreateBulbItems() =
let importActions = this.CreateBulbActions().ToArray()

let anchor: IAnchor =
if importActions.Length > 2 then
SubmenuAnchor(ResolveProblemsFixAnchors.ImportFix, "Import and use...")
else
ResolveProblemsFixAnchors.ImportFix

importActions.ToQuickFixIntentions(anchor)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using JetBrains.Annotations;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree;
using JetBrains.ReSharper.Plugins.FSharp.Util;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Caches2;
using JetBrains.ReSharper.Psi.Modules;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Cache2.Compiled;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Caches2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ internal abstract class FSharpMemberBase<TDeclaration>([NotNull] ITypeMemberDecl
public bool IsFSharpExtensionMember =>
GetContainingType() is IFSharpModule && GetDeclaration() is IMemberSignatureOrDeclaration;

public override ExtensionMemberKind ExtensionMemberKind =>
IsFSharpExtensionMember ? FSharpExtensionMemberKind.INSTANCE : ExtensionMemberKind.NONE;

protected override ITypeElement GetTypeElement(IDeclaration declaration)
{
var typeDeclaration = declaration.GetContainingNode<ITypeDeclaration>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public override DeclaredElementType GetElementType() =>
public override ExtensionMemberKind ExtensionMemberKind =>
Attributes.HasAttributeInstance(PredefinedType.EXTENSION_ATTRIBUTE_CLASS)
? ExtensionMemberKind.CLASSIC_METHOD
: ExtensionMemberKind.NONE;
: base.ExtensionMemberKind;

public bool IsAsync => false;
public bool IsVarArg => false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using JetBrains.Annotations;
using JetBrains.ReSharper.Psi;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Cache2.Compiled;
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Impl;

/// <summary>
/// Represents a kind of F#-specific extension member.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ private ResolvedSymbols CreateFileResolvedSymbols()

foreach (var diagnostic in checkResults.Diagnostics)
{
if (diagnostic.ErrorNumber != 39)
if (diagnostic.ErrorNumber != 39 &&
diagnostic.ErrorNumber != 72)
continue;

var identRange = document.GetTextRange(diagnostic.Range);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let _ = "".FirstOrDefault{caret}()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
let _ = "".|FirstOrDefault|(0)()

------------------------------------------------
0: The type 'String' does not define a field, constructor, or member named 'FirstOrDefault'.
QUICKFIXES:
Import missing references in file
--Import missing references in project
--Import missing references in solution
Use Enumerable.FirstOrDefault
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module A =
type System.String with
member _.FirstOrDefault() = ()

module B =
type System.String with
member _.FirstOrDefault() = ()

let _ = "".FirstOrDefault{caret}()
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module A =
type System.String with
member _.FirstOrDefault() = ()

module B =
type System.String with
member _.FirstOrDefault() = ()

let _ = "".|FirstOrDefault|(0)()

------------------------------------------------
0: The type 'String' does not define a field, constructor, or member named 'FirstOrDefault'.
QUICKFIXES:
Import and use...
--Use A.FirstOrDefault
--Use B.FirstOrDefault
--Use Enumerable.FirstOrDefault
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//${RUN:Import missing references in file}
let x: StreamReader{caret} = null
let y = "".FirstOrDefault()
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//${RUN:Import missing references in file}
open System.IO
open System.Linq

let x: StreamReader{caret} = null
let y = "".FirstOrDefault()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//${RUN:Import missing references in file}
let f (x: IList{caret}<int>) = x.Select(id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//${RUN:Import missing references in file}
open System.Collections.Generic
open System.Linq

let f (x: IList{caret}<int>) = x.Select(id)
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type ImportTypeTest() =
[<Test>] member x.``Type 02 - Empty line``() = x.DoNamedTest()
[<Test>] member x.``Type 03 - Multiple``() = x.DoNamedTest()
[<Test>] member x.``Type 04 - Multiple - Static members``() = x.DoNamedTest()
[<Test>] member x.``Type 05 - Multiple - Extensions``() = x.DoNamedTest()
[<Test>] member x.``Type 06 - Multiple - Indeterminate type extension``() = x.DoNamedTest()
[<Test>] member x.``Type extension 01``() = x.DoNamedTest()

[<Test>] member x.``Generic List 01``() = x.DoNamedTest()
Expand Down Expand Up @@ -169,6 +171,16 @@ type ImportExtensionMemberTest() =
[<Test>] member x.``Not Available - Type 02``() = x.DoNamedTest()


[<FSharpTest>]
type ImportExtensionAvailabilityTest() =
inherit QuickFixAvailabilityTestBase<FSharpImportExtensionMemberFix>()

override x.RelativeTestDataPath = "features/quickFixes/import/extension/availability"

[<Test>] member x.``Extension 01``() = x.DoNamedTest()
[<Test>] member x.``Extension 02 - Multiple``() = x.DoNamedTest()


[<FSharpTest>]
type FSharpImportModuleMemberTest() =
inherit FSharpQuickFixTestBase<FSharpImportModuleMemberFix>()
Expand Down
Loading