-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathReanalyze.ml
More file actions
225 lines (219 loc) · 8.18 KB
/
Reanalyze.ml
File metadata and controls
225 lines (219 loc) · 8.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
open Common
let loadCmtFile cmtFilePath =
let cmt_infos = Cmt_format.read_cmt cmtFilePath in
let excludePath sourceFile =
!Cli.excludePaths
|> List.exists (fun prefix_ ->
let prefix =
match Filename.is_relative sourceFile with
| true -> prefix_
| false -> Filename.concat (Sys.getcwd ()) prefix_
in
String.length prefix <= String.length sourceFile
&&
try String.sub sourceFile 0 (String.length prefix) = prefix
with Invalid_argument _ -> false)
in
match cmt_infos.cmt_annots |> FindSourceFile.cmt with
| Some sourceFile when not (excludePath sourceFile) ->
if !Cli.debug then
Log_.item "Scanning %s Source:%s@."
(match !Cli.ci && not (Filename.is_relative cmtFilePath) with
| true -> Filename.basename cmtFilePath
| false -> cmtFilePath)
(match !Cli.ci && not (Filename.is_relative sourceFile) with
| true -> sourceFile |> Filename.basename
| false -> sourceFile);
FileReferences.addFile sourceFile;
currentSrc := sourceFile;
currentModule := Paths.getModuleName sourceFile;
currentModuleName :=
!currentModule
|> Name.create ~isInterface:(Filename.check_suffix !currentSrc "i");
if runConfig.dce then cmt_infos |> DeadCode.processCmt ~cmtFilePath;
if runConfig.exception_ then cmt_infos |> Exception.processCmt;
if runConfig.termination then cmt_infos |> Arnold.processCmt
| _ -> ()
let processCmtFiles ~cmtRoot =
let ( +++ ) = Filename.concat in
match cmtRoot with
| Some root ->
Cli.cmtCommand := true;
let rec walkSubDirs dir =
let absDir =
match dir = "" with
| true -> root
| false -> root +++ dir
in
let skipDir =
let base = Filename.basename dir in
base = "node_modules" || base = "_esy"
in
if (not skipDir) && Sys.file_exists absDir then
if Sys.is_directory absDir then
absDir |> Sys.readdir |> Array.iter (fun d -> walkSubDirs (dir +++ d))
else if
Filename.check_suffix absDir ".cmt"
|| Filename.check_suffix absDir ".cmti"
then absDir |> loadCmtFile
in
walkSubDirs ""
| None ->
Lazy.force Paths.setReScriptProjectRoot;
let lib_bs = runConfig.projectRoot +++ ("lib" +++ "bs") in
let sourceDirs =
Paths.readSourceDirs ~configSources:None |> List.sort String.compare
in
sourceDirs
|> List.iter (fun sourceDir ->
let libBsSourceDir = Filename.concat lib_bs sourceDir in
let files =
match Sys.readdir libBsSourceDir |> Array.to_list with
| files -> files
| exception Sys_error _ -> []
in
let cmtFiles =
files
|> List.filter (fun x ->
Filename.check_suffix x ".cmt"
|| Filename.check_suffix x ".cmti")
in
cmtFiles |> List.sort String.compare
|> List.iter (fun cmtFile ->
let cmtFilePath = Filename.concat libBsSourceDir cmtFile in
cmtFilePath |> loadCmtFile))
let runAnalysis ~cmtRoot =
processCmtFiles ~cmtRoot;
if runConfig.dce then (
DeadException.forceDelayedItems ();
DeadOptionalArgs.forceDelayedItems ();
DeadCommon.reportDead ~checkOptionalArg:DeadOptionalArgs.check;
WriteDeadAnnotations.write ());
if runConfig.exception_ then Exception.Checks.doChecks ();
if runConfig.termination && !Common.Cli.debug then Arnold.reportStats ()
let runAnalysisAndReport ~cmtRoot =
Log_.Color.setup ();
if !Common.Cli.json then EmitJson.start ();
runAnalysis ~cmtRoot;
Log_.Stats.report ();
Log_.Stats.clear ();
if !Common.Cli.json then EmitJson.finish ()
let cli () =
let analysisKindSet = ref false in
let cmtRootRef = ref None in
let usage = "reanalyze version " ^ Version.version in
let versionAndExit () =
print_endline usage;
exit 0
[@@raises exit]
in
let rec setAll cmtRoot =
RunConfig.all ();
cmtRootRef := cmtRoot;
analysisKindSet := true
and setConfig () =
Paths.Config.processBsconfig ();
analysisKindSet := true
and setDCE cmtRoot =
RunConfig.dce ();
cmtRootRef := cmtRoot;
analysisKindSet := true
and setException cmtRoot =
RunConfig.exception_ ();
cmtRootRef := cmtRoot;
analysisKindSet := true
and setTermination cmtRoot =
RunConfig.termination ();
cmtRootRef := cmtRoot;
analysisKindSet := true
and speclist =
[
("-all", Arg.Unit (fun () -> setAll None), "Run all the analyses.");
( "-all-cmt",
String (fun s -> setAll (Some s)),
"root_path Run all the analyses for all the .cmt files under the root \
path" );
("-ci", Unit (fun () -> Cli.ci := true), "Internal flag for use in CI");
( "-config",
Unit setConfig,
"Read the analysis mode from rescript.json/bsconfig.json" );
("-dce", Unit (fun () -> setDCE None), "Eperimental DCE");
("-debug", Unit (fun () -> Cli.debug := true), "Print debug information");
( "-dce-cmt",
String (fun s -> setDCE (Some s)),
"root_path Experimental DCE for all the .cmt files under the root path"
);
( "-exception",
Unit (fun () -> setException None),
"Experimental exception analysis" );
( "-exception-cmt",
String (fun s -> setException (Some s)),
"root_path Experimental exception analysis for all the .cmt files \
under the root path" );
( "-exclude-paths",
String
(fun s ->
let paths = s |> String.split_on_char ',' in
Common.Cli.excludePaths := paths @ Common.Cli.excludePaths.contents),
"comma-separated-path-prefixes Exclude from analysis files whose path \
has a prefix in the list" );
( "-experimental",
Set Common.Cli.experimental,
"Turn on experimental analyses (this option is currently unused)" );
( "-externals",
Set DeadCommon.Config.analyzeExternals,
"Report on externals in dead code analysis" );
("-json", Set Common.Cli.json, "Print reports in json format");
( "-live-names",
String
(fun s ->
let names = s |> String.split_on_char ',' in
Common.Cli.liveNames := names @ Common.Cli.liveNames.contents),
"comma-separated-names Consider all values with the given names as live"
);
( "-live-paths",
String
(fun s ->
let paths = s |> String.split_on_char ',' in
Common.Cli.livePaths := paths @ Common.Cli.livePaths.contents),
"comma-separated-path-prefixes Consider all values whose path has a \
prefix in the list as live" );
( "-suppress",
String
(fun s ->
let names = s |> String.split_on_char ',' in
runConfig.suppress <- names @ runConfig.suppress),
"comma-separated-path-prefixes Don't report on files whose path has a \
prefix in the list" );
( "-termination",
Unit (fun () -> setTermination None),
"Experimental termination analysis" );
( "-termination-cmt",
String (fun s -> setTermination (Some s)),
"root_path Experimental termination analysis for all the .cmt files \
under the root path" );
( "-unsuppress",
String
(fun s ->
let names = s |> String.split_on_char ',' in
runConfig.unsuppress <- names @ runConfig.unsuppress),
"comma-separated-path-prefixes Report on files whose path has a prefix \
in the list, overriding -suppress (no-op if -suppress is not \
specified)" );
("-version", Unit versionAndExit, "Show version information and exit");
("--version", Unit versionAndExit, "Show version information and exit");
( "-write",
Set Common.Cli.write,
"Write @dead annotations directly in the source files" );
]
in
Arg.parse speclist print_endline usage;
if !analysisKindSet = false then setConfig ();
let cmtRoot = !cmtRootRef in
runAnalysisAndReport ~cmtRoot
[@@raises exit]
module RunConfig = RunConfig
module Log_ = Log_
module Common = Common
module Paths = Paths
module DeadCommon = DeadCommon