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

Merge main to release/dev17.7 #15311

Merged
merged 5 commits into from
Jun 6, 2023
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Signature of nested type with generic type parameter (#15259)
* Proof of concept

* Add generic parameter names to ModuleOrType.

* Revert ModuleOrType change

* Process ticks in demangledPath of TType_app.

* Only apply new logic when includeStaticParametersInTypeNames is active.

* Use FactForNETCOREAPP

* Fix build

---------

Co-authored-by: Tomas Grosup <tomasgrosup@microsoft.com>
  • Loading branch information
nojaf and T-Gro authored Jun 5, 2023
commit 97c650fe28949833c3f2bfafded2f3b3638fc028
65 changes: 55 additions & 10 deletions src/Compiler/Checking/NicePrint.fs
Original file line number Diff line number Diff line change
@@ -168,7 +168,19 @@ module internal PrintUtilities =
| GenericParameterStyle.Prefix -> true
| GenericParameterStyle.Suffix -> false

let layoutTyconRefImpl isAttribute (denv: DisplayEnv) (tcref: TyconRef) =
/// <summary>
/// Creates a layout for TyconRef.
/// </summary>
/// <param name="isAttribute"></param>
/// <param name="denv"></param>
/// <param name="tcref"></param>
/// <param name="demangledPath">
/// Used in the case the TyconRef is a nested type from another assembly which has generic type parameters in the path.
/// For example: System.Collections.Immutable.ImmutableArray&gt;'T&lt;.Builder
/// Lead to access path: System.Collections.Immutable.ImmutableArray`1
/// ImmutableArray`1 will be transformed to ImmutableArray&gt;'t&lt;
/// </param>
let layoutTyconRefImpl isAttribute (denv: DisplayEnv) (tcref: TyconRef) (demangledPath: string list option) =

let prefix = usePrefix denv tcref
let isArray = not prefix && isArrayTyconRef denv.g tcref
@@ -201,21 +213,22 @@ module internal PrintUtilities =
if denv.shortTypeNames then
tyconTextL
else
let path = tcref.CompilationPath.DemangledPath
let path =
if denv.includeStaticParametersInTypeNames then
path
Option.defaultValue tcref.CompilationPath.DemangledPath demangledPath
else
path |> List.map (fun s ->
tcref.CompilationPath.DemangledPath
|> List.map (fun s ->
let i = s.IndexOf(',')
if i <> -1 then s.Substring(0, i)+"<...>" // apparently has static params, shorten
else s)

let pathText = trimPathByDisplayEnv denv path
if pathText = "" then tyconTextL else leftL (tagUnknownEntity pathText) ^^ tyconTextL

let layoutBuiltinAttribute (denv: DisplayEnv) (attrib: BuiltinAttribInfo) =
let tcref = attrib.TyconRef
squareAngleL (layoutTyconRefImpl true denv tcref)
squareAngleL (layoutTyconRefImpl true denv tcref None)

/// layout the xml docs immediately before another block
let layoutXmlDoc (denv: DisplayEnv) alwaysAddEmptyLine (xml: XmlDoc) restL =
@@ -499,7 +512,7 @@ module PrintTypes =
layoutAccessibilityCore denv accessibility ++ itemL

/// Layout a reference to a type
let layoutTyconRef denv tcref = layoutTyconRefImpl false denv tcref
let layoutTyconRef denv tcref = layoutTyconRefImpl false denv tcref None

/// Layout the flags of a member
let layoutMemberFlags (memFlags: SynMemberFlags) =
@@ -571,7 +584,7 @@ module PrintTypes =

/// Layout an attribute 'Type(arg1, ..., argN)'
and layoutAttrib denv (Attrib(tcref, _, args, props, _, _, _)) =
let tcrefL = layoutTyconRefImpl true denv tcref
let tcrefL = layoutTyconRefImpl true denv tcref None
let argsL = bracketL (layoutAttribArgs denv args props)
if List.isEmpty args && List.isEmpty props then
tcrefL
@@ -900,7 +913,39 @@ module PrintTypes =
| TType_ucase (UnionCaseRef(tc, _), args)
| TType_app (tc, args, _) ->
let prefix = usePrefix denv tc
layoutTypeAppWithInfoAndPrec denv env (layoutTyconRef denv tc) prec prefix args
let demangledCompilationPathOpt, args =
if not denv.includeStaticParametersInTypeNames then
None, args
else
let regex = System.Text.RegularExpressions.Regex(@"\`\d+")
let path, skip =
(0, tc.CompilationPath.DemangledPath)
||> List.mapFold (fun skip path ->
// Verify the path does not contain a generic parameter count.
// For example Foo`3 indicates that there are three parameters in args that belong to this path.
let m = regex.Match(path)
if not m.Success then
path, skip
else
let take = m.Value.Replace("`", "") |> int
let genericArgs =
List.skip skip args
|> List.take take
|> List.map (layoutTypeWithInfoAndPrec denv env prec >> showL)
|> String.concat ","
|> sprintf "<%s>"
String.Concat(path.Substring(0, m.Index), genericArgs), (skip + take)
)

Some path, List.skip skip args

layoutTypeAppWithInfoAndPrec
denv
env
(layoutTyconRefImpl false denv tc demangledCompilationPathOpt)
prec
prefix
args

// Layout a tuple type
| TType_anon (anonInfo, tys) ->
@@ -1621,7 +1666,7 @@ module TastDefinitionPrinting =
let layoutExtensionMember denv infoReader (vref: ValRef) =
let (@@*) = if denv.printVerboseSignatures then (@@----) else (@@--)
let tycon = vref.MemberApparentEntity.Deref
let nameL = layoutTyconRefImpl false denv vref.MemberApparentEntity
let nameL = layoutTyconRefImpl false denv vref.MemberApparentEntity None
let nameL = layoutAccessibility denv tycon.Accessibility nameL // "type-accessibility"
let tps =
match PartitionValTyparsForApparentEnclosingType denv.g vref.Deref with
@@ -2615,7 +2660,7 @@ let stringOfFSAttrib denv x = x |> PrintTypes.layoutAttrib denv |> squareAngleL

let stringOfILAttrib denv x = x |> PrintTypes.layoutILAttrib denv |> squareAngleL |> showL

let fqnOfEntityRef g x = x |> layoutTyconRefImpl false (DisplayEnv.Empty g) |> showL
let fqnOfEntityRef g x = layoutTyconRefImpl false (DisplayEnv.Empty g) x None |> showL

let layoutImpliedSignatureOfModuleOrNamespace showHeader denv infoReader ad m contents =
InferredSigPrinting.layoutImpliedSignatureOfModuleOrNamespace showHeader denv infoReader ad m contents
3 changes: 2 additions & 1 deletion src/Compiler/TypedTree/TypedTreeOps.fs
Original file line number Diff line number Diff line change
@@ -3117,7 +3117,8 @@ type DisplayEnv =
suppressInlineKeyword = false
showDocumentation = true
shrinkOverloads = false
escapeKeywordNames = true }
escapeKeywordNames = true
includeStaticParametersInTypeNames = true }
denv.SetOpenPaths
[ FSharpLib.RootPath
FSharpLib.CorePath
Original file line number Diff line number Diff line change
@@ -247,6 +247,7 @@
<Compile Include="Signatures\ArrayTests.fs" />
<Compile Include="Signatures\TypeTests.fs" />
<Compile Include="Signatures\SigGenerationRoundTripTests.fs" />
<Compile Include="Signatures\NestedTypeTests.fs" />
<Compile Include="StaticLinking\StaticLinking.fs" />
<Compile Include="FSharpChecker\CommonWorkflows.fs" />
<Compile Include="FSharpChecker\SymbolUse.fs" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
module FSharp.Compiler.ComponentTests.Signatures.NestedTypeTests

open Xunit
open FsUnit
open FSharp.Test
open FSharp.Test.Compiler
open FSharp.Compiler.ComponentTests.Signatures.TestHelpers

[<Fact>]
let ``Nested type with generics`` () =
let CSLib =
CSharp """
namespace Lib
{
public class Upper<T>
{
public class Lower<U>
{
public void Meh()
{

}
}
}
}
"""

FSharp
"""
module Sample

open Lib

let f (g: Upper<int>.Lower<string>) = g.Meh()
"""
|> withReferences [ CSLib ]
|> printSignatures
|> should equal
"""
module Sample

val f: g: Lib.Upper<int>.Lower<string> -> unit"""

[<Fact>]
let ``Multiple generics in nested type`` () =
let CSLib =
CSharp """
namespace Lib
{
public class Root<A, B, C, D, E>
{
public class Foo<T, U, V, W>
{
public class Bar<X, Y, Z>
{
public void Meh()
{

}
}
}
}
}
"""

FSharp
"""
module Sample

open System
open Lib

let f (g: Root<TimeSpan,TimeSpan,TimeSpan,TimeSpan,TimeSpan>.Foo<int, float, string, System.DateTime>.Bar<char, int, string>) = g.Meh()
"""
|> withReferences [ CSLib ]
|> printSignatures
|> should equal
"""
module Sample

val f: g: Lib.Root<System.TimeSpan,System.TimeSpan,System.TimeSpan,System.TimeSpan,System.TimeSpan>.Foo<int,float,string,System.DateTime>.Bar<char,int,string> -> unit"""

[<FactForNETCOREAPP>]
let ``ImmutableArray<'T>.Builder roundtrip`` () =
let impl =
"""
module Library

open System.Collections.Immutable

type ImmutableArrayViaBuilder<'T>(builder: ImmutableArray<'T>.Builder) = class end
"""

let signature = printSignatures (Fs impl)

Fsi signature
|> withAdditionalSourceFile (FsSource impl)
|> compile
|> shouldSucceed
24 changes: 23 additions & 1 deletion tests/FSharp.Test.Utilities/Compiler.fs
Original file line number Diff line number Diff line change
@@ -891,7 +891,29 @@ module rec Compiler =
| FS fsSource ->
let source = fsSource.Source.GetSourceText |> Option.defaultValue ""
let fileName = fsSource.Source.ChangeExtension.GetSourceFileName
let options = fsSource.Options |> Array.ofList

let references =
let disposals = ResizeArray<IDisposable>()
let outputDirectory =
match fsSource.OutputDirectory with
| Some di -> di
| None -> DirectoryInfo(tryCreateTemporaryDirectory())
let references = processReferences fsSource.References outputDirectory
if references.IsEmpty then
Array.empty
else
outputDirectory.Create()
disposals.Add({ new IDisposable with member _.Dispose() = outputDirectory.Delete(true) })
// Note that only the references are relevant here
let compilation = Compilation.Compilation([], CompileOutput.Exe,Array.empty, TargetFramework.Current, references, None, None)
evaluateReferences outputDirectory disposals fsSource.IgnoreWarnings compilation
|> fst

let options =
[|
yield! fsSource.Options |> Array.ofList
yield! references
|]
CompilerAssert.TypeCheck(options, fileName, source)
| _ -> failwith "Typecheck only supports F#"