Skip to content

Commit de49e2f

Browse files
authored
add identifier analysis script (#13486)
* add identifier analysis script * add identifier analysis script
1 parent 5055281 commit de49e2f

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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

Comments
 (0)