зеркало из https://github.com/dotnet/fsharp.git
Merge pull request #15177 from dotnet/merges/main-to-release/dev17.7
Merge main to release/dev17.7
This commit is contained in:
Коммит
28d72829cd
|
@ -650,16 +650,19 @@ stages:
|
|||
displayName: Report dotnet SDK versions
|
||||
- script: .\Build.cmd $(_kind) -pack -c $(_BuildConfig)
|
||||
displayName: Initial build and prepare packages.
|
||||
- script: dotnet publish -c $(_BuildConfig) -bl:\"./bin/$(_BuildConfig)/net7.0/win-x64/publish/Trimming.binlog\"
|
||||
- script: dotnet publish -c Release -f:net472 -bl:"./artifacts/log/Release/AheadOfTime/SelfContained_Trimming_net472.binlog"
|
||||
displayName: Build and publish a trim test package.
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/projects/SelfContained_Trimming_Test
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/AheadOfTime/SelfContained_Trimming_Test
|
||||
- script: dotnet publish -c Release -f:net7.0 -bl:"./artifacts/log/Release/AheadOfTime/SelfContained_Trimming_net7.0.binlog"
|
||||
displayName: Build and publish a trim test package.
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/AheadOfTime/SelfContained_Trimming_Test
|
||||
- script: .\check.cmd
|
||||
displayName: Check the state of the trimmed app.
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/projects/SelfContained_Trimming_Test
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/AheadOfTime/SelfContained_Trimming_Test
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish Trim Tests Logs
|
||||
inputs:
|
||||
targetPath: '$(Build.SourcesDirectory)/tests/projects/SelfContained_Trimming_Test/bin/$(_BuildConfig)/net7.0/win-x64/publish'
|
||||
targetPath: './artifacts/log/Release/AheadOfTime'
|
||||
artifactName: 'Trim Test Logs Attempt $(System.JobAttempt) Logs $(_kind)'
|
||||
continueOnError: true
|
||||
condition: always()
|
||||
|
|
|
@ -539,14 +539,17 @@ let private GetCSharpStyleIndexedExtensionMembersForTyconRef (amap: Import.Impor
|
|||
if g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) then
|
||||
let ty = generalizedTyconRef g tcrefOfStaticClass
|
||||
|
||||
let minfos =
|
||||
GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty
|
||||
|> List.filter (IsMethInfoPlainCSharpStyleExtensionMember g m true)
|
||||
let csharpStyleExtensionMembers =
|
||||
if IsTyconRefUsedForCSharpStyleExtensionMembers g m tcrefOfStaticClass || tcrefOfStaticClass.IsLocalRef then
|
||||
GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty
|
||||
|> List.filter (IsMethInfoPlainCSharpStyleExtensionMember g m true)
|
||||
else
|
||||
[]
|
||||
|
||||
if IsTyconRefUsedForCSharpStyleExtensionMembers g m tcrefOfStaticClass || not minfos.IsEmpty then
|
||||
if not csharpStyleExtensionMembers.IsEmpty then
|
||||
let pri = NextExtensionMethodPriority()
|
||||
|
||||
[ for minfo in minfos do
|
||||
[ for minfo in csharpStyleExtensionMembers do
|
||||
let ilExtMem = ILExtMem (tcrefOfStaticClass, minfo, pri)
|
||||
|
||||
// The results are indexed by the TyconRef of the first 'this' argument, if any.
|
||||
|
|
|
@ -2346,29 +2346,32 @@ module InferredSigPrinting =
|
|||
|
||||
if mspec.IsImplicitNamespace then
|
||||
// The current mspec is a namespace that belongs to the `def` child (nested) module(s).
|
||||
let fullModuleName, def, denv =
|
||||
let fullModuleName, def, denv, moduleAttribs =
|
||||
let rec (|NestedModule|_|) (currentContents:ModuleOrNamespaceContents) =
|
||||
match currentContents with
|
||||
| ModuleOrNamespaceContents.TMDefRec (bindings = [ ModuleOrNamespaceBinding.Module(mn, NestedModule(path, contents)) ]) ->
|
||||
Some ([ yield mn.DisplayNameCore; yield! path ], contents)
|
||||
| ModuleOrNamespaceContents.TMDefs [ ModuleOrNamespaceContents.TMDefRec (bindings = [ ModuleOrNamespaceBinding.Module(mn, NestedModule(path, contents)) ]) ] ->
|
||||
Some ([ yield mn.DisplayNameCore; yield! path ], contents)
|
||||
| ModuleOrNamespaceContents.TMDefRec (bindings = [ ModuleOrNamespaceBinding.Module(mn, NestedModule(path, contents, attribs)) ]) ->
|
||||
Some ([ yield mn.DisplayNameCore; yield! path ], contents, List.append mn.Attribs attribs)
|
||||
| ModuleOrNamespaceContents.TMDefs [ ModuleOrNamespaceContents.TMDefRec (bindings = [ ModuleOrNamespaceBinding.Module(mn, NestedModule(path, contents, attribs)) ]) ] ->
|
||||
Some ([ yield mn.DisplayNameCore; yield! path ], contents, List.append mn.Attribs attribs)
|
||||
| ModuleOrNamespaceContents.TMDefs [ ModuleOrNamespaceContents.TMDefRec (bindings = [ ModuleOrNamespaceBinding.Module(mn, nestedModuleContents) ]) ] ->
|
||||
Some ([ mn.DisplayNameCore ], nestedModuleContents)
|
||||
Some ([ mn.DisplayNameCore ], nestedModuleContents, mn.Attribs)
|
||||
| _ ->
|
||||
None
|
||||
|
||||
match def with
|
||||
| NestedModule(path, nestedModuleContents) ->
|
||||
| NestedModule(path, nestedModuleContents, moduleAttribs) ->
|
||||
let fullPath = mspec.DisplayNameCore :: path
|
||||
fullPath, nestedModuleContents, denv.AddOpenPath(fullPath)
|
||||
| _ -> [ mspec.DisplayNameCore ], def, denv
|
||||
fullPath, nestedModuleContents, denv.AddOpenPath(fullPath), moduleAttribs
|
||||
| _ -> [ mspec.DisplayNameCore ], def, denv, List.empty
|
||||
|
||||
let nmL = List.map (tagModule >> wordL) fullModuleName |> sepListL SepL.dot
|
||||
let nmL = layoutAccessibility denv mspec.Accessibility nmL
|
||||
let denv = denv.AddAccessibility mspec.Accessibility
|
||||
let basic = imdefL denv def
|
||||
let modNameL = wordL (tagKeyword "module") ^^ nmL
|
||||
let attribs: Attribs = List.append mspec.Attribs moduleAttribs
|
||||
let modNameL =
|
||||
wordL (tagKeyword "module") ^^ nmL
|
||||
|> layoutAttribs denv None false mspec.TypeOrMeasureKind attribs
|
||||
let basicL = modNameL @@ basic
|
||||
layoutXmlDoc denv true mspec.XmlDoc basicL
|
||||
elif mspec.IsNamespace then
|
||||
|
|
|
@ -199,7 +199,7 @@ let GetDynamicallyAccessedMemberTypes (g: TcGlobals) =
|
|||
|
||||
ILType.Value(mkILNonGenericTySpec (tref))
|
||||
|
||||
let GetDynamicDependencyAttribute (g: TcGlobals) memberTypes ilType =
|
||||
let GetDynamicDependencyAttribute (g: TcGlobals) memberTypes (ilType: ILType) =
|
||||
let tref = g.attrib_DynamicDependencyAttribute.TypeRef
|
||||
|
||||
g.TryEmbedILType(
|
||||
|
@ -214,7 +214,12 @@ let GetDynamicDependencyAttribute (g: TcGlobals) memberTypes ilType =
|
|||
let typIlMemberTypes =
|
||||
ILType.Value(mkILNonGenericTySpec (g.enum_DynamicallyAccessedMemberTypes.TypeRef))
|
||||
|
||||
mkILCustomAttribute (tref, [ typIlMemberTypes; g.ilg.typ_Type ], [ ILAttribElem.Int32 memberTypes; ILAttribElem.Type(Some ilType) ], [])
|
||||
mkILCustomAttribute (
|
||||
tref,
|
||||
[ typIlMemberTypes; g.ilg.typ_Type ],
|
||||
[ ILAttribElem.Int32 memberTypes; ILAttribElem.TypeRef(Some ilType.TypeRef) ],
|
||||
[]
|
||||
)
|
||||
|
||||
/// Generate "modreq([mscorlib]System.Runtime.InteropServices.InAttribute)" on inref types.
|
||||
let GenReadOnlyModReqIfNecessary (g: TcGlobals) ty ilTy =
|
||||
|
|
|
@ -158,7 +158,7 @@ let collectGhostDependencies (fileIndex: FileIndex) (trie: TrieNode) (queryTrie:
|
|||
// We pick the lowest file index from the namespace to satisfy the type-checker for the open statement.
|
||||
connectedFileIdx < fileIndex))
|
||||
|
||||
let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInProject array) : Graph<FileIndex> =
|
||||
let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInProject array) : Graph<FileIndex> * TrieNode =
|
||||
// We know that implementation files backed by signatures cannot be depended upon.
|
||||
// Do not include them when building the Trie.
|
||||
let trieInput =
|
||||
|
@ -238,6 +238,9 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP
|
|||
|
||||
allDependencies
|
||||
|
||||
files
|
||||
|> Array.Parallel.map (fun file -> file.Idx, findDependencies file)
|
||||
|> readOnlyDict
|
||||
let graph =
|
||||
files
|
||||
|> Array.Parallel.map (fun file -> file.Idx, findDependencies file)
|
||||
|> readOnlyDict
|
||||
|
||||
graph, trie
|
||||
|
|
|
@ -16,7 +16,7 @@ val processOpenPath:
|
|||
/// <param name="compilingFSharpCore">"Are we compiling FSharp.Core?" - used to add extra dependencies for FSharp.Core that are not otherwise detectable.</param>
|
||||
/// <param name="filePairs">Maps the index of a signature file with the index of its implementation counterpart and vice versa.</param>
|
||||
/// <param name="files">The files inside a project.</param>
|
||||
/// <returns>A dictionary of FileIndex (alias for int)</returns>
|
||||
/// <returns>A tuple consisting of a dictionary of FileIndex (alias for int) and a Trie</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// *The constructed graph is a supergraph of the "necessary" file dependency graph,
|
||||
|
@ -30,4 +30,5 @@ val processOpenPath:
|
|||
/// Hence this function cannot, as it stands, be used to help create a "reasonable" file ordering for an unordered set of files.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
val mkGraph: compilingFSharpCore: bool -> filePairs: FilePairMap -> files: FileInProject array -> Graph<FileIndex>
|
||||
val mkGraph:
|
||||
compilingFSharpCore: bool -> filePairs: FilePairMap -> files: FileInProject array -> Graph<FileIndex> * TrieNode
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
module internal FSharp.Compiler.GraphChecking.TrieMapping
|
||||
|
||||
open System.Collections.Generic
|
||||
open System.Text
|
||||
open FSharp.Compiler.IO
|
||||
open FSharp.Compiler.Syntax
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
|
@ -297,3 +299,82 @@ and mkTrieForSynModuleSigDecl (fileIndex: FileIndex) (decl: SynModuleSigDecl) :
|
|||
|
||||
let mkTrie (files: FileInProject array) : TrieNode =
|
||||
mergeTrieNodes 0 (files |> Array.Parallel.map mkTrieNodeFor)
|
||||
|
||||
type MermaidBoxPos =
|
||||
| First
|
||||
| Second
|
||||
|
||||
let serializeToMermaid (path: string) (filesInProject: FileInProject array) (trie: TrieNode) =
|
||||
let sb = StringBuilder()
|
||||
let appendLine (line: string) = sb.AppendLine(line) |> ignore
|
||||
let discovered = HashSet<TrieNodeInfo>()
|
||||
|
||||
let getName (node: TrieNodeInfo) =
|
||||
match node with
|
||||
| Root _ -> "root"
|
||||
| Module (name, _) -> $"mod_{name}"
|
||||
| Namespace (name, _, _) -> $"ns_{name}"
|
||||
|
||||
let toBoxList (boxPos: MermaidBoxPos) (files: HashSet<FileIndex>) =
|
||||
let sb = StringBuilder()
|
||||
let orderedIndexes = Seq.sort files
|
||||
|
||||
let opening, closing =
|
||||
match boxPos with
|
||||
| First -> "[", "]"
|
||||
| Second -> "(", ")"
|
||||
|
||||
for file in orderedIndexes do
|
||||
let fileName = System.IO.Path.GetFileName(filesInProject[file].FileName)
|
||||
sb.Append($" {fileName}{opening}{file}{closing}\n") |> ignore
|
||||
|
||||
sb.ToString()
|
||||
|
||||
let printNode (parent: TrieNode, node: TrieNode) =
|
||||
match node.Current with
|
||||
| TrieNodeInfo.Root files ->
|
||||
let firstBox = toBoxList First files
|
||||
|
||||
if System.String.IsNullOrWhiteSpace firstBox then
|
||||
appendLine "class root\n"
|
||||
else
|
||||
appendLine $"class root {{\n{firstBox}}}\n"
|
||||
| TrieNodeInfo.Module (_name, file) as md ->
|
||||
let name = getName md
|
||||
let fileName = System.IO.Path.GetFileName(filesInProject[file].FileName)
|
||||
appendLine $"{getName parent.Current} <|-- {name}"
|
||||
appendLine $"class {name} {{\n {fileName}[{file}]\n}}\n"
|
||||
| TrieNodeInfo.Namespace (_name, filesThatExposeTypes, filesDefiningNamespaceWithoutTypes) as ns ->
|
||||
let name = getName ns
|
||||
let firstBox = toBoxList First filesThatExposeTypes
|
||||
let secondBox = toBoxList Second filesDefiningNamespaceWithoutTypes
|
||||
appendLine $"{getName parent.Current} <|-- {name}"
|
||||
|
||||
if
|
||||
System.String.IsNullOrWhiteSpace(firstBox)
|
||||
&& System.String.IsNullOrWhiteSpace(secondBox)
|
||||
then
|
||||
appendLine $"class {name}"
|
||||
else
|
||||
appendLine $"class {name} {{\n{firstBox}\n{secondBox}}}\n"
|
||||
|
||||
let rec traverse (v: TrieNode) =
|
||||
discovered.Add(v.Current) |> ignore
|
||||
|
||||
for c in v.Children do
|
||||
if not (discovered.Contains(c.Value.Current)) then
|
||||
printNode (v, c.Value)
|
||||
traverse c.Value
|
||||
|
||||
appendLine "```mermaid"
|
||||
appendLine "classDiagram\n"
|
||||
|
||||
printNode (trie, trie)
|
||||
traverse trie
|
||||
|
||||
appendLine "```"
|
||||
|
||||
use out =
|
||||
FileSystem.OpenFileForWriteShim(path, fileMode = System.IO.FileMode.Create)
|
||||
|
||||
out.WriteAllText(sb.ToString())
|
||||
|
|
|
@ -3,3 +3,5 @@ module internal FSharp.Compiler.GraphChecking.TrieMapping
|
|||
/// Process all the files (in parallel) in a project to construct a Root TrieNode.
|
||||
/// When the project has signature files, the implementation counterparts will not be processed.
|
||||
val mkTrie: files: FileInProject array -> TrieNode
|
||||
|
||||
val serializeToMermaid: path: string -> filesInProject: FileInProject array -> trie: TrieNode -> unit
|
||||
|
|
|
@ -1627,7 +1627,7 @@ let CheckMultipleInputsUsingGraphMode
|
|||
|
||||
let filePairs = FilePairMap(sourceFiles)
|
||||
|
||||
let graph =
|
||||
let graph, trie =
|
||||
DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles
|
||||
|
||||
let nodeGraph =
|
||||
|
@ -1674,6 +1674,9 @@ let CheckMultipleInputsUsingGraphMode
|
|||
let outputFile = FileSystem.GetFullPathShim(outputFile)
|
||||
let graphFile = FileSystem.ChangeExtensionShim(outputFile, ".graph.md")
|
||||
|
||||
let trieFile = FileSystem.ChangeExtensionShim(outputFile, ".trie.md")
|
||||
TrieMapping.serializeToMermaid trieFile sourceFiles trie
|
||||
|
||||
graph
|
||||
|> Graph.map (fun idx ->
|
||||
let friendlyFileName =
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<!-- empty to prevent directory crawling -->
|
||||
</Project>
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<!-- empty to prevent directory crawling -->
|
||||
</Project>
|
|
@ -5,7 +5,7 @@
|
|||
</solution>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="localPackages" value="../../../artifacts/packages/Release/Shipping"/>
|
||||
<add key="localPackages" value="../../artifacts/packages/Release/Shipping"/>
|
||||
<add key="remote" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
|
||||
</packageSources>
|
||||
<disabledPackageSources>
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFrameworks>net472;net7.0</TargetFrameworks>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DotNetBuildOffline>true</DotNetBuildOffline>
|
||||
|
@ -24,7 +24,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.fs" />
|
||||
<Compile Include="..\Program.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)../../../eng/Versions.props" />
|
|
@ -0,0 +1,35 @@
|
|||
function CheckTrim($tfm, $expected_len) {
|
||||
Write-Host "Verify trimming ${tfm}: Expected length: ${expected_len}"
|
||||
|
||||
$cwd = Get-Location
|
||||
Set-Location (Join-Path $PSScriptRoot "bin\Release\${tfm}\win-x64\publish\")
|
||||
$output = .\SelfContained_Trimming_Test.exe
|
||||
Set-Location ${cwd}
|
||||
|
||||
# Checking that it is actually running.
|
||||
if (-not ($LASTEXITCODE -eq 0))
|
||||
{
|
||||
Write-Error "Test failed with exit code ${LASTEXITCODE}" -ErrorAction Stop
|
||||
}
|
||||
|
||||
# Checking that the output is as expected.
|
||||
$expected = "All tests passed"
|
||||
if (-not ($output -eq $expected))
|
||||
{
|
||||
Write-Error "Test failed with unexpected output:`nExpected:`n`t${expected}`nActual`n`t${output}" -ErrorAction Stop
|
||||
}
|
||||
|
||||
# Checking that the trimmed FSharp.Core binary is of expected size (needs adjustments if test is updated).
|
||||
$file = Get-Item (Join-Path $PSScriptRoot "bin\Release\${tfm}\win-x64\publish\FSharp.Core.dll")
|
||||
$file_len = $file.Length
|
||||
if (-not ($file_len -eq $expected_len))
|
||||
{
|
||||
Write-Error "Test failed with unexpected ${tfm} - trimmed FSharp.Core length:`nExpected:`n`t${expected_len} Bytes`nActual:`n`t${file_len} Bytes`nEither codegen or trimming logic have changed. Please investigate and update expected dll size or report an issue." -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
|
||||
# Check net472 trimmed assemblies trimming doesn't work for tfm net472
|
||||
### CheckTrim -tfm "net472" -expected_len 287744
|
||||
|
||||
# Check net7.0 trimmed assemblies
|
||||
CheckTrim -tfm "net7.0" -expected_len 287744
|
|
@ -0,0 +1,32 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
|
||||
<NoWarn>$(NoWarn);NU1504</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Projects Include="SelfContained_Trimming_Test\SelfContained_Trimming_Test.fsproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Build">
|
||||
<MSBuild Projects="@(Projects)" Targets="Build" Properties="Configuration=$(Configuration)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Rebuild">
|
||||
<MSBuild Projects="@(Projects)" Targets="Rebuild" Properties="Configuration=$(Configuration)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Clean">
|
||||
<MSBuild Projects="@(Projects)" Targets="Clean" Properties="Configuration=$(Configuration)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Restore">
|
||||
<MSBuild Projects="@(Projects)" Targets="Restore" Properties="Configuration=$(Configuration)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Publish">
|
||||
<MSBuild Projects="@(Projects)" Targets="Publish" Properties="Configuration=$(Configuration)" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
|
@ -339,4 +339,8 @@ module Basic =
|
|||
|> verifyCompile
|
||||
|> shouldSucceed
|
||||
|
||||
|
||||
[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"EnsureValidCustomAttributeBlob.fs"|])>]
|
||||
let ``EnsureValidCustomAttributeBlob_fs`` compilation =
|
||||
compilation
|
||||
|> verifyCompileAndRun
|
||||
|> shouldSucceed
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
open System
|
||||
open System.Reflection
|
||||
open FSharp.Collections
|
||||
|
||||
let attributes = typeof<List<int>>.GetConstructor(BindingFlags.NonPublic ||| BindingFlags.Instance, Unchecked.defaultof<Binder>, Type.EmptyTypes, [||]).GetCustomAttributes()
|
||||
for a in attributes do Console.WriteLine(a.ToString())
|
|
@ -86,11 +86,11 @@
|
|||
!a item2) cil managed
|
||||
{
|
||||
.custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes,
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 43 43 6F 6D 70 61 72 65 30 37
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 3D 43 6F 6D 70 61 72 65 30 37
|
||||
2B 43 6F 6D 70 61 72 65 4D 69 63 72 6F 50 65 72
|
||||
66 41 6E 64 43 6F 64 65 47 65 6E 65 72 61 74 69
|
||||
6F 6E 54 65 73 74 73 2B 47 65 6E 65 72 69 63 4B
|
||||
65 79 60 31 5B 5B 21 30 5D 5D 00 00 )
|
||||
65 79 60 31 00 00 )
|
||||
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
|
||||
.custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 )
|
||||
|
||||
|
|
|
@ -86,11 +86,11 @@
|
|||
!a item2) cil managed
|
||||
{
|
||||
.custom instance void [runtime]System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype [runtime]System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes,
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 43 43 6F 6D 70 61 72 65 30 37
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 3D 43 6F 6D 70 61 72 65 30 37
|
||||
2B 43 6F 6D 70 61 72 65 4D 69 63 72 6F 50 65 72
|
||||
66 41 6E 64 43 6F 64 65 47 65 6E 65 72 61 74 69
|
||||
6F 6E 54 65 73 74 73 2B 47 65 6E 65 72 69 63 4B
|
||||
65 79 60 31 5B 5B 21 30 5D 5D 00 00 )
|
||||
65 79 60 31 00 00 )
|
||||
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
|
||||
.custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 )
|
||||
|
||||
|
|
|
@ -86,11 +86,11 @@
|
|||
!a item2) cil managed
|
||||
{
|
||||
.custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes,
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 41 45 71 75 61 6C 73 30 36 2B
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 3B 45 71 75 61 6C 73 30 36 2B
|
||||
45 71 75 61 6C 73 4D 69 63 72 6F 50 65 72 66 41
|
||||
6E 64 43 6F 64 65 47 65 6E 65 72 61 74 69 6F 6E
|
||||
54 65 73 74 73 2B 47 65 6E 65 72 69 63 4B 65 79
|
||||
60 31 5B 5B 21 30 5D 5D 00 00 )
|
||||
60 31 00 00 )
|
||||
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
|
||||
.custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 )
|
||||
|
||||
|
|
|
@ -86,11 +86,11 @@
|
|||
!a item2) cil managed
|
||||
{
|
||||
.custom instance void [runtime]System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype [runtime]System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes,
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 41 45 71 75 61 6C 73 30 36 2B
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 3B 45 71 75 61 6C 73 30 36 2B
|
||||
45 71 75 61 6C 73 4D 69 63 72 6F 50 65 72 66 41
|
||||
6E 64 43 6F 64 65 47 65 6E 65 72 61 74 69 6F 6E
|
||||
54 65 73 74 73 2B 47 65 6E 65 72 69 63 4B 65 79
|
||||
60 31 5B 5B 21 30 5D 5D 00 00 )
|
||||
60 31 00 00 )
|
||||
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
|
||||
.custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 )
|
||||
|
||||
|
|
|
@ -86,11 +86,10 @@
|
|||
!a item2) cil managed
|
||||
{
|
||||
.custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes,
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 3D 48 61 73 68 30 39 2B 48 61
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 37 48 61 73 68 30 39 2B 48 61
|
||||
73 68 4D 69 63 72 6F 50 65 72 66 41 6E 64 43 6F
|
||||
64 65 47 65 6E 65 72 61 74 69 6F 6E 54 65 73 74
|
||||
73 2B 47 65 6E 65 72 69 63 4B 65 79 60 31 5B 5B
|
||||
21 30 5D 5D 00 00 )
|
||||
73 2B 47 65 6E 65 72 69 63 4B 65 79 60 31 00 00 )
|
||||
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
|
||||
.custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 )
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -100,10 +100,9 @@
|
|||
!'<B>j__TPar' B) cil managed
|
||||
{
|
||||
.custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes,
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 29 3C 3E 66 5F 5F 41 6E 6F 6E
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 1E 3C 3E 66 5F 5F 41 6E 6F 6E
|
||||
79 6D 6F 75 73 54 79 70 65 31 39 31 32 37 35 36
|
||||
36 33 33 60 32 5B 5B 21 30 5D 2C 5B 21 31 5D 5D
|
||||
00 00 )
|
||||
36 33 33 60 32 00 00 )
|
||||
|
||||
.maxstack 8
|
||||
IL_0000: ldarg.0
|
||||
|
|
|
@ -100,10 +100,9 @@
|
|||
!'<B>j__TPar' B) cil managed
|
||||
{
|
||||
.custom instance void [runtime]System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype [runtime]System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes,
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 29 3C 3E 66 5F 5F 41 6E 6F 6E
|
||||
class [runtime]System.Type) = ( 01 00 60 06 00 00 1E 3C 3E 66 5F 5F 41 6E 6F 6E
|
||||
79 6D 6F 75 73 54 79 70 65 31 39 31 32 37 35 36
|
||||
36 33 33 60 32 5B 5B 21 30 5D 2C 5B 21 31 5D 5D
|
||||
00 00 )
|
||||
36 33 33 60 32 00 00 )
|
||||
|
||||
.maxstack 8
|
||||
IL_0000: ldarg.0
|
||||
|
|
|
@ -11,7 +11,7 @@ let scenarios = codebases
|
|||
let ``Supported scenario`` (scenario: Scenario) =
|
||||
let files = scenario.Files |> Array.map (fun f -> TestFileWithAST.Map f.FileWithAST)
|
||||
let filePairs = FilePairMap(files)
|
||||
let graph = DependencyResolution.mkGraph false filePairs files
|
||||
let graph, _trie = DependencyResolution.mkGraph false filePairs files
|
||||
|
||||
for file in scenario.Files do
|
||||
let expectedDeps = file.ExpectedDependencies
|
||||
|
|
|
@ -156,7 +156,7 @@ let ``Create Graph from typed tree`` (projectArgumentsFilePath: string) =
|
|||
|
||||
let filePairs = files.Values |> Seq.map TestFileWithAST.Map |> Seq.toArray |> FilePairMap
|
||||
|
||||
let graphFromHeuristic =
|
||||
let graphFromHeuristic, _trie =
|
||||
let isFSharpCore = Path.GetFileNameWithoutExtension(projectArgumentsFilePath).StartsWith("FSharp.Core")
|
||||
files.Values |> Seq.map TestFileWithAST.Map |> Seq.toArray |> DependencyResolution.mkGraph isFSharpCore filePairs
|
||||
|
||||
|
|
|
@ -126,4 +126,52 @@ module Inner =
|
|||
|
||||
/// union member
|
||||
member Thing: int
|
||||
"""
|
||||
"""
|
||||
|
||||
[<Test>]
|
||||
let ``can generate attributes for implicit namespace`` () =
|
||||
"""
|
||||
[<AutoOpen>]
|
||||
module A.B
|
||||
|
||||
open System
|
||||
|
||||
module Say =
|
||||
let hello name =
|
||||
printfn "Hello %s" name
|
||||
|
||||
let f a = a
|
||||
"""
|
||||
|> sigShouldBe """
|
||||
[<AutoOpen>]
|
||||
module A.B
|
||||
|
||||
module Say =
|
||||
|
||||
val hello: name: string -> unit
|
||||
|
||||
val f: a: 'a -> 'a"""
|
||||
|
||||
[<Test>]
|
||||
let ``can generate attributes for implicit namespace with multiple modules`` () =
|
||||
"""
|
||||
[<AutoOpen>]
|
||||
module A.B.C.D
|
||||
|
||||
open System
|
||||
|
||||
module Say =
|
||||
let hello name =
|
||||
printfn "Hello %s" name
|
||||
|
||||
let f a = a
|
||||
"""
|
||||
|> sigShouldBe """
|
||||
[<AutoOpen>]
|
||||
module A.B.C.D
|
||||
|
||||
module Say =
|
||||
|
||||
val hello: name: string -> unit
|
||||
|
||||
val f: a: 'a -> 'a"""
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
$output = .\bin\Release\net7.0\win-x64\publish\SelfContained_Trimming_Test.exe
|
||||
|
||||
# Checking that it is actually running.
|
||||
if (-not ($LASTEXITCODE -eq 0))
|
||||
{
|
||||
Write-Error "Test failed with exit code ${LASTEXITCODE}" -ErrorAction Stop
|
||||
}
|
||||
|
||||
# Checking that the output is as expected.
|
||||
$expected = "All tests passed"
|
||||
if (-not ($output -eq $expected))
|
||||
{
|
||||
Write-Error "Test failed with unexpected output:`nExpected:`n`t${expected}`nActual`n`t${output}" -ErrorAction Stop
|
||||
}
|
||||
|
||||
# Checking that FSharp.Core binary is of expected size (needs adjustments if test is updated).
|
||||
$expected_len = 287744 # In bytes
|
||||
$file = Get-Item .\bin\Release\net7.0\win-x64\publish\FSharp.Core.dll
|
||||
$file_len = $file.Length
|
||||
if (-not ($file_len -eq $expected_len))
|
||||
{
|
||||
Write-Error "Test failed with unexpected FSharp.Core length:`nExpected:`n`t${expected_len} Bytes`nActual:`n`t${file_len} Bytes`nEither codegen or trimming logic have changed. Please investigate and update expected dll size or report an issue." -ErrorAction Stop
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace FSharp.Editor.IntegrationTests;
|
||||
|
||||
public class CodeActionTests : AbstractIntegrationTest
|
||||
{
|
||||
[IdeFact]
|
||||
public async Task UnusedOpenDeclarations()
|
||||
{
|
||||
var template = WellKnownProjectTemplates.FSharpNetCoreClassLibrary;
|
||||
|
||||
var code = """
|
||||
module Library
|
||||
|
||||
open System
|
||||
|
||||
let x = 42
|
||||
""";
|
||||
|
||||
await SolutionExplorer.CreateSingleProjectSolutionAsync("Library", template, TestToken);
|
||||
await SolutionExplorer.RestoreNuGetPackagesAsync(TestToken);
|
||||
await Editor.SetTextAsync(code, TestToken);
|
||||
await Editor.PlaceCaretAsync("open System", TestToken);
|
||||
|
||||
await Workspace.WaitForProjectSystemAsync(TestToken);
|
||||
var codeActions = await Editor.InvokeCodeActionListAsync(TestToken);
|
||||
await Workspace.WaitForProjectSystemAsync(TestToken);
|
||||
|
||||
Assert.Single(codeActions);
|
||||
var actionSet = codeActions.Single();
|
||||
Assert.Equal("CodeFix", actionSet.CategoryName);
|
||||
|
||||
Assert.Single(actionSet.Actions);
|
||||
var codeFix = actionSet.Actions.Single();
|
||||
Assert.Equal("Remove unused open declarations", codeFix.DisplayText);
|
||||
}
|
||||
|
||||
[IdeFact]
|
||||
public async Task AddMissingFunKeyword()
|
||||
{
|
||||
var template = WellKnownProjectTemplates.FSharpNetCoreClassLibrary;
|
||||
|
||||
var code = """
|
||||
module Library
|
||||
|
||||
let original = []
|
||||
let transformed = original |> List.map (x -> x)
|
||||
""";
|
||||
|
||||
await SolutionExplorer.CreateSingleProjectSolutionAsync("Library", template, TestToken);
|
||||
await SolutionExplorer.RestoreNuGetPackagesAsync(TestToken);
|
||||
await Editor.SetTextAsync(code, TestToken);
|
||||
await Editor.PlaceCaretAsync("->", TestToken);
|
||||
|
||||
await Workspace.WaitForProjectSystemAsync(TestToken);
|
||||
var codeActions = await Editor.InvokeCodeActionListAsync(TestToken);
|
||||
await Workspace.WaitForProjectSystemAsync(TestToken);
|
||||
|
||||
Assert.Single(codeActions);
|
||||
var actionSet = codeActions.Single();
|
||||
Assert.Equal("ErrorFix", actionSet.CategoryName);
|
||||
|
||||
Assert.Single(actionSet.Actions);
|
||||
var errorFix = actionSet.Actions.Single();
|
||||
Assert.Equal("Add missing 'fun' keyword", errorFix.DisplayText);
|
||||
}
|
||||
|
||||
[IdeFact]
|
||||
public async Task AddNewKeywordToDisposables()
|
||||
{
|
||||
var template = WellKnownProjectTemplates.FSharpNetCoreClassLibrary;
|
||||
|
||||
var code = """
|
||||
module Library
|
||||
|
||||
let sr = System.IO.StreamReader("")
|
||||
""";
|
||||
|
||||
await SolutionExplorer.CreateSingleProjectSolutionAsync("Library", template, TestToken);
|
||||
await SolutionExplorer.RestoreNuGetPackagesAsync(TestToken);
|
||||
await Editor.SetTextAsync(code, TestToken);
|
||||
await Editor.PlaceCaretAsync("let sr", TestToken);
|
||||
|
||||
await Workspace.WaitForProjectSystemAsync(TestToken);
|
||||
var codeActions = await Editor.InvokeCodeActionListAsync(TestToken);
|
||||
await Workspace.WaitForProjectSystemAsync(TestToken);
|
||||
|
||||
Assert.Single(codeActions);
|
||||
var actionSet = codeActions.Single();
|
||||
Assert.Equal("CodeFix", actionSet.CategoryName);
|
||||
|
||||
Assert.Single(actionSet.Actions);
|
||||
var codeFix = actionSet.Actions.Single();
|
||||
Assert.Equal("Add 'new' keyword", codeFix.DisplayText);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.VisualStudio.Language.Intellisense;
|
||||
using Microsoft.VisualStudio.Text.Editor;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FSharp.Editor.IntegrationTests.Helpers
|
||||
{
|
||||
// I stole this voodoo from Razor and removed the obscurest bits
|
||||
internal static class LightBulbHelper
|
||||
{
|
||||
public static async Task<IEnumerable<SuggestedActionSet>> WaitForItemsAsync(
|
||||
ILightBulbBroker broker,
|
||||
IWpfTextView view,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var activeSession = broker.GetSession(view);
|
||||
var asyncSession = (IAsyncLightBulbSession)activeSession;
|
||||
var tcs = new TaskCompletionSource<IEnumerable<SuggestedActionSet>>();
|
||||
|
||||
void Handler(object s, SuggestedActionsUpdatedArgs e)
|
||||
{
|
||||
// ignore these. we care about when the lightbulb items are all completed.
|
||||
if (e.Status == QuerySuggestedActionCompletionStatus.InProgress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Status == QuerySuggestedActionCompletionStatus.Completed ||
|
||||
e.Status == QuerySuggestedActionCompletionStatus.CompletedWithoutData)
|
||||
{
|
||||
tcs.SetResult(e.ActionSets);
|
||||
}
|
||||
else
|
||||
{
|
||||
tcs.SetException(new InvalidOperationException($"Light bulb transitioned to non-complete state: {e.Status}"));
|
||||
}
|
||||
|
||||
asyncSession.SuggestedActionsUpdated -= Handler;
|
||||
}
|
||||
|
||||
asyncSession.SuggestedActionsUpdated += Handler;
|
||||
|
||||
asyncSession.Dismissed += (_, _) => tcs.TrySetCanceled(new CancellationToken(true));
|
||||
|
||||
if (asyncSession.IsDismissed)
|
||||
{
|
||||
tcs.TrySetCanceled(new CancellationToken(true));
|
||||
}
|
||||
|
||||
// Calling PopulateWithDataAsync ensures the underlying session will call SuggestedActionsUpdated at least once
|
||||
// with the latest data computed. This is needed so that if the lightbulb computation is already complete
|
||||
// that we hear about the results.
|
||||
await asyncSession.PopulateWithDataAsync(overrideRequestedActionCategories: null, operationContext: null).ConfigureAwait(false);
|
||||
|
||||
return await tcs.Task.WithCancellation(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,13 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FSharp.Editor.IntegrationTests.Extensions;
|
||||
using FSharp.Editor.IntegrationTests.Helpers;
|
||||
using Microsoft.VisualStudio.Language.Intellisense;
|
||||
using Microsoft.VisualStudio.OLE.Interop;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
|
@ -68,4 +72,23 @@ internal partial class EditorInProcess
|
|||
|
||||
view.Selection.Clear();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SuggestedActionSet>> InvokeCodeActionListAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
|
||||
|
||||
var shell = await GetRequiredGlobalServiceAsync<SVsUIShell, IVsUIShell>(cancellationToken);
|
||||
var cmdGroup = typeof(VSConstants.VSStd14CmdID).GUID;
|
||||
var cmdExecOpt = OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER;
|
||||
|
||||
var cmdID = VSConstants.VSStd14CmdID.ShowQuickFixes;
|
||||
object? obj = null;
|
||||
shell.PostExecCommand(cmdGroup, (uint)cmdID, (uint)cmdExecOpt, ref obj);
|
||||
|
||||
var view = await GetActiveTextViewAsync(cancellationToken);
|
||||
var broker = await GetComponentModelServiceAsync<ILightBulbBroker>(cancellationToken);
|
||||
|
||||
var lightbulbs = await LightBulbHelper.WaitForItemsAsync(broker, view, cancellationToken);
|
||||
return lightbulbs;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче