Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quick fix for ValueNotContainedMutabilityCompiledNamesDiffer #545

Open
wants to merge 5 commits into
base: net233
Choose a base branch
from
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 @@ -34,6 +34,9 @@ let cliEventAttrTypeName = clrTypeName "Microsoft.FSharp.Core.CLIEventAttribute"
[<CompiledName("StructAttribute")>]
let structAttrTypeName = clrTypeName "Microsoft.FSharp.Core.StructAttribute"

[<CompiledName("CompiledNameAttribute")>]
let compiledNameAttrTypeName = clrTypeName "Microsoft.FSharp.Core.CompiledNameAttribute"

[<CompiledName("FSharpListTypeName")>]
let fsListTypeName = clrTypeName "Microsoft.FSharp.Collections.FSharpList`1"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ type FcsErrorsStageProcessBase(fsFile, daemonProcess) =
| ValueNotContainedMutability ->
if error.Message.EndsWith("The mutability attributes differ") then
createHighlightingFromNodeWithMessage ValueNotContainedMutabilityAttributesDifferError range error
elif error.Message.EndsWith("The compiled names differ") then
createHighlightingFromNodeWithMessage ValueNotContainedMutabilityCompiledNamesDifferError range error
else
createGenericHighlighting error range

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
<Compile Include="src\QuickFixes\RemovePatternArgumentFix.fs" />
<Compile Include="src\QuickFixes\AddSetterFix.fs" />
<Compile Include="src\QuickFixes\UpdateMutabilityInSignatureFix.fs" />
<Compile Include="src\QuickFixes\UpdateCompiledNameInSignatureFix.fs" />
<ErrorsGen Include="..\FSharp.Psi.Services\src\Daemon\Highlightings\Errors.xml">
<Mode>QUICKFIX</Mode>
<Namespace>JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes</Namespace>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,50 @@ let updateSignatureFieldDecls (implementationRecordRepr: IRecordRepresentation)
if signatureFieldCount > implementationRecordRepr.FieldDeclarations.Count then
[ implementationRecordRepr.FieldDeclarations.Count .. (signatureFieldCount - 1) ]
|> List.iter (fun idx -> signatureRecordRepr.FieldDeclarations.Item idx |> deleteChild)

type BindingPair =
| BindingPair of
implBinding: IBindingLikeDeclaration *
implMember: IFSharpMember *
sigBinding: IBindingLikeDeclaration *
sigMember: IFSharpMember

let tryFindBindingPairFromTopReferencePat (implTopRefPat: ITopReferencePat) =
match implTopRefPat.Binding, implTopRefPat.DeclaredElement.As<IFSharpMember>() with
| null, _ | _, null -> None
| implBinding, implMember ->

let tryPickFromDeclaration (condition: IReferencePat -> bool) (declaration: IDeclaration) =
match declaration with
| :? IReferencePat as pat when pat.IsFSharpSigFile() && condition pat ->
Option.both (Option.ofObj pat.Binding) (Option.ofObj (pat.DeclaredElement.As<IFSharpMember>()))
|> Option.map (fun (sigBinding, sigMember) -> BindingPair(implBinding, implMember, sigBinding, sigMember))
| _ -> None

implMember.GetDeclarations()
|> Seq.tryPick (tryPickFromDeclaration (fun _ -> true))
|> Option.orElseWith (fun () ->
let parentDeclarations = implMember.ContainingType.GetDeclarations()

// Find the parent signature counter part
let parentSignatureDeclaration =
parentDeclarations
|> Seq.tryPick (fun d ->
match d with
| :? ITypeDeclaration as signatureModule when signatureModule.IsFSharpSigFile() ->
Some signatureModule
| _ -> None)

parentSignatureDeclaration
|> Option.bind (fun signatureModule ->
signatureModule.MemberDeclarations
|> Seq.tryPick (
tryPickFromDeclaration (fun (sigRefPat: IReferencePat) ->
match implBinding.HeadPattern with
| :? IReferencePat as implPat ->
implPat.DeclaredName = sigRefPat.DeclaredName
|| implPat.Identifier.Name = sigRefPat.Identifier.Name
| _ -> false)
)
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes

open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.Highlightings
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Util
open JetBrains.ReSharper.Psi.ExtensionsAPI
open JetBrains.ReSharper.Resources.Shell
open type JetBrains.ReSharper.Plugins.FSharp.Psi.FSharpTreeNodeExtensions
open JetBrains.ReSharper.Plugins.FSharp.Psi.PsiUtil.PsiModificationUtil
open JetBrains.ReSharper.Plugins.FSharp.Psi.Intentions.QuickFixes.SignatureFixUtil
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Tree

type UpdateCompiledNameInSignatureFix(error: ValueNotContainedMutabilityCompiledNamesDifferError) =
inherit FSharpQuickFixBase()

let mutable bindingImplementation = null
let mutable bindingSignature = null
let mutable signatureCompiledNameAttribute = None
let mutable implementationCompiledNamedAttribute = None

let tryFindCompiledNameAttribute (binding: IBindingLikeDeclaration) =
binding.Attributes
|> Seq.tryFind (FSharpAttributesUtil.resolvesToType FSharpPredefinedType.compiledNameAttrTypeName)

override this.Text = $"Update CompiledName for {error.Pat.Identifier.Name} in signature"

override x.IsAvailable _ =
match tryFindBindingPairFromTopReferencePat error.Pat with
| None -> false
| Some (BindingPair(implementation, implMember, signature, sigMember)) ->
bindingImplementation <- implementation
bindingSignature <- signature
signatureCompiledNameAttribute <-tryFindCompiledNameAttribute signature
implementationCompiledNamedAttribute <- tryFindCompiledNameAttribute implementation
implMember.Mfv.CompiledName <> sigMember.Mfv.CompiledName
&& (implementationCompiledNamedAttribute.IsSome || signatureCompiledNameAttribute.IsSome)

override x.ExecutePsiTransaction _ =
use writeCookie = WriteLockCookie.Create(error.Pat.IsPhysical())
use disableFormatter = new DisableCodeFormatter()

match implementationCompiledNamedAttribute, signatureCompiledNameAttribute with
| None, None -> () // This error will not be raised unless one side has a CompiledNameAttribute.
| Some value, None ->
if bindingSignature.Attributes.IsEmpty then
// We create an elementFactory with the implementation file because the CreateEmptyAttributeList is tied to implementation files only.
let elementFactory = bindingImplementation.CreateElementFactory()
let attributeList = elementFactory.CreateEmptyAttributeList()
FSharpAttributesUtil.addAttribute attributeList value |> ignore
addNodesBefore bindingSignature [
attributeList
NewLine(bindingSignature.GetLineEnding())
] |> ignore
else
FSharpAttributesUtil.addAttributeAfter (Seq.last bindingSignature.Attributes) value
| None, Some sigAttr ->
let parentAttributeList = AttributeListNavigator.GetByAttribute(sigAttr)
if parentAttributeList.Attributes.Count = 1 then
deleteChild parentAttributeList
else
deleteChild sigAttr
| Some implAttr, Some sigAttr ->
sigAttr.SetArgExpression(implAttr.ArgExpression)
|> ignore
Original file line number Diff line number Diff line change
@@ -1,45 +1,22 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes

open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.Highlightings
open JetBrains.ReSharper.Resources.Shell
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Intentions.QuickFixes.SignatureFixUtil

type UpdateMutabilityInSignatureFix(error: ValueNotContainedMutabilityAttributesDifferError) =
inherit FSharpQuickFixBase()

let tryFindBindingSignature (declaredElement: IFSharpMember) =
declaredElement.GetDeclarations()
|> Seq.tryPick (function
| :? IReferencePat as pat when pat.IsFSharpSigFile() -> Option.ofObj pat.Binding
| _ -> None
)

let tryFindImplementationBindingInfo (pat: ITopReferencePat) =
if isNull pat then None else

match pat.Binding, pat.DeclaredElement.As<IFSharpMember>() with
| null, _ | _, null -> None
| binding, fsMember ->

let mfv = fsMember.Mfv
if isNull mfv then None else

Some(binding, fsMember)

let mutable bindingSignature = null

override x.Text = $"Update mutability for {error.Pat.Identifier.Name} in signature"

override x.IsAvailable _ =
match tryFindImplementationBindingInfo error.Pat with
match tryFindBindingPairFromTopReferencePat error.Pat with
| None -> false
| Some (topLevelBinding, declaredElement) ->
match tryFindBindingSignature declaredElement with
| None -> false
| Some signature ->
bindingSignature <- signature
topLevelBinding.IsMutable <> signature.IsMutable
| Some (BindingPair(implBinding = topLevelBinding; sigBinding = signature)) ->
bindingSignature <- signature
topLevelBinding.IsMutable <> signature.IsMutable

override x.ExecutePsiTransaction _ =
use writeCookie = WriteLockCookie.Create(error.Pat.IsPhysical())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@
<QuickFix>UpdateMutabilityInSignatureFix</QuickFix>
</Error>

<Error staticGroup="FSharpErrors" name="ValueNotContainedMutabilityCompiledNamesDiffer" ID="FS0034: Module contains but its signature specifies The compiled names differ">
<Parameter type="ITopReferencePat" name="pat"/>
<Parameter type="string" name="fcsMessage"/>
<Range>pat.GetNavigationRange()</Range>
<Message resourceName="Message" resourceType="Strings">
<Argument>fcsMessage</Argument>
</Message>
<Behavour overlapResolvePolicy="NONE"/>
<QuickFix>UpdateCompiledNameInSignatureFix</QuickFix>
</Error>

<Error staticGroup="FSharpErrors" name="VarBoundTwice" ID="FS0038: Pattern bound twice">
<Parameter type="IReferencePat" name="pat"/>
<Message resourceName="IsBoundMultipleTimesMessage" resourceType="Strings">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("X")>]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("X")>]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("X")>]
val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("X")>]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("X")>]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("Y")>]
val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("X")>]
val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace A

module B =

[<CompiledName("X")>]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace A

module B =

[<CompiledName("X")>]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace A

module B =

val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace A

module B =

[<CompiledName("X")>]
val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<CompiledName("Y")>]
val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A


val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<System.Obsolete "meh">]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module A

[<System.Obsolete "meh">]
let x{caret} (a:int) (b:int) = a + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module A

[<CompiledName("Y")>]
[<System.Obsolete "meh">]
val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module A


[<System.Obsolete "meh">]
val x: a:int -> b:int -> int
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
<Compile Include="src\QuickFixes\RemovePatternArgumentFixTest.fs" />
<Compile Include="src\QuickFixes\AddSetterFixTest.fs" />
<Compile Include="src\QuickFixes\UpdateMutabilityInSignatureFixTest.fs" />
<Compile Include="src\QuickFixes\UpdateCompiledNameInSignatureFixTest.fs" />
<Compile Include="src\QuickDoc\QuickDocTest.fs" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Tests.Intentions.QuickFixes

open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes
open NUnit.Framework

type UpdateCompiledNameInSignatureFixTest() =
inherit FSharpQuickFixTestBase<UpdateCompiledNameInSignatureFix>()

override x.RelativeTestDataPath = "features/quickFixes/updateCompiledNameInSignatureFix"

[<Test>] member x.``Attribute in implementation - 01`` () = x.DoNamedTestWithSignature()
[<Test>] member x.``Attribute in implementation - 02`` () = x.DoNamedTestWithSignature()
[<Test>] member x.``Attribute in implementation - 03`` () = x.DoNamedTestWithSignature()
[<Test>] member x.``No attribute in implementation - 01`` () = x.DoNamedTestWithSignature()
[<Test>] member x.``No attribute in implementation - 02`` () = x.DoNamedTestWithSignature()