Open cloudRoutine opened 8 years ago
As I discussed with @dungpa, I would like to start working on this as part of my FSSF mentorship, as it doesn't require touching a lot of different parts of the code.
On this particular issue, the explanation is that we haven't handled Abstract attribute yet. We should handle it somewhere around https://github.com/fsprojects/VisualFSharpPowerTools/blob/7af929ea59b6c610a1f0ac0179ed8f40e30ba5f2/src/FSharp.Editing/CodeGeneration/SignatureGenerator.fs#L492-L509
It would be great to modify an existing test or add a seperate test to https://github.com/fsprojects/VisualFSharpPowerTools/tree/03c613acd3aa67cb5e3a339d3dcf1c7c783f30c2/tests/data/gotodef/generic-cases for the changes. The test is executed at https://github.com/fsprojects/VisualFSharpPowerTools/blob/03c613acd3aa67cb5e3a339d3dcf1c7c783f30c2/tests/FSharp.Editing.Tests/GoToDefinitionTests.fs#L154
Hope this gives you enough details to get started.
To verify my understanding here - is it correct that the same issue also exists for the Struct
attribute? (see https://github.com/fsprojects/VisualFSharpPowerTools/blob/03c613acd3aa67cb5e3a339d3dcf1c7c783f30c2/tests/data/gotodef/generic-cases/go-to-empty-struct-metadata/input.fs and https://github.com/fsprojects/VisualFSharpPowerTools/blob/03c613acd3aa67cb5e3a339d3dcf1c7c783f30c2/tests/data/gotodef/generic-cases/go-to-empty-struct-metadata/expected.fs)
I have been poking around a bit and trying a few things to expand my image of the situation, and I have found out a few things:
AbstractClass
attribute in the expected metadata and doesn't fail.AbstractClass
attribute in the metadata - if the class in question has been defined in F# and thus already has the attribute (I suppose this is why the test succeeds). For a class defined in C#, it will be missing, as originally reported.So, as I understand it, the issue isn't so much handling AbstractClassAttribute
, but rather adding it for an abstract class that doesn't have it. I'm currently trying to figure out how to add a test case for that as a first step.
I've started implementing the fix and have run into the first real obstacle:
The type being handled is passed to the writeType
function as a value of type FSharpEntity
. This has the problem that it doesn't contain information about whether the type is abstract (unless it is an F# abstract class, in which case the Attributes
collection contains the AbstractClassAttribute
, but this is the case we aren't here for).
I have tried to get around that with this:
let assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(typ.Assembly.FileName.Value);
let clrType = assembly.GetType(typ.FullName)
let abstractClassAttributeHasToBeAdded =
(not typ.IsFSharp)
&& typ.IsClass
&& clrType.IsAbstract
&& not (hasAttribute<AbstractClassAttribute> typ.Attributes)
This is kludgy, and I'm not sure if it works in all cases, but it does work in a simple test case. It does not work, however, for types in mscorlib
from a different framework version than VFPT, because even when using the reflection only context, it is not possible to load multiple different versions of mscorlib
at the same time.
Ideally, we would get the System.Type
corresponding to the FSharpEntity
value, and @7sharp9 said there should be a way to do that, but I haven't been able to find out how.
What I have found is a mysterious Entity
property on the FSharpEntity
value that looks like it might be helpful - but I'm only seeing that while debugging; it's simply not present at design time. I currently don't understand at all where that is coming from.
So, that Entity
property is apparently the EntityRef
used to create the FSharpEntity
, but it's hidden by the .fsi
file.
My current understanding is that ideally, there should be an IsAbstract
property on FSharpEntity
, but that obviously would require a change to FCS.
Sorry for the late reply (I have been travelling in the last few days).
A few points:
We should not go into reflection route unless there is no alternative. This is to separate between compile time and runtime tooling.
Ideally FCS should provide an IsAbstract
member for FSharpEntity
. We could send a PR if we figure out a way.
We can be sure that a C# class is an abstract class if it is not F# class (entity.IsFSharp
= false) and it has an abstract member (check all of its MemberOrValue
for their IsDispatchSlot
properties). Do you think this is an OK idea?
I thought about reflection as well, and I'm thinking the same as you. Especially as according to @7sharp9, EntityRef
is an internal TAST type, so we'd probably have to use reflection all the way to the IsAbstract
property of ILTypeDef
. That does not seem like the right thing to do.
From what I'm seeing in the debugger, I think we'd need to add this to FSharpEntity
:
member __.IsAbstract =
isResolved () &&
match entity.TypeReprInfo with
| TILObjectRepr (_, _, ilTypeDef) -> ilTypeDef.IsAbstract
| _ -> false
I'm currently trying to figure out how to build FSharp.LanguageService.Compiler.dll
with that change to test it in VFPT. I can't get it to build in either VisualFSharp nor FCS, though.
We can be sure that a C# class is an abstract class if it is not F# class (entity.IsFSharp = false) and it has an abstract member (check all of its MemberOrValue for their IsDispatchSlot properties). Do you think this is an OK idea?
I considered that as well, but that would be inexact for types that are declared abstract but have no (abstract) members. That may not make much sense, but it's possible to do.
Now I think it's best to send a PR to FSharp.Compiler.Service. We still need to add a test to FCS to check whether this works.
I'm currently trying to figure out how to build FSharp.LanguageService.Compiler.dll with that change to test it in VFPT. I can't get it to build in either VisualFSharp nor FCS, though.
Could you provide the errors you encountered? I was confused since FSharp.LanguageService.Compiler.dll
, which is part of VisualFSharp code base, has nothing to do with FSharp.Compiler.Service.
Sorry, my mistake; I wasn't aware those are different things. I have made the change to FSharp.Compiler.Service
now, compiled the DLL and referenced it in VFPT, and generating the metadata now works as expected when using the application - however, all the tests (or at least a very large number) in VFPT break with various MissingMethodException
s in (see attached NUnit test results file). I don't quite understand why - both FSharp.Editing
and FCS are .NET 4.5 and reference FSharp.Core
4.4.0.0.
As for the test to be added to FCS, where would that go, and what would it have to look like?
Looking at the test results, it seems like there are internal breaking changes in AST in FCS. Once a new FCS NuGet package is published, we should upgrade and adapt accordingly in VFPT.
For now, a test for FCS should be added to https://github.com/fsharp/FSharp.Compiler.Service/blob/7e18b8a1aa08e6d4df33bdafbd1f98743a4bb9d7/tests/service/ProjectAnalysisTests.fs
But why don't breaking changes in FCS cause compiler errors in VFPT or the tests?
Are you using the untyped ast
On 11 Dec 2016 6:12 pm, "TeaDrivenDev" notifications@github.com wrote:
But why don't breaking changes in FCS cause compiler errors in VFPT or the tests?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fsprojects/VisualFSharpPowerTools/issues/1482#issuecomment-266297371, or mute the thread https://github.com/notifications/unsubscribe-auth/AAj7yp76_5GMhZ8Le8bOxRh4DyR8aJAhks5rHD0TgaJpZM4Kmy-5 .
@TeaDrivenDev I don't know how you did it. If you take the new FCS and compile the whole VFPT solution against it, there will be compiler errors due to breaking changes.
@7sharp9 No. This happens without any changes to VFPT, just with a newly built FSharp.Compiler.Service.dll
.
@dungpa I referenced the new DLL directly in the projects. I now tried to build the FCS NuGet package to get a more meaningful result, but I'm running into errors; I have logged an issue there.
When I generate the metadata for
HostWorkspaceServices
The resulting metadata is
It should show the abstract class attribute above the type, i.e.
If attributes are generally being left out of the metadata, perhaps the fix is to add them generally? If that would add far too many attributes maybe we could identify a subset of attributes that would be most useful to add.