October 11th, 2023

Overhauled F# code fixes in Visual Studio

Petr Semkin
Software Engineer

This summer, the F# code fixes in Visual Studio underwent significant updates. We addressed a few dozens of bugs and regressions, sped things up and brought an easy way to improve, test and create code fixes. Here, we’ll share a few details on what has changed and give you tips on how you can contribute to our mission of boosting developer productivity.

Code fixes in F#

Code fixes fall under Quick Actions available in the light bulb menu. They are triggered by diagnostics – errors, warnings, or informational messages. Each diagnostic has an ID and a location, often indicated by a squiggly line. Code fixes aim to automatically resolve these diagnostics. Here is the Add missing 'fun' keyword code fix in action:

At present, Visual Studio offers over 30 F# code fixes. These include tasks like removing redundant code, converting C# constructs to F#, and adding missing import directives.

How can they be improved?

Compiler diagnostics can relate to a wide variety of situations. Take the FS0010 error as an example: it is raised for any unexpected symbol, be it an operator, keyword, or a misplaced brace. Challenges emerge when a code fix is suitable for certain instances but might lead to undesirable source modifications in others. Our primary objective has been to ensure code fixes are precise and don’t mislead developers.

And as usual, there are performance opportunities. Recently, we incorporated IcedTasks – an amazing library created by our external contributor, @TheAngryByrd. Among other benefits, the library helps reduce memory consumption and improves the management of canceled user actions. With this change, we transitioned from async to task expressions in code fixes. This shift is in line with our general recommendation when interoperating with .NET libraries.

How can they be tested?

Traditionally, much of Visual Studio’s F# functionality, including code fixes, relied on manual testing — an approach neither convenient nor sustainable. Fortunately, we’ve transitioned this year, implementing a framework tailored for unit testing code fixes.

The framework allows specifying the examples of broken code and the desired fixed code, and specify instances when code fixes should remain inactive (negative testing). This promises to ease the life of contributors dedicated to refining the F# editor.


[<Fact>]
let ``Fixes FS0010 for missing fun keyword`` () =
    let code =
        """
let getEvens numbers =
    numbers
    |> Seq.filter (x -> x % 2 = 0)
"""

    let expected =
        Some
            {
                Message = "Add missing 'fun' keyword"
                FixedCode =
                    """
let getEvens numbers =
    numbers
    |> Seq.filter (fun x -> x % 2 = 0)
"""
            }

    let actual =  AddMissingFunKeywordCodeFixProvider() |> tryFix code Auto

    Assert.Equal(expected, actual)

Get involved!

While we’ve resolved numerous issues, some are still there 🐞. Many code fixes can be further improved in the ways described above. And of course, the more code fixes the better – we have a lot of ideas for them and we welcome fresh perspectives. All of this is tracked in this ticket and some tasks we consider as good first issues.

Additionally, we’ve crafted practical guidelines on enhancing, testing, and developing F# code fixes. We are looking forward to your contributions!

Author

Petr Semkin
Software Engineer

Hey, I'm Petr - a .NET developer based in Prague, currently working in the F# compiler & tooling team. Apart from functional programming, I'm also into artificial intelligence and software testing of all sorts.

3 comments

Discussion is closed. Login to edit/delete existing comments.