diff --git a/tests/scripts/identifierAnalysisByType.fsx b/tests/scripts/identifierAnalysisByType.fsx new file mode 100644 index 00000000000..7ac8de7a20b --- /dev/null +++ b/tests/scripts/identifierAnalysisByType.fsx @@ -0,0 +1,152 @@ +// Print some stats about identifiers grouped by type +// + +#r "nuget: Ionide.ProjInfo" +#I @"..\..\artifacts\bin\fsc\Debug\net6.0\" +#r "FSharp.Compiler.Service.dll" + +open System +open System.IO +open Ionide.ProjInfo +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Symbols +open Ionide.ProjInfo.Types + +let argv = fsi.CommandLineArgs + +if argv.Length = 1 then + eprintfn "usage:" + eprintfn " dotnet fsi tests/scripts/identifierAnalysisByType.fsx " + eprintfn "" + eprintfn "examples:" + eprintfn " dotnet fsi tests/scripts/identifierAnalysisByType.fsx src/FSharp.Build/FSharp.Build.fsproj" + eprintfn " dotnet fsi tests/scripts/identifierAnalysisByType.fsx src/Compiler/FSharp.Compiler.Service.fsproj" + eprintfn "" + eprintfn "Sample output is at https://gist.github.com/dsyme/abfa11bebf0713251418906d55c08804" + +//let projectFile = Path.Combine(__SOURCE_DIRECTORY__, @"..\..\src\Compiler\FSharp.Compiler.Service.fsproj") +//let projectFile = Path.Combine(__SOURCE_DIRECTORY__, @"..\..\src\FSharp.Build\FSharp.Build.fsproj") +let projectFile = Path.GetFullPath(argv[1]) + +let cwd = System.Environment.CurrentDirectory |> System.IO.DirectoryInfo + +let _toolsPath = Init.init cwd None + +printfn "Cracking project options...." +let opts = + match ProjectLoader.getProjectInfo projectFile [] BinaryLogGeneration.Off [] with + | Result.Ok res -> res + | Result.Error err -> failwithf "%s" err + +let checker = FSharpChecker.Create() + +let checkerOpts = checker.GetProjectOptionsFromCommandLineArgs(projectFile, [| yield! opts.SourceFiles; yield! opts.OtherOptions |] ) + +printfn "Checking project...." +let results = checker.ParseAndCheckProject(checkerOpts) |> Async.RunSynchronously + +printfn "Grouping symbol uses...." +let symbols = results.GetAllUsesOfAllSymbols() + +let rec stripTy (ty: FSharpType) = + if ty.IsAbbreviation then stripTy ty.AbbreviatedType else ty + +let getTypeText (sym: FSharpMemberOrFunctionOrValue) = + let ty = stripTy sym.FullType + FSharpType.Prettify(ty).Format(FSharpDisplayContext.Empty) + +symbols +|> Array.choose (fun vUse -> match vUse.Symbol with :? FSharpMemberOrFunctionOrValue as v -> Some (v, vUse.Range) | _ -> None) +|> Array.filter (fun (v, _) -> v.GenericParameters.Count = 0) +|> Array.filter (fun (v, _) -> v.CurriedParameterGroups.Count = 0) +|> Array.filter (fun (v, _) -> not v.FullType.IsGenericParameter) +|> Array.map (fun (v, vUse) -> getTypeText v, v, vUse) +|> Array.filter (fun (vTypeText, v, _) -> + match vTypeText with + | "System.String" -> false + | "System.Boolean" -> false + | "System.Int32" -> false + | "System.Int64" -> false + | "System.Object" -> false + | "Microsoft.FSharp.Collections.List" -> false + | "Microsoft.FSharp.Core.Option" -> false + | s when s.EndsWith(" Microsoft.FSharp.Core.[]") -> false // for now filter array types + | _ when v.DisplayName.StartsWith "_" -> false + | _ -> true) +|> Array.groupBy (fun (vTypeText, _, _) -> vTypeText) +|> Array.map (fun (key, g) -> + key, + (g + |> Array.groupBy (fun (_, v, _) -> v.DisplayName) + |> Array.sortByDescending (snd >> Array.length))) +|> Array.filter (fun (_, g) -> g.Length > 1) +|> Array.sortByDescending (fun (key, g) -> Array.length g) +|> Array.iter (fun (key, g) -> + let key = key.Replace("Microsoft.FSharp", "FSharp").Replace("FSharp.Core.", "") + printfn "Type: %s" key + for (nm, entries) in g do + printfn " %s (%d times)" nm (Array.length entries) + for (_, _, vUse) in entries do + printfn " %s" (vUse.ToString()) + printfn "") + +(* +let isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + +let dotnet = + if isWindows then + "dotnet.exe" + else + "dotnet" +let fileExists pathToFile = + try + File.Exists(pathToFile) + with _ -> + false +// Look for global install of dotnet sdk +let getDotnetGlobalHostPath () = + let pf = Environment.GetEnvironmentVariable("ProgramW6432") + + let pf = + if String.IsNullOrEmpty(pf) then + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + else + pf + + let candidate = Path.Combine(pf, "dotnet", dotnet) + + if fileExists candidate then + Some candidate + else + // Can't find it --- give up + None + +let getDotnetHostPath () = + let probePathForDotnetHost () = + let paths = + let p = Environment.GetEnvironmentVariable("PATH") + + if not (isNull p) then + p.Split(Path.PathSeparator) + else + [||] + + paths |> Array.tryFind (fun f -> fileExists (Path.Combine(f, dotnet))) + + match (Environment.GetEnvironmentVariable("DOTNET_HOST_PATH")) with + // Value set externally + | value when not (String.IsNullOrEmpty(value)) && fileExists value -> Some value + | _ -> + // Probe for netsdk install, dotnet. and dotnet.exe is a constant offset from the location of System.Int32 + let candidate = + let assemblyLocation = Path.GetDirectoryName(typeof.Assembly.Location) + Path.GetFullPath(Path.Combine(assemblyLocation, "..", "..", "..", dotnet)) + + if fileExists candidate then + Some candidate + else + match probePathForDotnetHost () with + | Some f -> Some(Path.Combine(f, dotnet)) + | None -> getDotnetGlobalHostPath () +let dotnetExe = getDotnetHostPath () |> Option.map System.IO.FileInfo +*)