diff --git a/.fantomasignore b/.fantomasignore
new file mode 100644
index 0000000..01309d9
--- /dev/null
+++ b/.fantomasignore
@@ -0,0 +1 @@
+**/bin/**/*.fs
diff --git a/Directory.Packages.props b/Directory.Packages.props
index d10cb5f..0cf9f42 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,22 +1,29 @@
-
- true
- 17.2.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ true
+ 17.2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FSharp.Analyzers.SDK.sln b/FSharp.Analyzers.SDK.sln
index 6169984..c0f192c 100644
--- a/FSharp.Analyzers.SDK.sln
+++ b/FSharp.Analyzers.SDK.sln
@@ -34,6 +34,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.github\workflows\ci.yml = .github\workflows\ci.yml
EndProjectSection
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "OptionAnalyzer.Test", "samples\OptionAnalyzer.Test\OptionAnalyzer.Test.fsproj", "{9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -83,6 +85,18 @@ Global
{E4FAFA1A-4E26-4553-81F2-844C94B85349}.Release|x64.Build.0 = Release|Any CPU
{E4FAFA1A-4E26-4553-81F2-844C94B85349}.Release|x86.ActiveCfg = Release|Any CPU
{E4FAFA1A-4E26-4553-81F2-844C94B85349}.Release|x86.Build.0 = Release|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Debug|x64.Build.0 = Debug|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Debug|x86.Build.0 = Debug|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Release|x64.ActiveCfg = Release|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Release|x64.Build.0 = Release|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Release|x86.ActiveCfg = Release|Any CPU
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C1D38B7A-0193-46AA-B033-ADBBF642AAA0} = {95A9FA19-723D-4D2C-A936-F0B45656B0D6}
@@ -90,5 +104,6 @@ Global
{E4FAFA1A-4E26-4553-81F2-844C94B85349} = {0FE81935-26A8-45E1-A62E-5148C73BA6A2}
{7A9A1C69-ADF2-421C-90F8-AB3304D6E197} = {937D2F4A-7EF4-469F-8DDA-3D75F3D32C69}
{452A16E1-35C3-4392-B969-548E701748D5} = {7A9A1C69-ADF2-421C-90F8-AB3304D6E197}
+ {9A9AC3F8-E34B-4C30-A52A-A507D6E0CA01} = {0FE81935-26A8-45E1-A62E-5148C73BA6A2}
EndGlobalSection
EndGlobal
diff --git a/samples/OptionAnalyzer.Test/OptionAnalyzer.Test.fsproj b/samples/OptionAnalyzer.Test/OptionAnalyzer.Test.fsproj
new file mode 100644
index 0000000..dc26a9f
--- /dev/null
+++ b/samples/OptionAnalyzer.Test/OptionAnalyzer.Test.fsproj
@@ -0,0 +1,32 @@
+
+
+
+ net6.0
+ LatestMajor
+
+ false
+ false
+ true
+ Library
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OptionAnalyzer.Test/UnitTests.fs b/samples/OptionAnalyzer.Test/UnitTests.fs
new file mode 100644
index 0000000..5af620a
--- /dev/null
+++ b/samples/OptionAnalyzer.Test/UnitTests.fs
@@ -0,0 +1,82 @@
+module OptionAnalyzer.Test
+
+open FSharp.Compiler.CodeAnalysis
+open NUnit.Framework
+open FSharp.Compiler.Text
+open FSharp.Analyzers.SDK
+open FSharp.Analyzers.SDK.Testing
+
+let mutable projectOptions: FSharpProjectOptions = FSharpProjectOptions.zero
+
+[]
+let Setup () =
+ task {
+ let! opts =
+ mkOptionsFromProject
+ "net7.0"
+ [
+ {
+ Name = "Newtonsoft.Json"
+ Version = "13.0.3"
+ }
+ {
+ Name = "Fantomas.FCS"
+ Version = "6.2.0"
+ }
+ ]
+
+ projectOptions <- opts
+ }
+
+[]
+let ``warnings are emitted`` () =
+ async {
+ let source =
+ """
+module M
+
+let notUsed() =
+ let option : Option = None
+ option.Value
+ """
+
+ let ctx = getContext projectOptions source
+ let! msgs = optionValueAnalyzer ctx
+ Assert.IsNotEmpty msgs
+ Assert.IsTrue(Assert.messageContains "Option.Value" msgs[0])
+
+ }
+
+[]
+let ``expected warning is emitted`` () =
+ async {
+ let source =
+ """
+module M
+
+open Newtonsoft.Json
+open Fantomas.FCS
+
+let json = JsonConvert.SerializeObject([1;2;3])
+
+let p = Fantomas.FCS.Text.Position.mkPos 23 2
+
+let notUsed() =
+ let option : Option = None
+ option.Value
+ """
+
+ let expectedMsg =
+ {
+ Code = "OV001"
+ Fixes = []
+ Message = "Option.Value shouldn't be used"
+ Range = Range.mkRange "A.fs" (Position.mkPos 13 4) (Position.mkPos 13 16)
+ Severity = Severity.Warning
+ Type = "Option.Value analyzer"
+ }
+
+ let ctx = getContext projectOptions source
+ let! msgs = optionValueAnalyzer ctx
+ Assert.IsTrue(msgs |> List.contains expectedMsg)
+ }
diff --git a/samples/OptionAnalyzer.Test/packages.lock.json b/samples/OptionAnalyzer.Test/packages.lock.json
new file mode 100644
index 0000000..5fb36c3
--- /dev/null
+++ b/samples/OptionAnalyzer.Test/packages.lock.json
@@ -0,0 +1,538 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net6.0": {
+ "coverlet.collector": {
+ "type": "Direct",
+ "requested": "[3.2.0, )",
+ "resolved": "3.2.0",
+ "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg=="
+ },
+ "DotNet.ReproducibleBuilds": {
+ "type": "Direct",
+ "requested": "[1.1.1, )",
+ "resolved": "1.1.1",
+ "contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
+ "dependencies": {
+ "Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
+ "Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
+ "Microsoft.SourceLink.GitHub": "1.1.1",
+ "Microsoft.SourceLink.GitLab": "1.1.1"
+ }
+ },
+ "FSharp.Compiler.Service": {
+ "type": "Direct",
+ "requested": "[43.7.400, )",
+ "resolved": "43.7.400",
+ "contentHash": "lg3iNkZMvuj9hOxSJ0TM1/ntcFDPRQgr1+e4bLj/avNX3FkpUtPrzCRNRpy0VP8UCNa362al5NGkVWiihxjeUQ==",
+ "dependencies": {
+ "FSharp.Core": "[7.0.400]",
+ "System.Buffers": "4.5.1",
+ "System.Collections.Immutable": "7.0.0",
+ "System.Diagnostics.DiagnosticSource": "7.0.2",
+ "System.Memory": "4.5.5",
+ "System.Reflection.Emit": "4.7.0",
+ "System.Reflection.Metadata": "7.0.0",
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "FSharp.Core": {
+ "type": "Direct",
+ "requested": "[7.0.400, )",
+ "resolved": "7.0.400",
+ "contentHash": "kJQ7TBQqd1d2VoODSgwFSAaApaBN0Fuu8mZt2XExmd3UzUxLjUsMn5Y5XhpsUWdnxOL8amp7VFg7cwhPllR4Qw=="
+ },
+ "Microsoft.NET.Test.Sdk": {
+ "type": "Direct",
+ "requested": "[17.6.0, )",
+ "resolved": "17.6.0",
+ "contentHash": "tHyg4C6c89QvLv6Utz3xKlba4EeoyJyIz59Q1NrjRENV7gfGnSE6I+sYPIbVOzQttoo2zpHDgOK/p6Hw2OlD7A==",
+ "dependencies": {
+ "Microsoft.CodeCoverage": "17.6.0",
+ "Microsoft.TestPlatform.TestHost": "17.6.0"
+ }
+ },
+ "NUnit": {
+ "type": "Direct",
+ "requested": "[3.13.3, )",
+ "resolved": "3.13.3",
+ "contentHash": "KNPDpls6EfHwC3+nnA67fh5wpxeLb3VLFAfLxrug6JMYDLHH6InaQIWR7Sc3y75d/9IKzMksH/gi08W7XWbmnQ==",
+ "dependencies": {
+ "NETStandard.Library": "2.0.0"
+ }
+ },
+ "NUnit.Analyzers": {
+ "type": "Direct",
+ "requested": "[3.6.1, )",
+ "resolved": "3.6.1",
+ "contentHash": "RKP9tpKfl3DmRgUDGgh3XM3XzeLMrCXXMZm6vm1nMsObZ6vtQL1L9NrK7+oZh1jWearvNsbMis2+AIOY3NFmow=="
+ },
+ "NUnit3TestAdapter": {
+ "type": "Direct",
+ "requested": "[4.4.2, )",
+ "resolved": "4.4.2",
+ "contentHash": "vA/iHYcR+LYw+pRWQugckC/zW2fXHaqMr2uA82NOBt8v4YK4wMJrQ7QC8XLc7PjetEZ96cPbBTWsDDtmQiRZTA=="
+ },
+ "Microsoft.Build.Tasks.Git": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "AT3HlgTjsqHnWpBHSNeR0KxbLZD7bztlZVj7I8vgeYG9SYqbeFGh0TM/KVtC6fg53nrWHl3VfZFvb5BiQFcY6Q=="
+ },
+ "Microsoft.CodeCoverage": {
+ "type": "Transitive",
+ "resolved": "17.6.0",
+ "contentHash": "5v2GwzpR7JEuQUzupjx3zLwn2FutADW/weLzLt726DR3WXxsM+ICPoJG6pxuKFsumtZp890UrVuudTUhsE8Qyg=="
+ },
+ "Microsoft.DotNet.PlatformAbstractions": {
+ "type": "Transitive",
+ "resolved": "3.1.6",
+ "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
+ },
+ "Microsoft.Extensions.DependencyModel": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "umBECCoMC+sOUgm083yFr8SxTobUOcPFH4AXigdO2xJiszCHAnmeDl4qPphJt+oaJ/XIfV1wOjIts2nRnki61Q=="
+ },
+ "Microsoft.NET.StringTools": {
+ "type": "Transitive",
+ "resolved": "1.0.0",
+ "contentHash": "ZYVcoDM0LnSyT5nWoRGfShYdOecCw2sOXWwP6j1Z0u48Xq3+BVvZ+EiPCX9/8Gz439giW+O1H1kWF9Eb/w6rVg==",
+ "dependencies": {
+ "System.Memory": "4.5.4",
+ "System.Runtime.CompilerServices.Unsafe": "5.0.0"
+ }
+ },
+ "Microsoft.NETCore.Platforms": {
+ "type": "Transitive",
+ "resolved": "3.1.0",
+ "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w=="
+ },
+ "Microsoft.NETCore.Targets": {
+ "type": "Transitive",
+ "resolved": "1.1.0",
+ "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
+ },
+ "Microsoft.SourceLink.AzureRepos.Git": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ },
+ "Microsoft.SourceLink.Bitbucket.Git": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ },
+ "Microsoft.SourceLink.Common": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "WMcGpWKrmJmzrNeuaEb23bEMnbtR/vLmvZtkAP5qWu7vQsY59GqfRJd65sFpBszbd2k/bQ8cs8eWawQKAabkVg=="
+ },
+ "Microsoft.SourceLink.GitHub": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ },
+ "Microsoft.SourceLink.GitLab": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ },
+ "Microsoft.TestPlatform.ObjectModel": {
+ "type": "Transitive",
+ "resolved": "17.6.0",
+ "contentHash": "AA/rrf5zwC5/OBLEOajkhjbVTM3SvxRXy8kcQ8e4mJKojbyZvqqhpfNg362N9vXU94DLg9NUTFOAnoYVT0pTJw==",
+ "dependencies": {
+ "NuGet.Frameworks": "5.11.0",
+ "System.Reflection.Metadata": "1.6.0"
+ }
+ },
+ "Microsoft.TestPlatform.TestHost": {
+ "type": "Transitive",
+ "resolved": "17.6.0",
+ "contentHash": "7YdgUcIeCPVKLC7n7LNKDiEHWc7z3brkkYPdUbDnFsvf6WvY9UfzS0VSUJ8P2NgN0CDSD223GCJFSjSBLZRqOQ==",
+ "dependencies": {
+ "Microsoft.TestPlatform.ObjectModel": "17.6.0",
+ "Newtonsoft.Json": "13.0.1"
+ }
+ },
+ "Microsoft.Win32.Registry": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "Lw1/VwLH1yxz6SfFEjVRCN0pnflLEsWgnV4qsdJ512/HhTwnKXUG+zDQ4yTO3K/EJQemGoNaBHX5InISNKTzUQ==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "System.Collections": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0"
+ }
+ },
+ "Microsoft.Win32.SystemEvents": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0"
+ }
+ },
+ "NETStandard.Library": {
+ "type": "Transitive",
+ "resolved": "2.0.0",
+ "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0"
+ }
+ },
+ "Newtonsoft.Json": {
+ "type": "Transitive",
+ "resolved": "13.0.1",
+ "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
+ },
+ "NuGet.Frameworks": {
+ "type": "Transitive",
+ "resolved": "5.11.0",
+ "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q=="
+ },
+ "System.Buffers": {
+ "type": "Transitive",
+ "resolved": "4.5.1",
+ "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+ },
+ "System.Collections": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Collections.Immutable": {
+ "type": "Transitive",
+ "resolved": "7.0.0",
+ "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "System.Configuration.ConfigurationManager": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "/anOTeSZCNNI2zDilogWrZ8pNqCmYbzGNexUnNhjW8k0sHqEZ2nHJBp147jBV3hGYswu5lINpNg1vxR7bnqvVA==",
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "4.7.0",
+ "System.Security.Permissions": "4.7.0"
+ }
+ },
+ "System.Diagnostics.DiagnosticSource": {
+ "type": "Transitive",
+ "resolved": "7.0.2",
+ "contentHash": "hYr3I9N9811e0Bjf2WNwAGGyTuAFbbTgX1RPLt/3Wbm68x3IGcX5Cl75CMmgT6WlNwLQ2tCCWfqYPpypjaf2xA==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "System.Drawing.Common": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "v+XbyYHaZjDfn0ENmJEV1VYLgGgCTx1gnfOBcppowbpOAriglYgGCvFCPr2EEZyBvXlpxbEsTwkOlInl107ahA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "Microsoft.Win32.SystemEvents": "4.7.0"
+ }
+ },
+ "System.Globalization": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.IO": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ }
+ },
+ "System.Memory": {
+ "type": "Transitive",
+ "resolved": "4.5.5",
+ "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
+ },
+ "System.Reflection": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.IO": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Reflection.Emit": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ=="
+ },
+ "System.Reflection.Metadata": {
+ "type": "Transitive",
+ "resolved": "7.0.0",
+ "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==",
+ "dependencies": {
+ "System.Collections.Immutable": "7.0.0"
+ }
+ },
+ "System.Reflection.Primitives": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Resources.ResourceManager": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Globalization": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0"
+ }
+ },
+ "System.Runtime.CompilerServices.Unsafe": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+ },
+ "System.Runtime.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime.Handles": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime.InteropServices": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Handles": "4.3.0"
+ }
+ },
+ "System.Security.AccessControl": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "System.Security.Principal.Windows": "4.7.0"
+ }
+ },
+ "System.Security.Cryptography.ProtectedData": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ=="
+ },
+ "System.Security.Permissions": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==",
+ "dependencies": {
+ "System.Security.AccessControl": "4.7.0",
+ "System.Windows.Extensions": "4.7.0"
+ }
+ },
+ "System.Security.Principal.Windows": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ=="
+ },
+ "System.Text.Encoding": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Text.Encoding.CodePages": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "h4z6rrA/hxWf4655D18IIZ0eaLRa3tQC/j+e26W+VinIHY0l07iEXaAvO0YSYq3MvCjMYy8Zs5AdC1sxNQOB7Q==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.Collections": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.IO": "4.1.0",
+ "System.Reflection": "4.1.0",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Runtime.Handles": "4.0.1",
+ "System.Runtime.InteropServices": "4.1.0",
+ "System.Text.Encoding": "4.0.11",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "System.Threading": {
+ "type": "Transitive",
+ "resolved": "4.0.11",
+ "contentHash": "N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==",
+ "dependencies": {
+ "System.Runtime": "4.1.0",
+ "System.Threading.Tasks": "4.0.11"
+ }
+ },
+ "System.Threading.Tasks": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Windows.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==",
+ "dependencies": {
+ "System.Drawing.Common": "4.7.0"
+ }
+ },
+ "fsharp.analyzers.sdk": {
+ "type": "Project",
+ "dependencies": {
+ "CliWrap": "[3.6.4, )",
+ "FSharp.Compiler.Service": "[43.7.400, )",
+ "FSharp.Core": "[7.0.400, )",
+ "MSBuild.StructuredLogger": "[2.1.815, )",
+ "McMaster.NETCore.Plugins": "[1.4.0, )"
+ }
+ },
+ "optionanalyzer": {
+ "type": "Project",
+ "dependencies": {
+ "FSharp.Analyzers.SDK": "[1.0.0, )",
+ "FSharp.Core": "[7.0.400, )"
+ }
+ },
+ "CliWrap": {
+ "type": "CentralTransitive",
+ "requested": "[3.6.4, )",
+ "resolved": "3.6.4",
+ "contentHash": "KVGVZlR0GWgN3Xr88oZMSzYu38TXIogwLz588e6wku3mIfg6lPchxpYWtZSZfurpTY63ANF61xWp8EZF3jkN4g=="
+ },
+ "McMaster.NETCore.Plugins": {
+ "type": "CentralTransitive",
+ "requested": "[1.4.0, )",
+ "resolved": "1.4.0",
+ "contentHash": "UKw5Z2/QHhkR7kiAJmqdCwVDMQV0lwsfj10+FG676r8DsJWIpxtachtEjE0qBs9WoK5GUQIqxgyFeYUSwuPszg==",
+ "dependencies": {
+ "Microsoft.DotNet.PlatformAbstractions": "3.1.6",
+ "Microsoft.Extensions.DependencyModel": "5.0.0"
+ }
+ },
+ "Microsoft.Build.Framework": {
+ "type": "CentralTransitive",
+ "requested": "[17.2.0, )",
+ "resolved": "16.10.0",
+ "contentHash": "uD2GUw3AYlFSpU42c/80DouuJL6w1Kb06q4FEjQhW/9wjhBwukgx13T5MPIpSvQ8ssahKINanHfMUL89EVQHgQ==",
+ "dependencies": {
+ "System.Security.Permissions": "4.7.0"
+ }
+ },
+ "Microsoft.Build.Utilities.Core": {
+ "type": "CentralTransitive",
+ "requested": "[17.2.0, )",
+ "resolved": "16.10.0",
+ "contentHash": "R8eATgdaGCfdepd67LMe1qhJz6iQOTuI9gVoOqXrHwhc77sBDqG0XD9zKvrgOqfS6NJ03KKTAhbbXnLgD5fKCA==",
+ "dependencies": {
+ "Microsoft.Build.Framework": "16.10.0",
+ "Microsoft.NET.StringTools": "1.0.0",
+ "Microsoft.Win32.Registry": "4.3.0",
+ "System.Collections.Immutable": "5.0.0",
+ "System.Configuration.ConfigurationManager": "4.7.0",
+ "System.Security.Permissions": "4.7.0",
+ "System.Text.Encoding.CodePages": "4.0.1"
+ }
+ },
+ "MSBuild.StructuredLogger": {
+ "type": "CentralTransitive",
+ "requested": "[2.1.815, )",
+ "resolved": "2.1.815",
+ "contentHash": "5UfWYgsWBGI3w0npjYLeXPv8TUcNmGB/QBhoYLDeYTosuvmG6pz6HPb+xPg/Boi1bmw2Bgnjo+XopcwzCbZAqg==",
+ "dependencies": {
+ "Microsoft.Build.Framework": "16.10.0",
+ "Microsoft.Build.Utilities.Core": "16.10.0"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/OptionAnalyzer/packages.lock.json b/samples/OptionAnalyzer/packages.lock.json
index dcf852a..4c61ff4 100644
--- a/samples/OptionAnalyzer/packages.lock.json
+++ b/samples/OptionAnalyzer/packages.lock.json
@@ -35,6 +35,25 @@
"resolved": "5.0.0",
"contentHash": "umBECCoMC+sOUgm083yFr8SxTobUOcPFH4AXigdO2xJiszCHAnmeDl4qPphJt+oaJ/XIfV1wOjIts2nRnki61Q=="
},
+ "Microsoft.NET.StringTools": {
+ "type": "Transitive",
+ "resolved": "1.0.0",
+ "contentHash": "ZYVcoDM0LnSyT5nWoRGfShYdOecCw2sOXWwP6j1Z0u48Xq3+BVvZ+EiPCX9/8Gz439giW+O1H1kWF9Eb/w6rVg==",
+ "dependencies": {
+ "System.Memory": "4.5.4",
+ "System.Runtime.CompilerServices.Unsafe": "5.0.0"
+ }
+ },
+ "Microsoft.NETCore.Platforms": {
+ "type": "Transitive",
+ "resolved": "3.1.0",
+ "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w=="
+ },
+ "Microsoft.NETCore.Targets": {
+ "type": "Transitive",
+ "resolved": "1.1.0",
+ "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
+ },
"Microsoft.SourceLink.AzureRepos.Git": {
"type": "Transitive",
"resolved": "1.1.1",
@@ -76,11 +95,44 @@
"Microsoft.SourceLink.Common": "1.1.1"
}
},
+ "Microsoft.Win32.Registry": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "Lw1/VwLH1yxz6SfFEjVRCN0pnflLEsWgnV4qsdJ512/HhTwnKXUG+zDQ4yTO3K/EJQemGoNaBHX5InISNKTzUQ==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "System.Collections": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0"
+ }
+ },
+ "Microsoft.Win32.SystemEvents": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0"
+ }
+ },
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
+ "System.Collections": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
"System.Collections.Immutable": {
"type": "Transitive",
"resolved": "7.0.0",
@@ -89,6 +141,15 @@
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
+ "System.Configuration.ConfigurationManager": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "/anOTeSZCNNI2zDilogWrZ8pNqCmYbzGNexUnNhjW8k0sHqEZ2nHJBp147jBV3hGYswu5lINpNg1vxR7bnqvVA==",
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "4.7.0",
+ "System.Security.Permissions": "4.7.0"
+ }
+ },
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "7.0.2",
@@ -97,11 +158,54 @@
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
+ "System.Drawing.Common": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "v+XbyYHaZjDfn0ENmJEV1VYLgGgCTx1gnfOBcppowbpOAriglYgGCvFCPr2EEZyBvXlpxbEsTwkOlInl107ahA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "Microsoft.Win32.SystemEvents": "4.7.0"
+ }
+ },
+ "System.Globalization": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.IO": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ }
+ },
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
},
+ "System.Reflection": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.IO": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
"System.Reflection.Emit": {
"type": "Transitive",
"resolved": "4.7.0",
@@ -115,19 +219,175 @@
"System.Collections.Immutable": "7.0.0"
}
},
+ "System.Reflection.Primitives": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Resources.ResourceManager": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Globalization": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0"
+ }
+ },
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
+ "System.Runtime.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime.Handles": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime.InteropServices": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Handles": "4.3.0"
+ }
+ },
+ "System.Security.AccessControl": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "System.Security.Principal.Windows": "4.7.0"
+ }
+ },
+ "System.Security.Cryptography.ProtectedData": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ=="
+ },
+ "System.Security.Permissions": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==",
+ "dependencies": {
+ "System.Security.AccessControl": "4.7.0",
+ "System.Windows.Extensions": "4.7.0"
+ }
+ },
+ "System.Security.Principal.Windows": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ=="
+ },
+ "System.Text.Encoding": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Text.Encoding.CodePages": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "h4z6rrA/hxWf4655D18IIZ0eaLRa3tQC/j+e26W+VinIHY0l07iEXaAvO0YSYq3MvCjMYy8Zs5AdC1sxNQOB7Q==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.Collections": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.IO": "4.1.0",
+ "System.Reflection": "4.1.0",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Runtime.Handles": "4.0.1",
+ "System.Runtime.InteropServices": "4.1.0",
+ "System.Text.Encoding": "4.0.11",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "System.Threading": {
+ "type": "Transitive",
+ "resolved": "4.0.11",
+ "contentHash": "N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==",
+ "dependencies": {
+ "System.Runtime": "4.1.0",
+ "System.Threading.Tasks": "4.0.11"
+ }
+ },
+ "System.Threading.Tasks": {
+ "type": "Transitive",
+ "resolved": "4.3.0",
+ "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0"
+ }
+ },
+ "System.Windows.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==",
+ "dependencies": {
+ "System.Drawing.Common": "4.7.0"
+ }
+ },
"fsharp.analyzers.sdk": {
"type": "Project",
"dependencies": {
+ "CliWrap": "[3.6.4, )",
"FSharp.Compiler.Service": "[43.7.400, )",
"FSharp.Core": "[7.0.400, )",
+ "MSBuild.StructuredLogger": "[2.1.815, )",
"McMaster.NETCore.Plugins": "[1.4.0, )"
}
},
+ "CliWrap": {
+ "type": "CentralTransitive",
+ "requested": "[3.6.4, )",
+ "resolved": "3.6.4",
+ "contentHash": "KVGVZlR0GWgN3Xr88oZMSzYu38TXIogwLz588e6wku3mIfg6lPchxpYWtZSZfurpTY63ANF61xWp8EZF3jkN4g=="
+ },
"FSharp.Compiler.Service": {
"type": "CentralTransitive",
"requested": "[43.7.400, )",
@@ -153,6 +413,40 @@
"Microsoft.DotNet.PlatformAbstractions": "3.1.6",
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
+ },
+ "Microsoft.Build.Framework": {
+ "type": "CentralTransitive",
+ "requested": "[17.2.0, )",
+ "resolved": "16.10.0",
+ "contentHash": "uD2GUw3AYlFSpU42c/80DouuJL6w1Kb06q4FEjQhW/9wjhBwukgx13T5MPIpSvQ8ssahKINanHfMUL89EVQHgQ==",
+ "dependencies": {
+ "System.Security.Permissions": "4.7.0"
+ }
+ },
+ "Microsoft.Build.Utilities.Core": {
+ "type": "CentralTransitive",
+ "requested": "[17.2.0, )",
+ "resolved": "16.10.0",
+ "contentHash": "R8eATgdaGCfdepd67LMe1qhJz6iQOTuI9gVoOqXrHwhc77sBDqG0XD9zKvrgOqfS6NJ03KKTAhbbXnLgD5fKCA==",
+ "dependencies": {
+ "Microsoft.Build.Framework": "16.10.0",
+ "Microsoft.NET.StringTools": "1.0.0",
+ "Microsoft.Win32.Registry": "4.3.0",
+ "System.Collections.Immutable": "5.0.0",
+ "System.Configuration.ConfigurationManager": "4.7.0",
+ "System.Security.Permissions": "4.7.0",
+ "System.Text.Encoding.CodePages": "4.0.1"
+ }
+ },
+ "MSBuild.StructuredLogger": {
+ "type": "CentralTransitive",
+ "requested": "[2.1.815, )",
+ "resolved": "2.1.815",
+ "contentHash": "5UfWYgsWBGI3w0npjYLeXPv8TUcNmGB/QBhoYLDeYTosuvmG6pz6HPb+xPg/Boi1bmw2Bgnjo+XopcwzCbZAqg==",
+ "dependencies": {
+ "Microsoft.Build.Framework": "16.10.0",
+ "Microsoft.Build.Utilities.Core": "16.10.0"
+ }
}
}
}
diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs
index c0eca1a..cd75a13 100644
--- a/src/FSharp.Analyzers.Cli/Program.fs
+++ b/src/FSharp.Analyzers.Cli/Program.fs
@@ -19,13 +19,7 @@ type Arguments =
let mutable verbose = false
-let createFCS () =
- let checker =
- FSharpChecker.Create(projectCacheSize = 200, keepAllBackgroundResolutions = true, keepAssemblyContents = true)
- // checker.ImplicitlyStartBackgroundWork <- true
- checker
-
-let fcs = createFCS ()
+let fcs = Utils.createFCS None
let parser = ArgumentParser.Create(errorHandler = ProcessExiter())
@@ -63,33 +57,6 @@ let loadProject toolsPath projPath =
return fcsPo
}
-let typeCheckFile (options: FSharpProjectOptions) (fileName: string) (sourceText: ISourceText) =
- let parseRes, checkAnswer =
- fcs.ParseAndCheckFileInProject(fileName, 0, sourceText, options)
- |> Async.RunSynchronously //ToDo: Validate if 0 is ok
-
- match checkAnswer with
- | FSharpCheckFileAnswer.Aborted ->
- printError "Checking of file %s aborted" fileName
- None
- | FSharpCheckFileAnswer.Succeeded result -> Some(parseRes, result)
-
-let createContext
- (checkProjectResults: FSharpCheckProjectResults)
- (fileName: string)
- (sourceText: ISourceText)
- ((parseFileResults: FSharpParseFileResults, checkFileResults: FSharpCheckFileResults))
- : CliContext
- =
- {
- FileName = fileName
- SourceText = sourceText
- ParseFileResults = parseFileResults
- CheckFileResults = checkFileResults
- TypedTree = checkFileResults.ImplementationFile
- CheckProjectResults = checkProjectResults
- }
-
let runProject (client: Client) toolsPath proj (globs: Glob list) =
async {
let path = Path.Combine(Environment.CurrentDirectory, proj) |> Path.GetFullPath
@@ -109,8 +76,13 @@ let runProject (client: Client) toolsPath proj
let fileContent = File.ReadAllText fileName
let sourceText = SourceText.ofString fileContent
- typeCheckFile option fileName sourceText
- |> Option.map (createContext checkProjectResults fileName sourceText)
+ Utils.typeCheckFile
+ fcs
+ (fun s -> printError "%s" s)
+ option
+ fileName
+ (Utils.SourceOfSource.SourceText sourceText)
+ |> Option.map (Utils.createContext checkProjectResults fileName sourceText)
)
|> Array.map (fun ctx ->
printInfo "Running analyzers for %s" ctx.FileName
diff --git a/src/FSharp.Analyzers.Cli/packages.lock.json b/src/FSharp.Analyzers.Cli/packages.lock.json
index 465daf1..4019be9 100644
--- a/src/FSharp.Analyzers.Cli/packages.lock.json
+++ b/src/FSharp.Analyzers.Cli/packages.lock.json
@@ -584,11 +584,19 @@
"fsharp.analyzers.sdk": {
"type": "Project",
"dependencies": {
+ "CliWrap": "[3.6.4, )",
"FSharp.Compiler.Service": "[43.7.400, )",
"FSharp.Core": "[7.0.400, )",
+ "MSBuild.StructuredLogger": "[2.1.815, )",
"McMaster.NETCore.Plugins": "[1.4.0, )"
}
},
+ "CliWrap": {
+ "type": "CentralTransitive",
+ "requested": "[3.6.4, )",
+ "resolved": "3.6.4",
+ "contentHash": "KVGVZlR0GWgN3Xr88oZMSzYu38TXIogwLz588e6wku3mIfg6lPchxpYWtZSZfurpTY63ANF61xWp8EZF3jkN4g=="
+ },
"FSharp.Compiler.Service": {
"type": "CentralTransitive",
"requested": "[43.7.400, )",
@@ -614,6 +622,16 @@
"Microsoft.DotNet.PlatformAbstractions": "3.1.6",
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
+ },
+ "MSBuild.StructuredLogger": {
+ "type": "CentralTransitive",
+ "requested": "[2.1.815, )",
+ "resolved": "2.1.815",
+ "contentHash": "5UfWYgsWBGI3w0npjYLeXPv8TUcNmGB/QBhoYLDeYTosuvmG6pz6HPb+xPg/Boi1bmw2Bgnjo+XopcwzCbZAqg==",
+ "dependencies": {
+ "Microsoft.Build.Framework": "16.10.0",
+ "Microsoft.Build.Utilities.Core": "16.10.0"
+ }
}
}
}
diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs
index 2c017bc..4f96a53 100644
--- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs
+++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs
@@ -5,6 +5,7 @@ open System.IO
open System.Collections.Concurrent
open System.Reflection
open System.Runtime.Loader
+open System.Text.RegularExpressions
open McMaster.NETCore.Plugins
type AnalysisResult =
@@ -102,8 +103,13 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC
member x.LoadAnalyzers (printError: string -> unit) (dir: string) : int * int =
if Directory.Exists dir then
let analyzerAssemblies =
+ let regex = Regex(@".*test.*\.dll$")
+
Directory.GetFiles(dir, "*Analyzer*.dll", SearchOption.AllDirectories)
- |> Array.filter (fun a -> not (a.EndsWith("FSharp.Analyzers.SDK.dll")))
+ |> Array.filter (fun a ->
+ let s = Path.GetFileName(a).ToLowerInvariant()
+ not (s.EndsWith("fsharp.analyzers.sdk.dll") || regex.IsMatch(s))
+ )
|> Array.choose (fun analyzerDll ->
try
// loads an assembly and all of it's dependencies
@@ -120,9 +126,6 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC
None
)
- let currentFSharpAnalyzersSDKVersion =
- Assembly.GetExecutingAssembly().GetName().Version
-
let findFSharpAnalyzerSDKVersion (assembly: Assembly) =
let references = assembly.GetReferencedAssemblies()
let fas = references |> Array.find (fun ra -> ra.Name = "FSharp.Analyzers.SDK")
@@ -133,11 +136,11 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC
|> Array.filter (fun (name, analyzerAssembly) ->
let version = findFSharpAnalyzerSDKVersion analyzerAssembly
- if version = currentFSharpAnalyzersSDKVersion then
+ if version = Utils.currentFSharpAnalyzersSDKVersion then
true
else
printError
- $"Trying to load %s{name} which was built using SDK version %A{version}. Expect %A{currentFSharpAnalyzersSDKVersion} instead. Assembly will be skipped."
+ $"Trying to load %s{name} which was built using SDK version %A{version}. Expect %A{Utils.currentFSharpAnalyzersSDKVersion} instead. Assembly will be skipped."
false
)
diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.TestHelpers.fs b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.TestHelpers.fs
new file mode 100644
index 0000000..83362c8
--- /dev/null
+++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.TestHelpers.fs
@@ -0,0 +1,271 @@
+module FSharp.Analyzers.SDK.Testing
+
+// Don't warn about using NotifyFileChanged of the FCS API
+#nowarn "57"
+
+open FSharp.Compiler.Text
+open Microsoft.Build.Logging.StructuredLogger
+open CliWrap
+open System
+open System.IO
+open System.Collections.Generic
+open System.Collections.ObjectModel
+open FSharp.Compiler.CodeAnalysis
+
+type FSharpProjectOptions with
+
+ static member zero =
+ {
+ ProjectFileName = ""
+ ProjectId = None
+ SourceFiles = [||]
+ OtherOptions = [||]
+ ReferencedProjects = [||]
+ IsIncompleteTypeCheckEnvironment = false
+ UseScriptResolutionRules = false
+ LoadTime = DateTime.UtcNow
+ UnresolvedReferences = None
+ OriginalLoadReferences = []
+ Stamp = None
+ }
+
+type Package =
+ {
+ Name: string
+ Version: string
+ }
+
+ override x.ToString() = $"{x.Name}_{x.Version}"
+
+let fsharpFiles = set [| ".fs"; ".fsi"; ".fsx" |]
+
+let isFSharpFile (file: string) =
+ Seq.exists (fun (ext: string) -> file.EndsWith ext) fsharpFiles
+
+let readCompilerArgsFromBinLog (build: Build) =
+ if not build.Succeeded then
+ failwith $"Build failed: {build.LogFilePath}"
+
+ let projectName =
+ build.Children
+ |> Seq.choose (
+ function
+ | :? Project as p -> Some p.Name
+ | _ -> None
+ )
+ |> Seq.distinct
+ |> Seq.exactlyOne
+
+ let message (fscTask: FscTask) =
+ fscTask.Children
+ |> Seq.tryPick (
+ function
+ | :? Message as m when m.Text.Contains "fsc" -> Some m.Text
+ | _ -> None
+ )
+
+ let mutable args = None
+
+ build.VisitAllChildren(fun task ->
+ match task with
+ | :? FscTask as fscTask ->
+ match fscTask.Parent.Parent with
+ | :? Project as p when p.Name = projectName -> args <- message fscTask
+ | _ -> ()
+ | _ -> ()
+ )
+
+ match args with
+ | None -> failwith $"Could not parse binlog at {build.LogFilePath}, does it contain CoreCompile?"
+ | Some args ->
+ let idx = args.IndexOf "-o:"
+ args.Substring(idx).Split [| '\n' |]
+
+let mkOptions (compilerArgs: string array) =
+ let sourceFiles =
+ compilerArgs
+ |> Array.filter (fun (line: string) -> isFSharpFile line && File.Exists line)
+
+ let otherOptions =
+ compilerArgs |> Array.filter (fun line -> not (isFSharpFile line))
+
+ {
+ ProjectFileName = "Project"
+ ProjectId = None
+ SourceFiles = sourceFiles
+ OtherOptions = otherOptions
+ ReferencedProjects = [||]
+ IsIncompleteTypeCheckEnvironment = false
+ UseScriptResolutionRules = false
+ LoadTime = DateTime.Now
+ UnresolvedReferences = None
+ OriginalLoadReferences = []
+ Stamp = None
+ }
+
+let mkOptionsFromBinaryLog build =
+ let compilerArgs = readCompilerArgsFromBinLog build
+ mkOptions compilerArgs
+
+let getCachedIfOldBuildSucceeded binLogPath =
+ if File.Exists binLogPath then
+ let build = BinaryLog.ReadBuild binLogPath
+
+ if build.Succeeded then
+ Some build
+ else
+ File.Delete binLogPath
+ None
+ else
+ None
+
+let createProject (binLogPath: string) (tmpProjectDir: string) (framework: string) (additionalPkgs: Package list) =
+ let stdOutBuffer = System.Text.StringBuilder()
+ let stdErrBuffer = System.Text.StringBuilder()
+
+ task {
+ try
+ Directory.CreateDirectory(tmpProjectDir) |> ignore
+
+ // needed to escape the global.json circle of influence in a unit testing process
+ let envDic = Dictionary()
+ envDic["MSBuildExtensionsPath"] <- null
+ envDic["MSBuildSDKsPath"] <- null
+ let roDic = ReadOnlyDictionary(envDic)
+
+ let! _ =
+ Cli
+ .Wrap("dotnet")
+ .WithWorkingDirectory(tmpProjectDir)
+ .WithArguments($"new classlib -f {framework} -lang F#")
+ .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
+ .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
+ .WithValidation(CommandResultValidation.ZeroExitCode)
+ .ExecuteAsync()
+
+ for p in additionalPkgs do
+ let! _ =
+ Cli
+ .Wrap("dotnet")
+ .WithEnvironmentVariables(roDic)
+ .WithWorkingDirectory(tmpProjectDir)
+ .WithArguments($"add package {p.Name} --version {p.Version}")
+ .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
+ .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
+ .WithValidation(CommandResultValidation.ZeroExitCode)
+ .ExecuteAsync()
+
+ ()
+
+ let! _ =
+ Cli
+ .Wrap("dotnet")
+ .WithEnvironmentVariables(roDic)
+ .WithWorkingDirectory(tmpProjectDir)
+ .WithArguments($"build -bl:{binLogPath}")
+ .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
+ .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
+ .WithValidation(CommandResultValidation.ZeroExitCode)
+ .ExecuteAsync()
+
+ return ()
+ with e ->
+ printfn $"StdOut:\n%s{stdOutBuffer.ToString()}"
+ printfn $"StdErr:\n%s{stdErrBuffer.ToString()}"
+ printfn $"Exception:\n%s{e.ToString()}"
+ }
+
+let mkOptionsFromProject (framework: string) (additionalPkgs: Package list) =
+ task {
+ try
+ let id = Guid.NewGuid().ToString("N")
+ let tmpProjectDir = Path.Combine(Path.GetTempPath(), id)
+
+ let uniqueBinLogName =
+ let packages =
+ additionalPkgs |> List.map (fun p -> p.ToString()) |> String.concat "_"
+
+ $"v{Utils.currentFSharpAnalyzersSDKVersion}_{framework}_{packages}.binlog"
+
+ let binLogCache =
+ Path.Combine(Path.GetTempPath(), "FSharp.Analyzers.SDK.BinLogCache")
+
+ let binLogPath = Path.Combine(binLogCache, uniqueBinLogName)
+
+ let! binLogFile =
+ let cached = getCachedIfOldBuildSucceeded binLogPath
+
+ match cached with
+ | Some f -> task { return f }
+ | None ->
+ task {
+ Directory.CreateDirectory(binLogCache) |> ignore
+ let! _ = createProject binLogPath tmpProjectDir framework additionalPkgs
+ return BinaryLog.ReadBuild binLogPath
+ }
+
+ return mkOptionsFromBinaryLog binLogFile
+ with e ->
+ printfn $"Exception:\n%s{e.ToString()}"
+ return FSharpProjectOptions.zero
+ }
+
+let getContext (opts: FSharpProjectOptions) source =
+ let fileName = "A.fs"
+ let files = Map.ofArray [| (fileName, SourceText.ofString source) |]
+
+ let documentSource fileName =
+ Map.tryFind fileName files |> async.Return
+
+ let fcs = Utils.createFCS (Some documentSource)
+ let printError (s: string) = Console.WriteLine(s)
+ let pathToAnalyzerDlls = Path.GetFullPath(".")
+
+ let foundDlls, registeredAnalyzers =
+ let client = Client()
+ client.LoadAnalyzers printError pathToAnalyzerDlls
+
+ if foundDlls = 0 then
+ failwith $"no Dlls found in {pathToAnalyzerDlls}"
+
+ if registeredAnalyzers = 0 then
+ failwith $"no Analyzers found in {pathToAnalyzerDlls}"
+
+ let opts =
+ { opts with
+ SourceFiles = [| fileName |]
+ }
+
+ fcs.NotifyFileChanged(fileName, opts) |> Async.RunSynchronously // workaround for https://github.com/dotnet/fsharp/issues/15960
+ let checkProjectResults = fcs.ParseAndCheckProject(opts) |> Async.RunSynchronously
+ let allSymbolUses = checkProjectResults.GetAllUsesOfAllSymbols()
+
+ if Array.isEmpty allSymbolUses then
+ failwith "no symboluses"
+
+ let printError s = printf $"{s}"
+
+ match Utils.typeCheckFile fcs printError opts fileName (Utils.SourceOfSource.DiscreteSource source) with
+ | Some(parseFileResults, checkFileResults) ->
+ let sourceText = SourceText.ofString source
+ Utils.createContext checkProjectResults fileName sourceText (parseFileResults, checkFileResults)
+ | None -> failwith "typechecking file failed"
+
+module Assert =
+
+ let hasWarningsInLines (expectedLines: Set) (msgs: FSharp.Analyzers.SDK.Message list) =
+ let msgLines = msgs |> List.map (fun m -> m.Range.StartLine) |> Set.ofList
+ msgLines = expectedLines
+
+ let messageContains (expectedContent: string) (msg: FSharp.Analyzers.SDK.Message) =
+ not (String.IsNullOrWhiteSpace(msg.Message))
+ && msg.Message.Contains(expectedContent)
+
+ let allMessagesContain (expectedContent: string) (msgs: FSharp.Analyzers.SDK.Message list) =
+ msgs |> List.forall (messageContains expectedContent)
+
+ let messageContainsAny (expectedContents: string list) (msg: FSharp.Analyzers.SDK.Message) =
+ expectedContents |> List.exists msg.Message.Contains
+
+ let messagesContainAny (expectedContents: string list) (msgs: FSharp.Analyzers.SDK.Message list) =
+ msgs |> List.forall (messageContainsAny expectedContents)
diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.TestHelpers.fsi b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.TestHelpers.fsi
new file mode 100644
index 0000000..b93c7d8
--- /dev/null
+++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.TestHelpers.fsi
@@ -0,0 +1,30 @@
+module FSharp.Analyzers.SDK.Testing
+
+open System.Threading.Tasks
+open FSharp.Compiler.CodeAnalysis
+
+type FSharpProjectOptions with
+
+ static member zero: FSharpProjectOptions
+
+type Package = { Name: string; Version: string }
+
+/// Creates a classlib project in a temporary folder to gather the needed FSharpProjectOptions.
+/// The target framework for the tested code to use. E.g. net6.0, net7.0
+/// A list of additional packages that should be referenced. The tested code can use these.
+/// FSharpProjectOptions
+val mkOptionsFromProject: framework: string -> additionalPkgs: Package list -> Task
+
+val getContext: opts: FSharpProjectOptions -> source: string -> CliContext
+
+module Assert =
+
+ val hasWarningsInLines: expectedLines: Set -> msgs: Message list -> bool
+
+ val messageContains: expectedContent: string -> msg: Message -> bool
+
+ val allMessagesContain: expectedContent: string -> msgs: Message list -> bool
+
+ val messageContainsAny: expectedContents: string list -> msg: Message -> bool
+
+ val messagesContainAny: expectedContents: string list -> msgs: Message list -> bool
diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fs b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fs
index ad68dee..9689562 100644
--- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fs
+++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fs
@@ -1,9 +1,12 @@
namespace FSharp.Analyzers.SDK
+#nowarn "57"
+
open System
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open FSharp.Compiler.EditorServices
+open System.Reflection
open System.Runtime.InteropServices
open FSharp.Compiler.Text
@@ -139,3 +142,69 @@ type Message =
}
type Analyzer<'TContext> = 'TContext -> Async
+
+module Utils =
+
+ let currentFSharpAnalyzersSDKVersion =
+ Assembly.GetExecutingAssembly().GetName().Version
+
+ let createContext
+ (checkProjectResults: FSharpCheckProjectResults)
+ (fileName: string)
+ (sourceText: ISourceText)
+ ((parseFileResults: FSharpParseFileResults, checkFileResults: FSharpCheckFileResults))
+ : CliContext
+ =
+ {
+ FileName = fileName
+ SourceText = sourceText
+ ParseFileResults = parseFileResults
+ CheckFileResults = checkFileResults
+ TypedTree = checkFileResults.ImplementationFile
+ CheckProjectResults = checkProjectResults
+ }
+
+ let createFCS documentSource =
+ let ds =
+ documentSource
+ |> Option.map DocumentSource.Custom
+ |> Option.defaultValue DocumentSource.FileSystem
+
+ FSharpChecker.Create(
+ projectCacheSize = 200,
+ keepAllBackgroundResolutions = true,
+ keepAssemblyContents = true,
+ documentSource = ds
+ )
+
+ []
+ type SourceOfSource =
+ | Path of string
+ | DiscreteSource of string
+ | SourceText of ISourceText
+
+ let typeCheckFile
+ (fcs: FSharpChecker)
+ (printError: (string -> unit))
+ (options: FSharpProjectOptions)
+ (fileName: string)
+ (source: SourceOfSource)
+ =
+
+ let sourceText =
+ match source with
+ | SourceOfSource.Path path ->
+ let text = System.IO.File.ReadAllText path
+ SourceText.ofString text
+ | SourceOfSource.DiscreteSource s -> SourceText.ofString s
+ | SourceOfSource.SourceText s -> s
+
+ let parseRes, checkAnswer =
+ fcs.ParseAndCheckFileInProject(fileName, 0, sourceText, options)
+ |> Async.RunSynchronously //ToDo: Validate if 0 is ok
+
+ match checkAnswer with
+ | FSharpCheckFileAnswer.Aborted ->
+ printError $"Checking of file {fileName} aborted"
+ None
+ | FSharpCheckFileAnswer.Succeeded result -> Some(parseRes, result)
diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fsi b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fsi
index 141805a..fb69758 100644
--- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fsi
+++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.fsi
@@ -120,3 +120,30 @@ type Message =
}
type Analyzer<'TContext> = 'TContext -> Async
+
+module Utils =
+
+ []
+ type SourceOfSource =
+ | Path of string
+ | DiscreteSource of string
+ | SourceText of ISourceText
+
+ val currentFSharpAnalyzersSDKVersion: Version
+
+ val createFCS: documentSource: option Async