Merge pull request #15177 from dotnet/merges/main-to-release/dev17.7

Merge main to release/dev17.7
This commit is contained in:
Vlad Zarytovskii 2023-05-04 10:31:06 +02:00 коммит произвёл GitHub
Родитель 4aa2bf326f 061c52fc9c
Коммит 28d72829cd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
34 изменённых файлов: 1138 добавлений и 738 удалений

Просмотреть файл

@ -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;
}
}