|
| 1 | +// Print some stats about identifiers grouped by type |
| 2 | +// |
| 3 | + |
| 4 | +#r "nuget: Ionide.ProjInfo" |
| 5 | +#I @"..\..\artifacts\bin\fsc\Debug\net6.0\" |
| 6 | +#r "FSharp.Compiler.Service.dll" |
| 7 | + |
| 8 | +open System |
| 9 | +open System.IO |
| 10 | +open Ionide.ProjInfo |
| 11 | +open FSharp.Compiler.CodeAnalysis |
| 12 | +open FSharp.Compiler.Symbols |
| 13 | +open Ionide.ProjInfo.Types |
| 14 | + |
| 15 | +let argv = fsi.CommandLineArgs |
| 16 | + |
| 17 | +if argv.Length = 1 then |
| 18 | + eprintfn "usage:" |
| 19 | + eprintfn " dotnet fsi tests/scripts/identifierAnalysisByType.fsx <project-file>" |
| 20 | + eprintfn "" |
| 21 | + eprintfn "examples:" |
| 22 | + eprintfn " dotnet fsi tests/scripts/identifierAnalysisByType.fsx src/FSharp.Build/FSharp.Build.fsproj" |
| 23 | + eprintfn " dotnet fsi tests/scripts/identifierAnalysisByType.fsx src/Compiler/FSharp.Compiler.Service.fsproj" |
| 24 | + eprintfn "" |
| 25 | + eprintfn "Sample output is at https://gist.github.com/dsyme/abfa11bebf0713251418906d55c08804" |
| 26 | + |
| 27 | +//let projectFile = Path.Combine(__SOURCE_DIRECTORY__, @"..\..\src\Compiler\FSharp.Compiler.Service.fsproj") |
| 28 | +//let projectFile = Path.Combine(__SOURCE_DIRECTORY__, @"..\..\src\FSharp.Build\FSharp.Build.fsproj") |
| 29 | +let projectFile = Path.GetFullPath(argv[1]) |
| 30 | + |
| 31 | +let cwd = System.Environment.CurrentDirectory |> System.IO.DirectoryInfo |
| 32 | + |
| 33 | +let _toolsPath = Init.init cwd None |
| 34 | + |
| 35 | +printfn "Cracking project options...." |
| 36 | +let opts = |
| 37 | + match ProjectLoader.getProjectInfo projectFile [] BinaryLogGeneration.Off [] with |
| 38 | + | Result.Ok res -> res |
| 39 | + | Result.Error err -> failwithf "%s" err |
| 40 | + |
| 41 | +let checker = FSharpChecker.Create() |
| 42 | + |
| 43 | +let checkerOpts = checker.GetProjectOptionsFromCommandLineArgs(projectFile, [| yield! opts.SourceFiles; yield! opts.OtherOptions |] ) |
| 44 | + |
| 45 | +printfn "Checking project...." |
| 46 | +let results = checker.ParseAndCheckProject(checkerOpts) |> Async.RunSynchronously |
| 47 | + |
| 48 | +printfn "Grouping symbol uses...." |
| 49 | +let symbols = results.GetAllUsesOfAllSymbols() |
| 50 | + |
| 51 | +let rec stripTy (ty: FSharpType) = |
| 52 | + if ty.IsAbbreviation then stripTy ty.AbbreviatedType else ty |
| 53 | + |
| 54 | +let getTypeText (sym: FSharpMemberOrFunctionOrValue) = |
| 55 | + let ty = stripTy sym.FullType |
| 56 | + FSharpType.Prettify(ty).Format(FSharpDisplayContext.Empty) |
| 57 | + |
| 58 | +symbols |
| 59 | +|> Array.choose (fun vUse -> match vUse.Symbol with :? FSharpMemberOrFunctionOrValue as v -> Some (v, vUse.Range) | _ -> None) |
| 60 | +|> Array.filter (fun (v, _) -> v.GenericParameters.Count = 0) |
| 61 | +|> Array.filter (fun (v, _) -> v.CurriedParameterGroups.Count = 0) |
| 62 | +|> Array.filter (fun (v, _) -> not v.FullType.IsGenericParameter) |
| 63 | +|> Array.map (fun (v, vUse) -> getTypeText v, v, vUse) |
| 64 | +|> Array.filter (fun (vTypeText, v, _) -> |
| 65 | + match vTypeText with |
| 66 | + | "System.String" -> false |
| 67 | + | "System.Boolean" -> false |
| 68 | + | "System.Int32" -> false |
| 69 | + | "System.Int64" -> false |
| 70 | + | "System.Object" -> false |
| 71 | + | "Microsoft.FSharp.Collections.List<Microsoft.FSharp.Core.string>" -> false |
| 72 | + | "Microsoft.FSharp.Core.Option<Microsoft.FSharp.Core.string>" -> false |
| 73 | + | s when s.EndsWith(" Microsoft.FSharp.Core.[]") -> false // for now filter array types |
| 74 | + | _ when v.DisplayName.StartsWith "_" -> false |
| 75 | + | _ -> true) |
| 76 | +|> Array.groupBy (fun (vTypeText, _, _) -> vTypeText) |
| 77 | +|> Array.map (fun (key, g) -> |
| 78 | + key, |
| 79 | + (g |
| 80 | + |> Array.groupBy (fun (_, v, _) -> v.DisplayName) |
| 81 | + |> Array.sortByDescending (snd >> Array.length))) |
| 82 | +|> Array.filter (fun (_, g) -> g.Length > 1) |
| 83 | +|> Array.sortByDescending (fun (key, g) -> Array.length g) |
| 84 | +|> Array.iter (fun (key, g) -> |
| 85 | + let key = key.Replace("Microsoft.FSharp", "FSharp").Replace("FSharp.Core.", "") |
| 86 | + printfn "Type: %s" key |
| 87 | + for (nm, entries) in g do |
| 88 | + printfn " %s (%d times)" nm (Array.length entries) |
| 89 | + for (_, _, vUse) in entries do |
| 90 | + printfn " %s" (vUse.ToString()) |
| 91 | + printfn "") |
| 92 | + |
| 93 | +(* |
| 94 | +let isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) |
| 95 | +
|
| 96 | +let dotnet = |
| 97 | + if isWindows then |
| 98 | + "dotnet.exe" |
| 99 | + else |
| 100 | + "dotnet" |
| 101 | +let fileExists pathToFile = |
| 102 | + try |
| 103 | + File.Exists(pathToFile) |
| 104 | + with _ -> |
| 105 | + false |
| 106 | +// Look for global install of dotnet sdk |
| 107 | +let getDotnetGlobalHostPath () = |
| 108 | + let pf = Environment.GetEnvironmentVariable("ProgramW6432") |
| 109 | +
|
| 110 | + let pf = |
| 111 | + if String.IsNullOrEmpty(pf) then |
| 112 | + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) |
| 113 | + else |
| 114 | + pf |
| 115 | +
|
| 116 | + let candidate = Path.Combine(pf, "dotnet", dotnet) |
| 117 | +
|
| 118 | + if fileExists candidate then |
| 119 | + Some candidate |
| 120 | + else |
| 121 | + // Can't find it --- give up |
| 122 | + None |
| 123 | +
|
| 124 | +let getDotnetHostPath () = |
| 125 | + let probePathForDotnetHost () = |
| 126 | + let paths = |
| 127 | + let p = Environment.GetEnvironmentVariable("PATH") |
| 128 | +
|
| 129 | + if not (isNull p) then |
| 130 | + p.Split(Path.PathSeparator) |
| 131 | + else |
| 132 | + [||] |
| 133 | +
|
| 134 | + paths |> Array.tryFind (fun f -> fileExists (Path.Combine(f, dotnet))) |
| 135 | +
|
| 136 | + match (Environment.GetEnvironmentVariable("DOTNET_HOST_PATH")) with |
| 137 | + // Value set externally |
| 138 | + | value when not (String.IsNullOrEmpty(value)) && fileExists value -> Some value |
| 139 | + | _ -> |
| 140 | + // Probe for netsdk install, dotnet. and dotnet.exe is a constant offset from the location of System.Int32 |
| 141 | + let candidate = |
| 142 | + let assemblyLocation = Path.GetDirectoryName(typeof<Int32>.Assembly.Location) |
| 143 | + Path.GetFullPath(Path.Combine(assemblyLocation, "..", "..", "..", dotnet)) |
| 144 | +
|
| 145 | + if fileExists candidate then |
| 146 | + Some candidate |
| 147 | + else |
| 148 | + match probePathForDotnetHost () with |
| 149 | + | Some f -> Some(Path.Combine(f, dotnet)) |
| 150 | + | None -> getDotnetGlobalHostPath () |
| 151 | +let dotnetExe = getDotnetHostPath () |> Option.map System.IO.FileInfo |
| 152 | +*) |
0 commit comments