Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 526d4a6

Browse files
committed
Merge remote-tracking branch 'remote/main' into analysis-fix
2 parents 7085f50 + 1df766e commit 526d4a6

22 files changed

+327
-43
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.Editor
4+
5+
open System
6+
open System.Composition
7+
open System.Threading
8+
open System.Threading.Tasks
9+
10+
open Microsoft.CodeAnalysis.Text
11+
open Microsoft.CodeAnalysis.CodeFixes
12+
13+
open FSharp.Compiler
14+
open FSharp.Compiler.EditorServices
15+
open FSharp.Compiler.Text
16+
open FSharp.Compiler.Symbols
17+
open Microsoft.CodeAnalysis.CodeActions
18+
19+
[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = "AddTypeAnnotationToObjectOfIndeterminateType"); Shared>]
20+
type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider
21+
[<ImportingConstructor>]
22+
(
23+
checkerProvider: FSharpCheckerProvider,
24+
projectInfoManager: FSharpProjectOptionsManager
25+
) =
26+
inherit CodeFixProvider()
27+
28+
static let userOpName = "AddTypeAnnotationToObjectOfIndeterminateType"
29+
30+
let fixableDiagnosticIds = set ["FS0072"; "FS3245"]
31+
32+
override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds
33+
34+
override _.RegisterCodeFixesAsync context : Task =
35+
asyncMaybe {
36+
let diagnostics =
37+
context.Diagnostics
38+
|> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id)
39+
|> Seq.toImmutableArray
40+
41+
let document = context.Document
42+
let position = context.Span.Start
43+
let checker = checkerProvider.Checker
44+
let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName)
45+
let! sourceText = document.GetTextAsync () |> liftTaskAsync
46+
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
47+
let textLine = sourceText.Lines.GetLineFromPosition position
48+
let textLinePos = sourceText.Lines.GetLinePosition position
49+
let fcsTextLineNumber = Line.fromZ textLinePos.Line
50+
let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, sourceText=sourceText, userOpName=userOpName)
51+
let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false)
52+
let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false)
53+
54+
match decl with
55+
| FindDeclResult.DeclFound declRange when declRange.FileName = document.FilePath ->
56+
let! declSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, declRange)
57+
let declTextLine = sourceText.Lines.GetLineFromPosition declSpan.Start
58+
let! declLexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false)
59+
let! symbolUse = checkFileResults.GetSymbolUseAtLocation(declRange.StartLine, declRange.EndColumn, declTextLine.ToString(), declLexerSymbol.FullIsland)
60+
match symbolUse.Symbol with
61+
| :? FSharpMemberOrFunctionOrValue as mfv ->
62+
let typeString = mfv.FullType.FormatWithConstraints symbolUse.DisplayContext
63+
64+
let alreadyWrappedInParens =
65+
let rec leftLoop ch pos =
66+
if not (Char.IsWhiteSpace(ch)) then
67+
ch = '('
68+
else
69+
leftLoop sourceText.[pos - 1] (pos - 1)
70+
71+
let rec rightLoop ch pos =
72+
if not (Char.IsWhiteSpace(ch)) then
73+
ch = ')'
74+
else
75+
rightLoop sourceText.[pos + 1] (pos + 1)
76+
77+
let hasLeftParen = leftLoop sourceText.[declSpan.Start - 1] (declSpan.Start - 1)
78+
let hasRightParen = rightLoop sourceText.[declSpan.End] declSpan.End
79+
hasLeftParen && hasRightParen
80+
81+
let getChangedText (sourceText: SourceText) =
82+
if alreadyWrappedInParens then
83+
sourceText.WithChanges(TextChange(TextSpan(declSpan.End, 0), ": " + typeString))
84+
else
85+
sourceText.WithChanges(TextChange(TextSpan(declSpan.Start, 0), "("))
86+
.WithChanges(TextChange(TextSpan(declSpan.End + 1, 0), ": " + typeString + ")"))
87+
88+
let title = SR.AddTypeAnnotation()
89+
let codeAction =
90+
CodeAction.Create(
91+
title,
92+
(fun (cancellationToken: CancellationToken) ->
93+
async {
94+
let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask
95+
return context.Document.WithText(getChangedText sourceText)
96+
} |> RoslynHelpers.StartAsyncAsTask(cancellationToken)),
97+
title)
98+
99+
context.RegisterCodeFix(codeAction, diagnostics)
100+
|_ -> ()
101+
| _ -> ()
102+
}
103+
|> Async.Ignore
104+
|> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken)

Completion/SignatureHelp.fs

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ open FSharp.Compiler.Syntax
1818
open FSharp.Compiler.Text
1919
open FSharp.Compiler.Text.Position
2020
open FSharp.Compiler.Text.Range
21-
open FSharp.Compiler.Text
2221
open FSharp.Compiler.Tokenization
2322

2423
type SignatureHelpParameterInfo =
@@ -170,7 +169,7 @@ type internal FSharpSignatureHelpProvider
170169
| n -> n
171170

172171
// Compute the current argument name if it is named.
173-
let argumentName =
172+
let namedArgumentName =
174173
if argumentIndex < paramLocations.NamedParamNames.Length then
175174
paramLocations.NamedParamNames.[argumentIndex]
176175
else
@@ -223,7 +222,7 @@ type internal FSharpSignatureHelpProvider
223222
ApplicableSpan = applicableSpan
224223
ArgumentIndex = argumentIndex
225224
ArgumentCount = argumentCount
226-
ArgumentName = argumentName
225+
ArgumentName = namedArgumentName
227226
CurrentSignatureHelpSessionKind = MethodCall }
228227

229228
return! Some data
@@ -280,7 +279,7 @@ type internal FSharpSignatureHelpProvider
280279
| None -> 0
281280
| Some (_, numArgsApplied) -> numArgsApplied
282281

283-
let definedArgs = mfv.CurriedParameterGroups |> Seq.concat |> Array.ofSeq
282+
let definedArgs = mfv.CurriedParameterGroups |> Array.ofSeq
284283

285284
let numDefinedArgs = definedArgs.Length
286285

@@ -360,40 +359,97 @@ type internal FSharpSignatureHelpProvider
360359
// Offset by 1 here until we support reverse indexes in this codebase
361360
definedArgs.[.. definedArgs.Length - 1 - numArgsAlreadyApplied] |> Array.iteri (fun index argument ->
362361
let tt = ResizeArray()
363-
let taggedText = argument.Type.FormatLayout symbolUse.DisplayContext
364-
taggedText |> Seq.iter (RoslynHelpers.CollectTaggedText tt)
362+
363+
if argument.Count = 1 then
364+
let argument = argument.[0]
365+
let taggedText = argument.Type.FormatLayout symbolUse.DisplayContext
366+
taggedText |> Seq.iter (RoslynHelpers.CollectTaggedText tt)
365367

366-
let name =
367-
if String.IsNullOrWhiteSpace(argument.DisplayName) then
368-
"arg" + string index
369-
else
370-
argument.DisplayName
368+
let name =
369+
if String.IsNullOrWhiteSpace(argument.DisplayName) then
370+
"arg" + string index
371+
else
372+
argument.DisplayName
373+
374+
let display =
375+
[|
376+
RoslynTaggedText(TextTags.Local, name)
377+
RoslynTaggedText(TextTags.Punctuation, ":")
378+
RoslynTaggedText(TextTags.Space, " ")
379+
|]
380+
|> ResizeArray
381+
382+
if argument.Type.IsFunctionType then
383+
display.Add(RoslynTaggedText(TextTags.Punctuation, "("))
384+
385+
display.AddRange(tt)
371386

372-
let display =
373-
[|
374-
RoslynTaggedText(TextTags.Local, name)
375-
RoslynTaggedText(TextTags.Punctuation, ":")
376-
RoslynTaggedText(TextTags.Space, " ")
377-
|]
378-
|> ResizeArray
387+
if argument.Type.IsFunctionType then
388+
display.Add(RoslynTaggedText(TextTags.Punctuation, ")"))
379389

380-
if argument.Type.IsFunctionType then
390+
let info =
391+
{ ParameterName = name
392+
IsOptional = false
393+
CanonicalTypeTextForSorting = name
394+
Documentation = ResizeArray()
395+
DisplayParts = display }
396+
397+
displayArgs.Add(info)
398+
else
399+
let display = ResizeArray()
381400
display.Add(RoslynTaggedText(TextTags.Punctuation, "("))
382401

383-
display.AddRange(tt)
402+
let separatorParts =
403+
[|
404+
RoslynTaggedText(TextTags.Space, " ")
405+
RoslynTaggedText(TextTags.Operator, "*")
406+
RoslynTaggedText(TextTags.Space, " ")
407+
|]
384408

385-
if argument.Type.IsFunctionType then
409+
let mutable first = true
410+
argument |> Seq.iteri (fun index arg ->
411+
if first then
412+
first <- false
413+
else
414+
display.AddRange(separatorParts)
415+
let tt = ResizeArray()
416+
417+
let taggedText = arg.Type.FormatLayout symbolUse.DisplayContext
418+
taggedText |> Seq.iter (RoslynHelpers.CollectTaggedText tt)
419+
420+
let name =
421+
if String.IsNullOrWhiteSpace(arg.DisplayName) then
422+
"arg" + string index
423+
else
424+
arg.DisplayName
425+
426+
let namePart =
427+
[|
428+
RoslynTaggedText(TextTags.Local, name)
429+
RoslynTaggedText(TextTags.Punctuation, ":")
430+
RoslynTaggedText(TextTags.Space, " ")
431+
|]
432+
433+
display.AddRange(namePart)
434+
435+
if arg.Type.IsFunctionType then
436+
display.Add(RoslynTaggedText(TextTags.Punctuation, "("))
437+
438+
display.AddRange(tt)
439+
440+
if arg.Type.IsFunctionType then
441+
display.Add(RoslynTaggedText(TextTags.Punctuation, ")")))
442+
386443
display.Add(RoslynTaggedText(TextTags.Punctuation, ")"))
444+
445+
let info =
446+
{ ParameterName = "" // No name here, since it's a tuple of arguments has no name in the F# symbol info
447+
IsOptional = false
448+
CanonicalTypeTextForSorting = ""
449+
Documentation = ResizeArray()
450+
DisplayParts = display }
387451

388-
let info =
389-
{ ParameterName = name
390-
IsOptional = false
391-
// No need to do anything different here, as this field is only relevant for overloaded parameter names in methods.
392-
CanonicalTypeTextForSorting = name
393-
Documentation = ResizeArray()
394-
DisplayParts = display }
395-
396-
displayArgs.Add(info))
452+
displayArgs.Add(info))
397453

398454
do! Option.guard (displayArgs.Count > 0)
399455

@@ -509,7 +565,7 @@ type internal FSharpSignatureHelpProvider
509565
checkFileResults,
510566
documentationBuilder,
511567
sourceText,
512-
adjustedColumnInSource,
568+
caretPosition,
513569
triggerTypedChar)
514570
}
515571

Diagnostics/SimplifyNameDiagnosticAnalyzer.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ type internal SimplifyNameDiagnosticAnalyzer
3737
asyncMaybe {
3838
do! Option.guard document.FSharpOptions.CodeFixes.SimplifyName
3939
do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds)
40-
do! Async.Sleep DefaultTuning.SimplifyNameInitialDelay |> liftAsync
4140
let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
4241
let! textVersion = document.GetTextVersionAsync(cancellationToken)
4342
let textVersionHash = textVersion.GetHashCode()

Diagnostics/UnusedDeclarationsAnalyzer.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ type internal UnusedDeclarationsAnalyzer
2828
do! Option.guard document.FSharpOptions.CodeFixes.UnusedDeclarations
2929

3030
do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds)
31-
do! Async.Sleep DefaultTuning.UnusedDeclarationsAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time
3231
match! projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) with
3332
| (_parsingOptions, projectOptions) ->
3433
let! sourceText = document.GetTextAsync()

Diagnostics/UnusedOpensDiagnosticAnalyzer.fs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ type internal UnusedOpensDiagnosticAnalyzer
3737

3838
interface IFSharpUnusedOpensDiagnosticAnalyzer with
3939

40-
member this.AnalyzeSemanticsAsync(descriptor, document: Document, cancellationToken: CancellationToken) =
40+
member _.AnalyzeSemanticsAsync(descriptor, document: Document, cancellationToken: CancellationToken) =
4141
asyncMaybe {
4242
do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds)
43-
do! Async.Sleep DefaultTuning.UnusedOpensAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time
4443
let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
4544
let! sourceText = document.GetTextAsync()
4645
let checker = checkerProvider.Checker

FSharp.Editor.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@
9090
<Compile Include="Commands\FsiCommandService.fs" />
9191
<Compile Include="Commands\XmlDocCommandService.fs" />
9292
<Compile Include="Refactor\AddExplicitTypeToParameter.fs" />
93+
<Compile Include="Refactor\ChangeDerefToValueRefactoring.fs" />
9394
<Compile Include="CodeFix\CodeFixHelpers.fs" />
95+
<Compile Include="CodeFix\AddTypeAnnotationToObjectOfIndeterminateType.fs" />
9496
<Compile Include="CodeFix\AddMissingRecToMutuallyRecFunctions.fs" />
9597
<Compile Include="CodeFix\ConvertCSharpLambdaToFSharpLambda.fs" />
9698
<Compile Include="CodeFix\MakeOuterBindingRecursive.fs" />

FSharp.Editor.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@
273273
<data name="ConvertToNotEqualsEqualityExpression" xml:space="preserve">
274274
<value>Use '&lt;&gt;' for inequality check</value>
275275
</data>
276+
<data name="UseValueInsteadOfDeref" xml:space="preserve">
277+
<value>Use '.Value' to dereference expression</value>
278+
</data>
276279
<data name="AddTypeAnnotation" xml:space="preserve">
277280
<value>Add type annotation</value>
278281
</data>

Options/EditorOptions.fs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,10 @@ namespace Microsoft.VisualStudio.FSharp.Editor
33
open System
44
open System.ComponentModel.Composition
55
open System.Runtime.InteropServices
6-
open System.Windows
7-
open System.Windows.Controls
86
open Microsoft.VisualStudio.Shell
97
open Microsoft.VisualStudio.FSharp.UIResources
108

11-
module DefaultTuning =
12-
let UnusedDeclarationsAnalyzerInitialDelay = 0 (* 1000 *) (* milliseconds *)
13-
let UnusedOpensAnalyzerInitialDelay = 0 (* 2000 *) (* milliseconds *)
14-
let SimplifyNameInitialDelay = 2000 (* milliseconds *)
15-
let SimplifyNameEachItemDelay = 0 (* milliseconds *)
16-
9+
module DefaultTuning =
1710
/// How long is the per-document data saved before it is eligible for eviction from the cache? 10 seconds.
1811
/// Re-tokenizing is fast so we don't need to save this data long.
1912
let PerDocumentSavedDataSlidingWindow = TimeSpan(0,0,10)(* seconds *)

0 commit comments

Comments
 (0)