dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.89k stars 783 forks source link

Ctrl-X/Ctrl-V causing NullReferenceException, load errors on next start, invalid file reference compile order in *.fsproj #2048

Closed abelbraaksma closed 7 years ago

abelbraaksma commented 7 years ago

If your fsproj file contains two or more references to a file in a folder and between those files there's an item from another folder or from the root, the project file cannot be opened.

In other words, using regular commands or menu actions, the fsproj file can get corrupted like as follows, which compiles fine but cannot be opened in Visual Studio:

  1. somefolder\source1.fs
  2. source2.fs
  3. somefolder\source3.fs

Detailed (and, even though there's a lot of text, quite straightforward) repros follow:

Repro steps

"Manual" repro method:

  1. Create a project, add three *.fs files to the project next to the default Program.fs
  2. Create a folder (either manually and refresh, or use the Powertools extension to create a folder)
  3. Move two *.fs files to the folder (either manually, or again using the FS Powertools "Move to folder" command)
  4. Open the *.fsproj file for editing and put the remaining file in the root (from step (1)) between the two files in the folder. Your fsproj file looks something like this:
<Compile Include="New\One.fs" />
<Compile Include="Three.fs" />
<Compile Include="New\Two.fs" />
<Compile Include="Program.fs" />

Reopening the project is now impossible and you'll receive an error.

"Accidental" repro method

This is one of many ways to accidentally get to the same as above, this works without any addins installed (i.e., in SafeMode).

  1. Create a project by step 1-3 above
  2. Select and Ctrl-X a file from the root
  3. Select the folder and Ctrl-V the file

You will now get a popup "Object reference not set to an object", i.e., a NullReferenceException.

You will also see that the Ctrl-X had no effect, it acted as a Ctrl-C (copy) and paste. The filename will have Copy of prepended, even though that is not necessary as it appears in a new folder.

  1. Save the project (Ctrl-Shift-S)
  2. Reopen the project (either close VS or reload in solution explorer)
  3. Same error as before will pop up now
  4. If you inspect the fsproj file you will find the copy of file on top of all file references

Remarks/alternates/related bugs

  1. If, between step 4 and 5 above you would empty the copied file (put an empty namespace + module in it), the solution compiles
  2. If, after successful compilation, you would try to reopen the solution, the same error shows up
  3. Doing Ctrl-C/Ctrl-V with any file in a folder shows the file at the bottom of the folder, raises NullReferenceException and places the new reference on top in the fsproj file (leading to load errors next time)
  4. Same as (3), doing Ctrl-X/Ctrl-V in a folder has the same effect
  5. Doing Ctrl-C/Ctrl-V or Ctrl-X/Ctrl-V in the root folder shows that file at the bottom in solution explorer, but places the copied file on the top in the fsproj file. No load errors (but possible compile errors)
  6. Suprisingly, doing the Ctrl-C/Ctrl-V with a file from a different project within the same solution works correctly (places it at the bottom of selected folder, or root folder, both in solution explorer and fsproj file)

Notes:

  1. There are many ways to accidentally come to this situation (but I don't recall them all and some are caused by NuGet adding files, or plugins doing their "magic")
  2. It seems to be impossible to properly to copy/paste or cut/paste, however it appears to be supported when you use these keys

Expected behavior

  1. The NullReferenceException should not happen, for sure. The other error is surprising, especially if it is possible to have a loaded project that compiles, and that, upon reopening, is invalid.
  2. The popup error on "Rendering a folder multiple times" makes sense (if the fsproj file is improperly edited), except that it can be made clearer.
  3. The same popup error does not make sense if you do "normal" copy/paste in the solution folder
  4. If the project compiles, you would expect it to also load if you reopen it
  5. Ctrl-X / Ctrl-V should cut-n-paste the file, not copy-n-paste
  6. The expected location in a folder is above the selected file in the selected folder (same as C# projects), or at the bottom in the selected folder if the folder is selected and not an actual file.

Actual behavior

(the following items correspond numerically with the ones under Expected behavior)

  1. If you Ctrl-X/Ctrl-V you see this (reminiscent of a NullReferenceException):

    image

  2. You see errors when reloading:

    1. You get the following error in a popup when you reopen or reload such project:

      The project 'Experiments.fsproj' could not be opened because opening it would cause a folder to be rendered multiple times in the solution explorer. One such problematic item is 'New\Two.fs'. To open this project in Visual Studio, first edit the project file and fix the problem.

      image

    2. In the solution explorer you'll see the following:

      image

  3. An invalid project file compiles fine

  4. Ctrl-X does not cut, it leaves the original file in place and copies instead

  5. The location shown in Solution Explorer does not match the location in the fsproj file

    1. The location in the fsproj file is invalid
    2. Reopening the solution throws an error (see above)
    3. Or reopening does not throw an error but shows a different order of the files in the solution explorer

Known workarounds

  1. Fix the fsproj file manually by reordering the items or removing the offending ones.
  2. Do not use Ctrl-X/Ctrl-V or Ctrl-C/Ctrl-V or the corresponding menu items
  3. Whenever you need to reorder items in the solution explorer, do it manually (open the fsproj file and rearrange the items, and at the same time rearrange them on disk)
  4. Basically: be very careful before you rely on the solution explorer and use a good version control system to be able to rollback easily any inadvertently edited and wronged fsproj files.

Related information

I have written two repro ways above, though it is possible this has been reported and/or is reproducible with different repro steps.

I realize this error is caused by Visual Studio not being able to render a directory multiple times (it would lead to two identical directory entries in the solution explorer). I don't know if this can be resolved by the F# team. However, the root cause of this error can definitely be mitigated, i.e. by making sure that Ctrl-X / Ctrl-V either works correctly, or doesn't work at all (drop the feature).

Worries

While there is something to say for the error to popup (apart from the NRE), being able to continue working when the error does not popup means you can edit in an incorrect environment for a long time, which may lead to hard-to-resolve issues later (apart from the surprise when you find you cannot reopen your project).

Suggestions

If we have influence on the error, I would like to discuss a better text. If not, we should strive to prevent it from happening in the first place.

Reproducible:

cartermp commented 7 years ago

Tagging this for VS Updates. We should be able to address this when we move onto the new Roslyn project system.

abelbraaksma commented 7 years ago

I have tried to repro this in VS2017, but it doesn't happen anymore, thanks for the much improved folder support! 🥇