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

Add MakeDeclarationMutable code fix #10480

Merged
merged 4 commits into from
Nov 18, 2020
Merged
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
26 changes: 26 additions & 0 deletions src/fsharp/service/ServiceUntypedParse.fs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,32 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option
match input with
| Some input -> FSharpNoteworthyParamInfoLocations.Find(pos, input)
| _ -> None

member scope.IsPositionContainedInACurriedParameter pos =
match input with
| Some input ->
let result =
AstTraversal.Traverse(pos, input, { new AstTraversal.AstVisitorBase<_>() with
member __.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) =
defaultTraverse(expr)

override __.VisitBinding (_, binding) =
match binding with
| Binding(_, _, _, _, _, _, valData, _, _, _, range, _) when rangeContainsPos range pos ->
let info = valData.SynValInfo.CurriedArgInfos
let mutable found = false
for group in info do
for arg in group do
match arg.Ident with
| Some ident when rangeContainsPos ident.idRange pos ->
found <- true
| _ -> ()
if found then Some range else None
| _ ->
None
})
result.IsSome
| _ -> false

/// Get declared items and the selected item at the specified location
member private scope.GetNavigationItemsImpl() =
Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/service/ServiceUntypedParse.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ type public FSharpParseFileResults =
/// Notable parse info for ParameterInfo at a given location
member FindNoteworthyParamInfoLocations : pos:pos -> FSharpNoteworthyParamInfoLocations option

/// Determines if the given position is contained within a curried parameter in a binding.
member IsPositionContainedInACurriedParameter: pos: pos -> bool

/// Name of the file for which this information were created
member FileName : string

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22754,6 +22754,7 @@ FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.Sourc
FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] TryRangeOfRefCellDereferenceContainingPos(pos)
FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] ValidateBreakpointLocation(pos)
FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations] FindNoteworthyParamInfoLocations(pos)
FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Boolean IsPositionContainedInACurriedParameter(pos)
FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SyntaxTree+ParsedInput] ParseTree
FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SyntaxTree+ParsedInput] get_ParseTree()
FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: System.String FileName
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor

open System.Composition
open System.Threading
open System.Threading.Tasks

open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.CodeFixes

open FSharp.Compiler.Range
open FSharp.Compiler.SourceCodeServices
open FSharp.Compiler.AbstractIL.Internal.Library

[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = "MakeDeclarationMutable"); Shared>]
type internal FSharpMakeDeclarationMutableFixProvider
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =
inherit CodeFixProvider()

static let userOpName = "MakeDeclarationMutable"

let fixableDiagnosticIds = set ["FS0027"]

override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds

override _.RegisterCodeFixesAsync context : Task =
asyncMaybe {
let diagnostics =
context.Diagnostics
|> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id)
|> Seq.toImmutableArray

let document = context.Document
do! Option.guard (not(isSignatureFile document.FilePath))
let position = context.Span.Start
let checker = checkerProvider.Checker
let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName)
let! sourceText = document.GetTextAsync () |> liftTaskAsync
let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
let textLine = sourceText.Lines.GetLineFromPosition position
let textLinePos = sourceText.Lines.GetLinePosition position
let fcsTextLineNumber = Line.fromZ textLinePos.Line
let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, sourceText=sourceText, userOpName=userOpName)
let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false)
let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false)

match decl with
// Only do this for symbols in the same file. That covers almost all cases anyways.
// We really shouldn't encourage making values mutable outside of local scopes anyways.
| FSharpFindDeclResult.DeclFound declRange when declRange.FileName = document.FilePath ->
let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, declRange)

// Bail if it's a parameter, because like, that ain't allowed
do! Option.guard (not (parseFileResults.IsPositionContainedInACurriedParameter declRange.Start))

let title = SR.MakeDeclarationMutable()
let codeFix =
CodeFixHelpers.createTextChangeCodeFix(
title,
context,
(fun () -> asyncMaybe.Return [| TextChange(TextSpan(span.Start, 0), "mutable ") |]))

context.RegisterCodeFix(codeFix, diagnostics)
| _ ->
()
}
|> Async.Ignore
|> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken)
1 change: 1 addition & 0 deletions vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<Compile Include="Commands\FsiCommandService.fs" />
<Compile Include="Commands\XmlDocCommandService.fs" />
<Compile Include="CodeFix\CodeFixHelpers.fs" />
<Compile Include="CodeFix\MakeDeclarationMutable.fs" />
<Compile Include="CodeFix\ChangeToUpcast.fs" />
<Compile Include="CodeFix\AddMissingEqualsToTypeDefinition.fs" />
<Compile Include="CodeFix\ConvertToSingleEqualsEqualityExpression.fs" />
Expand Down
3 changes: 3 additions & 0 deletions vsintegration/src/FSharp.Editor/FSharp.Editor.resx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
<data name="FSharpDisposableTopLevelValuesClassificationType" xml:space="preserve">
<value>F# Dispostable Values (top-level)</value>
</data>
<data name="MakeDeclarationMutable" xml:space="preserve">
<value>Make declaration 'mutable'</value>
</data>
<data name="UseUpcastKeyword" xml:space="preserve">
<value>Use 'upcast'</value>
</data>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Implementujte rozhraní bez anotace typu.</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">Před {0} vložte podtržítko.</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Schnittstelle ohne Typanmerkung implementieren</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">"{0}" einen Unterstrich voranstellen</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Implementar interfaz sin anotación de tipos</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">Colocar un carácter de subrayado delante de "{0}"</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Implémenter l'interface sans annotation de type</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">Faire précéder '{0}' d'un trait de soulignement</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Implementa l'interfaccia senza annotazione di tipo</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">Anteponi a '{0}' un carattere di sottolineatura</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">型の注釈を指定しないでインターフェイスを実装する</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">アンダースコアが含まれているプレフィックス '{0}'</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">형식 주석 없이 인터페이스 구현</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">밑줄이 있는 '{0}' 접두사</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Zaimplementuj interfejs bez adnotacji typu</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">Prefiks „{0}” ze znakiem podkreślenia</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Implementar a interface sem a anotação de tipo</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">Prefixo '{0}' sem sublinhado</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Реализовать интерфейс без заметки с типом</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">Добавить символ подчеркивания как префикс "{0}"</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">Tür ek açıklaması olmadan arabirim uygulama</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">'{0}' öğesinin önüne alt çizgi ekleme</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">无类型批注的实现接口</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">带下划线的前缀“{0}”</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
<target state="translated">實作沒有類型註釋的介面</target>
<note />
</trans-unit>
<trans-unit id="MakeDeclarationMutable">
<source>Make declaration 'mutable'</source>
<target state="new">Make declaration 'mutable'</target>
<note />
</trans-unit>
<trans-unit id="PrefixValueNameWithUnderscore">
<source>Prefix '{0}' with underscore</source>
<target state="translated">有底線的前置詞 '{0}'</target>
Expand Down