Skip to content

Commit 462c19b

Browse files
authored
Merge branch 'main' into ber.a/nowarn-warnon
2 parents 95a163e + d29135c commit 462c19b

36 files changed

+438
-228
lines changed

RELEASE_NOTES.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
# Release notes
22

3+
## 2025.3
4+
5+
2025.3
6+
7+
Quick fixes:
8+
* Change type:
9+
* The new quick fix allows changing declarations to fix type mismatch errors. It allows changing types of parameters, function return, fields, properties and so on. It integrates into the common logic in R#, so changing types is also possible between the languages.
10+
11+
* Import:
12+
* Module values and functions from compiled F# code can now be imported
13+
* Static F# extension members can now be imported in code completion and quick fix
14+
* Extension members can now be imported on F# short lambdas
15+
* Extensions members are grouped by namespace, it's possible to choose which one to import
16+
17+
* Generate overrides: import parameter types when needed
18+
* To mutable: implemented for structs and their fields
19+
20+
Better AI support
21+
We've reimplemented context collection for Junie, so it can analyze F# code significantly better
22+
23+
Analisys:
24+
* Update F# compiler service for F# 10 features
25+
* Infer the maximum supported language version in the used compiler and use it inside IDE
26+
27+
Misc:
28+
* Improved support for F# short lambdas in refactorings like 'Introduce variable' and quick fixes
29+
* Typing assists: better 'Enter' implementation for single-line type declarations and `if` expressions
30+
* Language injections: fix reading annotations in compiled F# properties, allow injecting F# with `fsharp` alias
31+
* Update Fantomas to 7.0.3
32+
33+
334
## 2025.2
435

536
### Import code completion and quick fixes

ReSharper.FSharp/src/FSharp.TypeProviders.Host/PackagesLock.targets

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
<ItemGroup>
44
<PackageLock Include="FSharp.Core" Version="9.0.201" />
55
<PackageLock Include="JetBrains.Annotations" Version="2025.2.0" />
6-
<PackageLock Include="JetBrains.FSharp.Compiler.Service" Version="2025.3.4" />
6+
<PackageLock Include="JetBrains.FSharp.Compiler.Service" Version="2025.3.5.2" />
77
<PackageLock Include="JetBrains.Lifetimes" Version="2025.3.1" />
88
<PackageLock Include="JetBrains.RdFramework" Version="2025.3.1" />
99
<PackageLock Include="Microsoft.NETCore.Platforms" Version="3.1.0" />
1010
<PackageLock Include="Microsoft.NETCore.Targets" Version="1.1.3" />
1111
<PackageLock Include="System.Diagnostics.Debug" Version="4.3.0" />
12+
<PackageLock Include="Newtonsoft.Json" Version="13.0.3" />
1213
</ItemGroup>
13-
</Project>
14+
</Project>

ReSharper.FSharp/src/FSharp/FSharp.Common/src/Shim/AssemblyReader/FcsModuleReaderCommonCache.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ open JetBrains.Util.DataStructures
1616
type FcsModuleReaderCommonCache(lifetime: Lifetime, changeManager: ChangeManager) =
1717
inherit AssemblyReaderShimChangeListenerBase(lifetime, changeManager)
1818

19+
// todo: is it ever cleared?
1920
let assemblyTypeRefs = ConcurrentDictionary<IPsiModule, ConcurrentDictionary<IClrTypeName, ILTypeRef>>()
2021

2122
member this.GetOrCreateAssemblyTypeRefCache(targetModule: IPsiModule) =

ReSharper.FSharp/src/FSharp/FSharp.Common/src/Shim/AssemblyReader/ProjectFcsModuleReader.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,9 @@ type ProjectFcsModuleReader(psiModule: IPsiModule, cache: FcsModuleReaderCommonC
13421342
timestamp <- DateTime.UtcNow
13431343
shim.Logger.Trace("New timestamp: {0}: {1}", path, timestamp)
13441344

1345+
member this.GetFcsIlType(t: IType) =
1346+
mkType t
1347+
13451348
interface IProjectFcsModuleReader with
13461349
member this.ILModuleDef =
13471350
FSharpAsyncUtil.CheckAndThrow()

ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/CodeCompletion/FSharpScriptReferenceCompletionProvider.fs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ type FSharpScriptReferenceCompletionContextProvider() =
8383
let caretValueOffset = caretOffset - argOffset - startQuoteLength
8484
let prefixValue = argValue.Substring(0, caretValueOffset)
8585
let supportsNuget =
86-
match hashDirective.HashToken.GetText() with
86+
match hashDirective.HashToken with
87+
| null -> false
88+
| hashToken ->
89+
90+
match hashToken.GetText() with
8791
| "#r" | "#reference" -> "nuget:".StartsWith(prefixValue.TrimStart([|' '|]))
8892
| _ -> false
8993

ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/CodeCompletion/Rules/FSharpKeywordsRule.fs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ open JetBrains.ReSharper.Feature.Services.CodeCompletion.Settings
1414
open JetBrains.ReSharper.Feature.Services.Lookup
1515
open JetBrains.RdBackend.Common.Features.Completion
1616
open JetBrains.ReSharper.Plugins.FSharp
17+
open JetBrains.ReSharper.Plugins.FSharp.ProjectModel
1718
open JetBrains.ReSharper.Plugins.FSharp.Psi
1819
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion
1920
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
@@ -301,11 +302,12 @@ module FSharpKeywordsProvider =
301302
type FSharpKeywordsRule() =
302303
inherit ItemsProviderOfSpecificContext<FSharpCodeCompletionContext>()
303304

304-
let hashDirectives =
305-
[| KeywordSuffix.Quotes, [| "#load"; "#r"; "#I"; "#nowarn"; "#warnon"; "#time" |]
305+
let hashDirectives isFSharp9Supported =
306+
let spaceOrQuotesSuffix = if isFSharp9Supported then KeywordSuffix.Space else KeywordSuffix.Quotes
307+
308+
[| spaceOrQuotesSuffix, [| "#nowarn"; "#warnon"; "#time" |]
309+
KeywordSuffix.Quotes, [| "#load"; "#r"; "#I" |]
306310
KeywordSuffix.None, [| "#if"; "#else"; "#endif" |] |]
307-
|> Array.map (fun (suffix, directives) -> directives |> Array.map (fun d -> d, suffix))
308-
|> Array.concat
309311

310312
let scriptKeywords =
311313
[| "__SOURCE_DIRECTORY__"
@@ -374,10 +376,12 @@ type FSharpKeywordsRule() =
374376
item.InitializeRanges(context.Ranges, context.BasicContext)
375377
collector.Add(item)
376378

377-
for keyword, suffix in hashDirectives do
378-
let item = FSharpHashDirectiveLookupItem(keyword, suffix)
379-
item.InitializeRanges(context.Ranges, context.BasicContext)
380-
collector.Add(item)
379+
let isFSharp9Supported = FSharpLanguageLevel.isFSharp90Supported context.BasicContext.File
380+
for suffix, keywords in hashDirectives isFSharp9Supported do
381+
for keyword in keywords do
382+
let item = FSharpHashDirectiveLookupItem(keyword, suffix)
383+
item.InitializeRanges(context.Ranges, context.BasicContext)
384+
collector.Add(item)
381385

382386
true
383387

ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/CodeCompletion/Rules/ImportModuleMemberRule.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion
1616
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
1717
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
1818
open JetBrains.ReSharper.Plugins.FSharp.Psi.Resolve
19+
open JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util
1920
open JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util.FSharpCompletionUtil
2021
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
2122
open JetBrains.ReSharper.Plugins.FSharp.Psi.Util
@@ -96,7 +97,7 @@ type ImportModuleMemberRule() =
9697
match referenceContext.Value with
9798
| FSharpReferenceContext.Expression ->
9899
let treeNode = reparsedContext.TreeNode
99-
context.GetOrCreateDataUnderLock(LocalValuesRule.valuesKey, treeNode, LocalValuesRule.getLocalValues)
100+
context.GetOrCreateDataUnderLock(LocalValuesUtil.valuesKey, treeNode, LocalValuesUtil.getLocalValues)
100101

101102
| _ -> EmptyDictionary.Instance
102103

ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/CodeCompletion/Rules/LocalValuesRule.fs

Lines changed: 4 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion.Rules
22

3-
open System.Collections.Generic
43
open FSharp.Compiler.CodeAnalysis
5-
open JetBrains.Application
64
open JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure
75
open JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure.AspectLookupItems.BaseInfrastructure
86
open JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure.AspectLookupItems.Behaviors
@@ -13,12 +11,11 @@ open JetBrains.ReSharper.Plugins.FSharp.Psi
1311
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion
1412
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
1513
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util.FcsTypeUtil
14+
open JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util
1615
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
1716
open JetBrains.ReSharper.Plugins.FSharp.Util
1817
open JetBrains.ReSharper.Psi
19-
open JetBrains.ReSharper.Psi.Tree
2018
open JetBrains.UI.RichText
21-
open JetBrains.Util
2219

2320
type FcsSymbolInfo(text, symbolUse: FSharpSymbolUse) =
2421
inherit TextualInfo(text, text)
@@ -31,116 +28,6 @@ type FcsSymbolInfo(text, symbolUse: FSharpSymbolUse) =
3128
member this.FcsSymbolUse = symbolUse
3229
member this.NamespaceToOpen = [||]
3330

34-
module rec LocalValuesRule =
35-
let valuesKey = Key<IDictionary<string, FSharpSymbolUse>>(nameof LocalValuesRule)
36-
37-
let getLocalValues (context: ITreeNode) : IDictionary<_, _> =
38-
let values = Dictionary<string, FSharpSymbolUse>()
39-
40-
let addValue (decl: IFSharpDeclaration) =
41-
let sourceName = decl.SourceName
42-
if sourceName <> "_" then
43-
values.TryAdd(sourceName, decl.GetFcsSymbolUse()) |> ignore
44-
45-
let addPatternValues (pat: IFSharpPattern) =
46-
if isNull pat then () else
47-
48-
for declPattern in pat.Declarations do
49-
let pat = declPattern.As<IFSharpPattern>()
50-
if pat.IsDeclaration then
51-
addValue declPattern
52-
53-
let addLetBindingsValues (letBindings: ILetBindings) =
54-
for otherBinding in letBindings.Bindings do
55-
addPatternValues otherBinding.HeadPattern
56-
57-
let addSelfIdValue (selfId: ISelfId) =
58-
if isNotNull selfId then
59-
addValue selfId
60-
61-
let addMembersValues (contextOffset: int) (members: ITreeNode seq) =
62-
let members = members |> Seq.takeWhile (fun m -> m.GetTreeStartOffset().Offset < contextOffset)
63-
64-
for otherMember in members do
65-
match otherMember with
66-
| :? ILetBindings as letBindings ->
67-
for binding in letBindings.Bindings do
68-
addPatternValues binding.HeadPattern
69-
70-
| _ -> ()
71-
72-
for parentNode in context.ContainingNodes() do
73-
Interruption.Current.CheckAndThrow()
74-
75-
match parentNode with
76-
| :? IMatchClause as matchClause ->
77-
addPatternValues matchClause.Pattern
78-
79-
| :? IBinding as binding ->
80-
Seq.iter addPatternValues binding.ParameterPatterns
81-
82-
let letBindings = LetBindingsNavigator.GetByBinding(binding)
83-
if isNotNull letBindings && letBindings.IsRecursive then
84-
addLetBindingsValues letBindings
85-
86-
| :? IAccessorDeclaration as decl ->
87-
Seq.iter addPatternValues decl.ParameterPatternsEnumerable
88-
89-
| :? IFSharpTypeDeclaration as typeDecl ->
90-
let ctorDecl = typeDecl.PrimaryConstructorDeclaration
91-
if isNotNull ctorDecl then
92-
addPatternValues ctorDecl.ParameterPatterns
93-
addSelfIdValue ctorDecl.SelfIdentifier
94-
95-
| :? IFSharpExpression as expr ->
96-
let letExpr = LetOrUseExprNavigator.GetByInExpression(expr)
97-
if isNotNull letExpr then
98-
addLetBindingsValues letExpr
99-
100-
match expr with
101-
| :? ILambdaExpr as lambdaExpr ->
102-
for pattern in lambdaExpr.PatternsEnumerable do
103-
addPatternValues pattern
104-
105-
| :? IForEachExpr as forExpr ->
106-
addPatternValues forExpr.Pattern
107-
108-
| :? IForExpr as forExpr ->
109-
addValue forExpr.Identifier
110-
111-
| _ -> ()
112-
113-
| :? IModuleMember as moduleMember ->
114-
let moduleDecl = ModuleLikeDeclarationNavigator.GetByMember(moduleMember)
115-
if isNotNull moduleDecl then
116-
let isRecursive =
117-
match moduleDecl with
118-
| :? IDeclaredModuleLikeDeclaration as moduleDecl -> moduleDecl.IsRecursive
119-
| _ -> false
120-
121-
let contextOffset =
122-
if isRecursive then moduleDecl.GetTreeEndOffset() else moduleMember.GetTreeStartOffset()
123-
124-
addMembersValues contextOffset.Offset (moduleDecl.MembersEnumerable |> Seq.cast)
125-
126-
let typeDecl = FSharpTypeDeclarationNavigator.GetByTypeMember(moduleMember.As())
127-
if isNotNull typeDecl then
128-
addMembersValues (moduleMember.GetTreeStartOffset().Offset) (typeDecl.TypeMembersEnumerable |> Seq.cast)
129-
130-
| :? ITypeBodyMemberDeclaration as typeMember ->
131-
let typeDecl = FSharpTypeDeclarationNavigator.GetByTypeMember(typeMember)
132-
if isNotNull typeDecl then
133-
addMembersValues (typeMember.GetTreeStartOffset().Offset) (typeDecl.TypeMembersEnumerable |> Seq.cast)
134-
135-
let memberDecl = typeMember.As<IMemberDeclaration>()
136-
if isNotNull memberDecl then
137-
addSelfIdValue memberDecl.SelfId
138-
Seq.iter addPatternValues memberDecl.ParameterPatterns
139-
140-
| _ -> ()
141-
142-
values
143-
14431
[<Language(typeof<FSharpLanguage>)>]
14532
type LocalValuesRule() =
14633
inherit ItemsProviderOfSpecificContext<FSharpCodeCompletionContext>()
@@ -157,9 +44,9 @@ type LocalValuesRule() =
15744
override this.AddLookupItems(context, collector) =
15845
let values =
15946
let treeNode = context.ReparsedContext.TreeNode
160-
context.GetOrCreateDataUnderLock(LocalValuesRule.valuesKey, treeNode, LocalValuesRule.getLocalValues)
47+
context.GetOrCreateDataUnderLock(LocalValuesUtil.valuesKey, treeNode, LocalValuesUtil.getLocalValues)
16148

162-
for KeyValue(name, fcsSymbolUse) in values do
49+
for KeyValue(name, (_, fcsSymbolUse)) in values do
16350
let icon = if isNull fcsSymbolUse then null else getIconId fcsSymbolUse.Symbol
16451

16552
let info = FcsSymbolInfo(name, fcsSymbolUse, Ranges = context.Ranges)
@@ -181,7 +68,7 @@ type LocalValuesRule() =
18168
false
18269

18370
override this.TransformItems(context, collector) =
184-
let values = context.GetData(LocalValuesRule.valuesKey)
71+
let values = context.GetData(LocalValuesUtil.valuesKey)
18572
if isNull values then () else
18673

18774
collector.RemoveWhere(fun item ->
Lines changed: 15 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,25 @@
11
namespace JetBrains.ReSharper.Plugins.FSharp.Services.Debugger
22

3-
open FSharp.Compiler.Syntax
4-
open FSharp.Compiler.Text
5-
open JetBrains.DocumentModel
63
open JetBrains.ReSharper.Feature.Services.Debugger
74
open JetBrains.ReSharper.Plugins.FSharp.Psi
5+
open JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util
86
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
9-
open JetBrains.ReSharper.Plugins.FSharp.Util
107
open JetBrains.ReSharper.Psi
118
open JetBrains.ReSharper.Psi.Tree
129

1310
[<Language(typeof<FSharpLanguage>)>]
14-
type FSharpDebuggerLocalSymbolProvider() =
11+
type FSharpDebuggerLocalSymbolProvider2() =
1512
interface IDebuggerLocalSymbolProvider with
16-
member x.FindLocalDeclarationAt(file: IFile, range: DocumentRange, name: string) =
17-
match file.AsFSharpFile() with
18-
| null -> null, null
19-
| fsFile ->
20-
21-
match fsFile.ParseTree with
22-
| None -> null, null
23-
| Some parseTree ->
24-
25-
let pos = getPosFromDocumentOffset range.EndOffset
26-
let mutable declRange = None
27-
28-
let visitor =
29-
let inline (|SuitableIdent|_|) (ident: Ident) =
30-
if ident.idText = name && (Position.posLt ident.idRange.End pos || Position.posEq ident.idRange.End pos) then
31-
Some ident.idRange
32-
else None
33-
34-
let updateDeclRange (range: Range) =
35-
match declRange with
36-
| None ->
37-
declRange <- Some range
38-
| Some oldDeclRange when Position.posGt range.Start oldDeclRange.Start ->
39-
declRange <- Some range
40-
| _ -> ()
41-
42-
let visitPat pat defaultTraverse =
43-
match pat with
44-
| SynPat.Named(SynIdent(SuitableIdent range, _), false, _, _) -> updateDeclRange range
45-
| _ -> ()
46-
defaultTraverse pat
47-
48-
{ new SyntaxVisitorBase<_>() with
49-
member this.VisitExpr(_, traverseSynExpr, defaultTraverse, expr) =
50-
match expr with
51-
| SynExpr.For(ident = SuitableIdent range) -> updateDeclRange range
52-
| SynExpr.ForEach(pat = pat) -> visitPat pat ignore
53-
| SynExpr.App(argExpr = SynExpr.Ident (SuitableIdent range)) -> updateDeclRange range
54-
| _ -> ()
55-
defaultTraverse expr
56-
57-
member this.VisitPat(_, defaultTraverse, pat) = visitPat pat defaultTraverse
58-
59-
member this.VisitLetOrUse(_, _, _, bindings, range) =
60-
bindings |> List.tryPick (fun (SynBinding(headPat = headPat)) ->
61-
visitPat headPat (fun _ -> None))
62-
63-
member this.VisitMatchClause(_, defaultTraverse, (SynMatchClause(pat, _, _, _, _, _) as clause)) =
64-
visitPat pat (fun _ -> None) |> Option.orElseWith (fun _ -> defaultTraverse clause) }
65-
66-
SyntaxTraversal.Traverse(pos, parseTree, visitor) |> ignore
67-
68-
match declRange with
69-
| Some declRange ->
70-
let endOffset = getTreeEndOffset range.Document declRange
71-
let treeNode = fsFile.FindTokenAt(endOffset - 1)
72-
match treeNode.GetContainingNode<IFSharpDeclaration>() with
73-
| null -> treeNode, null
74-
| declaration -> treeNode, declaration.DeclaredElement
75-
76-
| None -> null, null
77-
78-
member x.FindContainingFunctionDeclarationBody(treeNode) =
79-
treeNode // todo: refactor to get tree node instead of function declaration in SDK
13+
member this.FindLocalDeclarationAt(file, range, name) =
14+
let token = file.FindTokenAt(range.StartOffset)
15+
if isNull token then null, null else
16+
17+
let values = LocalValuesUtil.getLocalValues token
18+
match values.TryGetValue(name) with
19+
| false, _ -> null, null
20+
| true, (decl, _) -> decl, decl.DeclaredElement
21+
22+
member this.FindContainingFunctionDeclarationBody(node) =
23+
match node.GetContainingNode<IChameleonExpression>() with
24+
| null -> node.GetContainingFile()
25+
| chameleonExpr -> chameleonExpr

0 commit comments

Comments
 (0)