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 @@ -319,6 +319,12 @@ type FcsErrorsStageProcessBase(fsFile, daemonProcess) =
| ValueNotContainedMutability ->
if error.Message.EndsWith("The mutability attributes differ") then
createHighlightingFromNodeWithMessage ValueNotContainedMutabilityAttributesDifferError range error
elif error.Message.EndsWith("The accessibility specified in the signature is more than that specified in the implementation") then
let refHighlighting = createHighlightingFromNodeWithMessage ValueNotContainedMutabilityAccessibilityMoreInBindingError range error
let memberHighlighting = createHighlightingFromParentNodeWithMessage ValueNotContainedMutabilityAccessibilityMoreInMemberError range error
if isNotNull refHighlighting then refHighlighting
elif isNotNull memberHighlighting then memberHighlighting
else createGenericHighlighting error range
else
createGenericHighlighting error range

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -927,17 +927,24 @@ type FSharpTreeBuilderBase(lexer, document: IDocument, lifetime, path: VirtualFi

member x.ProcessTypeMemberSignature(memberSig) =
match memberSig with
| SynMemberSig.Member(SynValSig(attrs, _, _, synType, arity, _, _, XmlDoc xmlDoc, _, _, _, trivia), flags, range, _) ->
| SynMemberSig.Member(SynValSig(attrs, _, _, synType, arity, _, _, XmlDoc xmlDoc, _, _, _, valSigTrivia), flags, range, memberTrivia) ->
let mark = x.MarkAndProcessIntro(attrs, xmlDoc, null, range)
x.ProcessReturnTypeInfo(arity, synType)
let elementType =
if flags.IsDispatchSlot then
x.ProcessAccessorsNamesClause(trivia, range)
x.ProcessAccessorsNamesClause(valSigTrivia, range)
ElementType.ABSTRACT_MEMBER_DECLARATION
else
match flags.MemberKind with
| SynMemberKind.Constructor -> ElementType.CONSTRUCTOR_SIGNATURE
| _ -> ElementType.MEMBER_SIGNATURE

match valSigTrivia.WithKeyword, memberTrivia.GetSetKeywords with
| Some withKw, Some getSetKeywords ->
let m = Range.unionRanges withKw getSetKeywords.Range
x.MarkAndDone(m, ElementType.ACCESSOR_DECLARATION)
| _ -> ()

x.Done(range, mark, elementType)

| SynMemberSig.ValField(SynField(attrs, _, id, synType, _, XmlDoc xmlDoc, _, _, _), range) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@
<Compile Include="src\QuickFixes\RemovePatternArgumentFix.fs" />
<Compile Include="src\QuickFixes\AddSetterFix.fs" />
<Compile Include="src\QuickFixes\UpdateMutabilityInSignatureFix.fs" />
<Compile Include="src\QuickFixes\UpdateAccessibilityInSignatureBindingFix.fs" />
<Compile Include="src\QuickFixes\UpdateAccessibilityInSignatureMemberFix.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
@@ -0,0 +1,51 @@
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.Psi
open JetBrains.ReSharper.Resources.Shell
open JetBrains.ReSharper.Plugins.FSharp.Psi

type UpdateAccessibilityInSignatureBindingFix(error: ValueNotContainedMutabilityAccessibilityMoreInBindingError) =
inherit FSharpQuickFixBase()

let tryFindBindingSignatureAccessRights (declaredElement: IFSharpMember) =
declaredElement.GetDeclarations()
|> Seq.tryPick (function
| :? IReferencePat as pat when pat.IsFSharpSigFile() ->
match pat.DeclaredElement.As<IFSharpMember>() with
| null -> None
| sigMember ->
let bindingSignature = BindingSignatureNavigator.GetByHeadPattern(pat)
if isNull bindingSignature then None else
Some (bindingSignature, sigMember.GetAccessRights())
| _ -> None
)

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

match pat.DeclaredElement.As<IFSharpMember>() with
| null -> None
| fsMember -> Some fsMember

let mutable implAccessRights = AccessRights.NONE
let mutable bindingSignature = null

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

override x.IsAvailable _ =
let topPat = TopReferencePatNavigator.GetByReferenceName(error.ReferenceName)
match tryFindImplementationBindingInfo topPat with
| None -> false
| Some implDeclaredElement ->
match tryFindBindingSignatureAccessRights implDeclaredElement with
| None -> false
| Some (bindingSig, sigAccessRights) ->
implAccessRights <- implDeclaredElement.GetAccessRights()
bindingSignature <- bindingSig
implAccessRights <> sigAccessRights

override x.ExecutePsiTransaction _ =
use writeCookie = WriteLockCookie.Create(error.ReferenceName.IsPhysical())
bindingSignature.SetAccessModifier(implAccessRights)
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes

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

type UpdateAccessibilityInSignatureMemberFix(error: ValueNotContainedMutabilityAccessibilityMoreInMemberError) =
inherit FSharpQuickFixBase()

let tryFindSignatureMemberAccessRights (memberDeclaration: IOverridableMemberDeclaration) =
if isNull memberDeclaration.DeclaredElement then None else
memberDeclaration.DeclaredElement.GetDeclarations()
|> Seq.tryPick (function
| :? IMemberSignature as memberSig -> Some (memberSig, memberSig.GetAccessRights())
| _ -> None)

let tryFindSignatureMemberInPropertyAccessRights (memberDeclaration: IMemberDeclaration) (accessorDeclaration:IAccessorDeclaration) =
if isNull memberDeclaration.DeclaredElement then None else
let implAccessRights = accessorDeclaration.GetAccessRights()

let allDeclarations : IDeclaration array = memberDeclaration.DeclaredElement.GetDeclarations() |> Seq.toArray
let allDeclarations =
if allDeclarations.Length > 1 then allDeclarations else

// Search via the parent
let fm = memberDeclaration.DeclaredElement.As<IFSharpMember>()
if isNull fm then Array.empty else

fm.ContainingType.GetDeclarations()
|> Seq.tryPick (fun t ->
match t with
| :? IFSharpTypeDeclaration as td when td.IsFSharpSigFile() ->
td.TypeMembers
|> Seq.choose (function | :? IDeclaration as decl -> Some decl | _ -> None)
|> Seq.toArray
|> Some
| _ -> None)
|> Option.defaultValue Array.empty

allDeclarations
|> Seq.tryPick (function
| :? IMemberSignature as memberSig ->
memberSig.AccessorDeclarationsEnumerable
|> Seq.tryFind (fun ad -> ad.DeclaredName = accessorDeclaration.DeclaredName)
|> Option.bind (fun _ ->
if implAccessRights <> memberSig.GetAccessRights() then
Some (memberSig, accessorDeclaration.DeclaredName, implAccessRights)
else None)
| _ -> None)

let updatableSignatures : ResizeArray<IMemberSignature * AccessRights> = ResizeArray()

override x.Text = $"Update accessibility for {error.MemberDeclaration.Identifier.GetText()} in signature"

override x.IsAvailable _ =
if isNull error.MemberDeclaration then false else
// If both the get and set of a property are reported, IsAvailable will be called twice.
if updatableSignatures.Count > 0 then true else

match error.MemberDeclaration with
| :? IMemberDeclaration as md when md.AccessorDeclarations.Count = 2 ->
// property with get/set
md.AccessorDeclarationsEnumerable
|> Seq.choose (tryFindSignatureMemberInPropertyAccessRights md)
|> Seq.iter (fun (memberSig, _declName, implAccR) -> updatableSignatures.Add (memberSig, implAccR))
| _ ->
match tryFindSignatureMemberAccessRights error.MemberDeclaration with
| None -> ()
| Some (ms, sigAccessRights) ->
let implAccessRights = error.MemberDeclaration.GetAccessRights()
if implAccessRights <> sigAccessRights then
updatableSignatures.Add (ms, implAccessRights)

updatableSignatures.Count > 0

override x.ExecutePsiTransaction _ =
use writeCookie = WriteLockCookie.Create(error.MemberDeclaration.IsPhysical())
for memberSignature, implAccessRights in updatableSignatures do
memberSignature.SetAccessModifier(implAccessRights)
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,28 @@
<QuickFix>UpdateMutabilityInSignatureFix</QuickFix>
</Error>

<Error staticGroup="FSharpErrors" name="ValueNotContainedMutabilityAccessibilityMoreInBinding" ID="FS0034: Module contains but its signature specifies The accessibility specified in the signature is more than that specified in the implementation">
<Parameter type="IExpressionReferenceName" name="referenceName"/>
<Parameter type="string" name="fcsMessage"/>
<Range>referenceName.GetNavigationRange()</Range>
<Message resourceName="Message" resourceType="Strings">
<Argument>fcsMessage</Argument>
</Message>
<Behavour overlapResolvePolicy="NONE"/>
<QuickFix>UpdateAccessibilityInSignatureBindingFix</QuickFix>
</Error>

<Error staticGroup="FSharpErrors" name="ValueNotContainedMutabilityAccessibilityMoreInMember" ID="FS0034: Module contains but its signature specifies The accessibility specified in the signature is more than that specified in the implementation">
<Parameter type="IOverridableMemberDeclaration" name="memberDeclaration"/>
<Parameter type="string" name="fcsMessage"/>
<Range>memberDeclaration.GetNavigationRange()</Range>
<Message resourceName="Message" resourceType="Strings">
<Argument>fcsMessage</Argument>
</Message>
<Behavour overlapResolvePolicy="NONE"/>
<QuickFix>UpdateAccessibilityInSignatureMemberFix</QuickFix>
</Error>

<Error staticGroup="FSharpErrors" name="VarBoundTwice" ID="FS0038: Pattern bound twice">
<Parameter type="IReferencePat" name="pat"/>
<Message resourceName="IsBoundMultipleTimesMessage" resourceType="Strings">
Expand Down
3 changes: 3 additions & 0 deletions ReSharper.FSharp/src/FSharp.Psi/src/FSharp.psi
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,7 @@ memberSignature options { stubBase="FSharpProperTypeMemberDeclarationBase"; } e
attributeList<ATTRIBUTE_LIST, AttributeLists>*
STATIC<STATIC, StaticKeyword>?
memberKeyword{MEMBER_KEYWORD, MemberKeyword}
INLINE<INLINE, InlineKeyword>?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accessModifier{ACCESS_MODIFIER, AccessModifier}?
identOrOpName{IDENTIFIER, Identifier}
postfixTypeParameterDeclarationList<TYPE_PARAMETER_LIST, TypeParameterList>?
Expand Down Expand Up @@ -1369,6 +1370,8 @@ autoPropertyDeclaration options { stubBase="FSharpProperTypeMemberDeclarationBas
STATIC<STATIC, StaticKeyword>?
memberKeyword{MEMBER, MemberKeyword}
VAL<VAL, ValKeyword>
MUTABLE<MUTABLE, MutableKeyword>?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accessModifier{ACCESS_MODIFIER, AccessModifier}?
IDENTIFIER<IDENTIFIER, Identifier>
EQUALS<FSHARP_EQUALS, EqualsToken>
chameleonExpression<CHAMELEON_EXPR, InitExpression>
Expand Down
11 changes: 11 additions & 0 deletions ReSharper.FSharp/src/FSharp.Psi/src/Impl/ModifiersUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Caches2;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.Tree;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
Expand Down Expand Up @@ -121,5 +122,15 @@ public static AccessRights GetAccessRights([CanBeNull] ITokenNode accessModifier

return AccessRights.PUBLIC;
}

public static ITreeNode GetAccessNode(AccessRights accessRights)
{
return accessRights switch
{
AccessRights.PRIVATE => FSharpTokenType.PRIVATE.CreateTreeElement(),
AccessRights.INTERNAL => FSharpTokenType.INTERNAL.CreateTreeElement(),
_ => FSharpTokenType.PUBLIC.CreateTreeElement()
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ protected override IDeclaredElement CreateDeclaredElement(FSharpSymbol fcsSymbol

public override bool IsOverride => this.IsOverride();
public override bool IsExplicitImplementation => this.IsExplicitImplementation();
public override AccessRights GetAccessRights() => ModifiersUtil.GetAccessRights(AccessModifier);
}
}
14 changes: 14 additions & 0 deletions ReSharper.FSharp/src/FSharp.Psi/src/Impl/Tree/BindingSignature.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using JetBrains.ReSharper.Plugins.FSharp.Psi.Parsing;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Tree
Expand All @@ -22,5 +23,18 @@ public void SetIsMutable(bool value)
if (headPat != null)
FSharpImplUtil.AddTokenBefore(headPat, FSharpTokenType.MUTABLE);
}

public void SetAccessModifier(AccessRights accessModifier)
{
// TODO: check for AccessRights.NONE
if (AccessModifier == null)
{
ModificationUtil.AddChildAfter(BindingKeyword, ModifiersUtil.GetAccessNode(accessModifier));
}
else
{
ModificationUtil.ReplaceChild(AccessModifier, ModifiersUtil.GetAccessNode(accessModifier));
}
}
}
}
13 changes: 13 additions & 0 deletions ReSharper.FSharp/src/FSharp.Psi/src/Impl/Tree/MemberSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using JetBrains.ReSharper.Plugins.FSharp.Psi.Parsing;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Tree
{
Expand All @@ -27,9 +28,21 @@ protected override IDeclaredElement CreateDeclaredElement(FSharpSymbol fcsSymbol
this.CreateMemberDeclaredElement(fcsSymbol);

public bool IsIndexer => this.IsIndexer();
public void SetAccessModifier(AccessRights accessModifier)
{
if (AccessModifier == null)
{
ModificationUtil.AddChildAfter(InlineKeyword ?? MemberKeyword, ModifiersUtil.GetAccessNode(accessModifier));
}
else
{
ModificationUtil.ReplaceChild(AccessModifier, ModifiersUtil.GetAccessNode(accessModifier));
}
}

public override bool IsStatic => StaticKeyword != null;
public override bool IsVirtual => MemberKeyword?.GetTokenType() == FSharpTokenType.DEFAULT;
public override bool IsOverride => this.IsOverride();
public override AccessRights GetAccessRights() => ModifiersUtil.GetAccessRights(AccessModifier);
}
}
9 changes: 9 additions & 0 deletions ReSharper.FSharp/src/FSharp.Psi/src/Tree/IBindingSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using JetBrains.ReSharper.Psi;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
{
public partial interface IBindingSignature
{
void SetAccessModifier(AccessRights accessModifier);
}
}
9 changes: 9 additions & 0 deletions ReSharper.FSharp/src/FSharp.Psi/src/Tree/IMemberSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using JetBrains.ReSharper.Psi;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
{
public partial interface IMemberSignature
{
void SetAccessModifier(AccessRights accessModifier);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let private a{caret} = 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let private a{caret} = 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val internal a: int
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val private a: int
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let internal a{caret} = 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let internal a{caret} = 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val a: int
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val internal a: int
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

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

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

val inline public a: b: 't -> unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val inline private a: b: 't -> unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let mutable private a{caret} = 4
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

let mutable private a{caret} = 4
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val mutable public a: int
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module A

val mutable private a: int
Loading