bend-lint 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -10
- package/package.json +4 -4
- package/src/files/main.bend +2 -2
- package/src/json/main.bend +2 -2
- package/src/linter/cli.bend +386 -118
- package/src/linter/defaults.json +1 -0
- package/src/linter/directives.bend +4 -4
- package/src/linter/fixes.bend +73 -8
- package/src/linter/seam.ts +215 -46
- package/src/shared/utils.ts +2 -0
- package/src/text/main.bend +3 -3
package/src/linter/cli.bend
CHANGED
|
@@ -2,12 +2,52 @@
|
|
|
2
2
|
import Base
|
|
3
3
|
import ../files/main.bend as Files
|
|
4
4
|
import ../json/main.bend as Json
|
|
5
|
-
import bend-lint-api@0.
|
|
6
|
-
import bend-lint-api@0.
|
|
5
|
+
import bend-lint-api@0.2.0.0/shared/lists.bend as Lists
|
|
6
|
+
import bend-lint-api@0.2.0.0/shared/names.bend as Names
|
|
7
|
+
import bend-lint-api@0.2.0.0/shared/sets.bend as Sets
|
|
7
8
|
import ../text/main.bend as Text
|
|
8
9
|
import ./directives.bend as Directives
|
|
9
10
|
import ./fixes.bend as Fixes
|
|
10
|
-
import bend-lint-api@0.
|
|
11
|
+
import bend-lint-api@0.2.0.0/linter/lint.bend as Lint
|
|
12
|
+
|
|
13
|
+
# Errors
|
|
14
|
+
# ======
|
|
15
|
+
|
|
16
|
+
# What a JSON run prints when an error ends it: `ok` false, and the error: what it says, and the file it is about, if
|
|
17
|
+
# one (seam.ts prints the same for the errors it finds).
|
|
18
|
+
def Error.json(+path: String, text: String) -> Json.Json:
|
|
19
|
+
Json.JObject{
|
|
20
|
+
[
|
|
21
|
+
Json.JField{"ok", Json.JFalse{}},
|
|
22
|
+
Json.JField{
|
|
23
|
+
"errors",
|
|
24
|
+
Json.JArray{
|
|
25
|
+
[
|
|
26
|
+
Json.JObject{
|
|
27
|
+
Lists.push(
|
|
28
|
+
Json.JField,
|
|
29
|
+
Lists.when(
|
|
30
|
+
Json.JField,
|
|
31
|
+
Bool.not(String.is_empty(path)),
|
|
32
|
+
Json.JField{"path", Json.JString{path}}
|
|
33
|
+
),
|
|
34
|
+
[Json.JField{"message", Json.JString{text}}]
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Ends the run with `status`, as `text` says on stderr, about the file at `path` ("" for none); a JSON run (`json`)
|
|
44
|
+
# first prints the error (see Error.json).
|
|
45
|
+
def Error.end(-A: Type, json: Bool, status: U32, path: String, +text: String) -> IO(A):
|
|
46
|
+
match json:
|
|
47
|
+
case True{}:
|
|
48
|
+
IO.bind(Unit, A, IO.print(Json.show(Error.json(path, text))), u => IO.die(A, status, text))
|
|
49
|
+
case False{}:
|
|
50
|
+
IO.die(A, status, text)
|
|
11
51
|
|
|
12
52
|
# Config
|
|
13
53
|
# ======
|
|
@@ -67,7 +107,7 @@ def Parsed.value(value: Json.Json) -> Maybe<&2, Lint.Value>:
|
|
|
67
107
|
case Json.JFalse{}:
|
|
68
108
|
Some{Lint.Flag{False{}}}
|
|
69
109
|
case Json.JString{text}:
|
|
70
|
-
Some{Lint.
|
|
110
|
+
Some{Lint.Chars{text}}
|
|
71
111
|
case _:
|
|
72
112
|
None{}
|
|
73
113
|
|
|
@@ -261,59 +301,91 @@ def Parsed.of(folder: String, json: Maybe<&2, Json.Json>) -> Result<&2, &2, Stri
|
|
|
261
301
|
Fail{"not a JSON object"}
|
|
262
302
|
|
|
263
303
|
# The config at `path`, if each of its entries is a file (`missing` lists those that are not); else the run ends.
|
|
264
|
-
def Config.entered(
|
|
304
|
+
def Config.entered(
|
|
305
|
+
json: Bool,
|
|
306
|
+
missing: List<&2, String>,
|
|
307
|
+
+path: String,
|
|
308
|
+
config: Project
|
|
309
|
+
) -> IO(Project):
|
|
265
310
|
match missing:
|
|
266
311
|
case Nil{}:
|
|
267
312
|
IO.pure(Project, config)
|
|
268
313
|
case Con{entry, rest}:
|
|
269
|
-
|
|
314
|
+
Error.end(
|
|
315
|
+
Project,
|
|
316
|
+
json,
|
|
317
|
+
2,
|
|
318
|
+
path,
|
|
319
|
+
"bend-lint: " ++ path ++ ": the entry " ++ entry ++ " is not a file"
|
|
320
|
+
)
|
|
270
321
|
|
|
271
|
-
def Config.parsed(
|
|
322
|
+
def Config.parsed(
|
|
323
|
+
json: Bool,
|
|
324
|
+
+path: String,
|
|
325
|
+
parsed: Result<&2, &2, String, Project>
|
|
326
|
+
) -> IO(Project):
|
|
272
327
|
match parsed:
|
|
273
328
|
case Done{Project{+entries, imports, rules}}:
|
|
274
329
|
IO.bind(
|
|
275
330
|
List<&2, String>,
|
|
276
331
|
Project,
|
|
277
332
|
Files.missing(entries, []),
|
|
278
|
-
missing => Config.entered(missing, path, Project{entries, imports, rules})
|
|
333
|
+
missing => Config.entered(json, missing, path, Project{entries, imports, rules})
|
|
279
334
|
)
|
|
280
335
|
case Fail{why}:
|
|
281
|
-
|
|
336
|
+
Error.end(Project, json, 2, path, "bend-lint: " ++ path ++ ": " ++ why)
|
|
282
337
|
|
|
283
|
-
def Config.read(+folder: String, text: Result<&2, &2, String, String>) -> IO(Project):
|
|
338
|
+
def Config.read(json: Bool, +folder: String, text: Result<&2, &2, String, String>) -> IO(Project):
|
|
284
339
|
match text:
|
|
285
340
|
case Done{t}:
|
|
286
|
-
Config.parsed(folder ++ "/bend-lint.json", Parsed.of(folder, Json.read(t)))
|
|
341
|
+
Config.parsed(json, folder ++ "/bend-lint.json", Parsed.of(folder, Json.read(t)))
|
|
287
342
|
case Fail{why}:
|
|
288
|
-
|
|
343
|
+
Error.end(
|
|
344
|
+
Project,
|
|
345
|
+
json,
|
|
346
|
+
2,
|
|
347
|
+
folder ++ "/bend-lint.json",
|
|
348
|
+
"bend-lint: reading a config failed: " ++ why
|
|
349
|
+
)
|
|
289
350
|
|
|
290
|
-
def Config.opened(
|
|
351
|
+
def Config.opened(
|
|
352
|
+
json: Bool,
|
|
353
|
+
+folder: String,
|
|
354
|
+
opened: Result<&1, &1, U32 & String, File>
|
|
355
|
+
) -> IO(Project):
|
|
291
356
|
match opened:
|
|
292
357
|
case Done{file}:
|
|
293
358
|
IO.bind(
|
|
294
359
|
Result<&2, &2, String, String>,
|
|
295
360
|
Project,
|
|
296
361
|
Files.read(file),
|
|
297
|
-
text => Config.read(folder, text)
|
|
362
|
+
text => Config.read(json, folder, text)
|
|
298
363
|
)
|
|
299
364
|
case Fail{Tuple{code, why}}:
|
|
300
|
-
|
|
365
|
+
Error.end(
|
|
366
|
+
Project,
|
|
367
|
+
json,
|
|
368
|
+
2,
|
|
369
|
+
folder ++ "/bend-lint.json",
|
|
370
|
+
"bend-lint: reading " ++ folder ++ "/bend-lint.json failed: " ++ why
|
|
371
|
+
)
|
|
301
372
|
|
|
302
|
-
def Config.at(+folder: String) -> IO(Project):
|
|
373
|
+
def Config.at(json: Bool, +folder: String) -> IO(Project):
|
|
303
374
|
IO.bind(
|
|
304
375
|
Result<&1, &1, U32 & String, File>,
|
|
305
376
|
Project,
|
|
306
377
|
File.open(folder ++ "/bend-lint.json", "r"),
|
|
307
|
-
opened => Config.opened(folder, opened)
|
|
378
|
+
opened => Config.opened(json, folder, opened)
|
|
308
379
|
)
|
|
309
380
|
|
|
310
|
-
# The run's config: the bend-lint.json the server found, in the folder it names in BEND_LINT_PROJECT.
|
|
311
|
-
|
|
381
|
+
# The run's config: the bend-lint.json the server found, in the folder it names in BEND_LINT_PROJECT. Its errors end
|
|
382
|
+
# the run, in JSON if `json`.
|
|
383
|
+
def Config.load(json: Bool) -> IO(Project):
|
|
312
384
|
IO.bind(
|
|
313
385
|
String,
|
|
314
386
|
Project,
|
|
315
387
|
IO.try(String, IO.get_env("BEND_LINT_PROJECT")),
|
|
316
|
-
folder => Config.at(folder)
|
|
388
|
+
folder => Config.at(json, folder)
|
|
317
389
|
)
|
|
318
390
|
|
|
319
391
|
# Seam
|
|
@@ -454,7 +526,9 @@ def Wire.receive(
|
|
|
454
526
|
Socket.close(s)
|
|
455
527
|
IO.die(List<&2, String>, 1, "bend-lint: the checker's answer is too long")
|
|
456
528
|
|
|
457
|
-
def Wire.sent(
|
|
529
|
+
def Wire.sent(
|
|
530
|
+
r: Socket & Result<&1, &1, (U32 & String) & List<&2, U32>, Unit>
|
|
531
|
+
) -> IO(List<&2, String>):
|
|
458
532
|
match r:
|
|
459
533
|
case Tuple{s, Done{u}}:
|
|
460
534
|
IO.bind(
|
|
@@ -497,7 +571,11 @@ def Wire.ask(request: List<&2, String>) -> IO(List<&2, List<&2, String>>):
|
|
|
497
571
|
p: U32 <- Wire.port()
|
|
498
572
|
sock: Socket <- IO.try(Socket, TCP.connect("127.0.0.1", p))
|
|
499
573
|
s: Socket
|
|
500
|
-
& Result
|
|
574
|
+
& Result<
|
|
575
|
+
&1,
|
|
576
|
+
&1,
|
|
577
|
+
(U32 & String) & List<&2, U32>,
|
|
578
|
+
Unit> <- TCP.send(sock, Wire.line(request, ""))
|
|
501
579
|
got: List<&2, String> <- Wire.sent(s)
|
|
502
580
|
Wire.answered(Wire.records(List.reverse(&2, String, got), Reading{False{}, "", [], []}))
|
|
503
581
|
|
|
@@ -959,12 +1037,14 @@ def About.needs_every(about: About) -> Bool:
|
|
|
959
1037
|
case _:
|
|
960
1038
|
False{}
|
|
961
1039
|
|
|
1040
|
+
# Whether a rule of this setting runs: one the config does not name is off, as the runner can hold more rules than the
|
|
1041
|
+
# config turns on (see getBuiltRules in seam.ts).
|
|
962
1042
|
def Setting.is_on(setting: Maybe<&2, Setting>) -> Bool:
|
|
963
1043
|
match setting:
|
|
964
|
-
case Some{
|
|
965
|
-
False{}
|
|
966
|
-
case _:
|
|
1044
|
+
case Some{RuleOn{severity, options}}:
|
|
967
1045
|
True{}
|
|
1046
|
+
case _:
|
|
1047
|
+
False{}
|
|
968
1048
|
|
|
969
1049
|
def Setting.severity(setting: Maybe<&2, Setting>) -> Maybe<&2, Lint.Severity>:
|
|
970
1050
|
match setting:
|
|
@@ -1071,9 +1151,9 @@ def Spec.accepts(spec: Lint.Spec, value: Lint.Value) -> Bool:
|
|
|
1071
1151
|
U32.is_le(min, n) && U32.is_le(n, max)
|
|
1072
1152
|
case Lint.FlagOption{} Lint.Flag{on}:
|
|
1073
1153
|
True{}
|
|
1074
|
-
case Lint.TextOption{Nil{}} Lint.
|
|
1154
|
+
case Lint.TextOption{Nil{}} Lint.Chars{text}:
|
|
1075
1155
|
True{}
|
|
1076
|
-
case Lint.TextOption{choices} Lint.
|
|
1156
|
+
case Lint.TextOption{choices} Lint.Chars{text}:
|
|
1077
1157
|
List.contains(~String, ~String.eq, choices, text)
|
|
1078
1158
|
case _ _:
|
|
1079
1159
|
False{}
|
|
@@ -1202,7 +1282,7 @@ def About.settle.of(about: About, setting: Maybe<&2, Setting>) -> Names.Named<Se
|
|
|
1202
1282
|
case About{id, needs, options, every} Some{RuleOn{severity, given}}:
|
|
1203
1283
|
Names.Named{id, RuleOn{severity, Option.values(options, given, [])}}
|
|
1204
1284
|
case About{id, needs, options, every} None{}:
|
|
1205
|
-
Names.Named{id,
|
|
1285
|
+
Names.Named{id, RuleOff{}}
|
|
1206
1286
|
|
|
1207
1287
|
# Each rule's setting, its option values settled: off, or on with its severity, if any, and every option it declares.
|
|
1208
1288
|
def About.settle(
|
|
@@ -1591,7 +1671,7 @@ def Found.keys(founds: List<&2, Lint.Found>, keys: Set()) -> Set():
|
|
|
1591
1671
|
Found.keys(rest, Set.add(keys, Found.key(f)))
|
|
1592
1672
|
|
|
1593
1673
|
def Found.is_among(keys: Set(), found: Lint.Found) -> Bool:
|
|
1594
|
-
|
|
1674
|
+
Sets.has(keys, Found.key(found))
|
|
1595
1675
|
|
|
1596
1676
|
def Found.keep(hit: Bool, found: Lint.Found, out: List<&2, Lint.Found>) -> List<&2, Lint.Found>:
|
|
1597
1677
|
match hit:
|
|
@@ -1967,21 +2047,43 @@ def Object.fixes(
|
|
|
1967
2047
|
case _ _:
|
|
1968
2048
|
List.reverse(&2, Json.Json, out)
|
|
1969
2049
|
|
|
2050
|
+
# Where a rule of bend-lint's own, from the package bend-lint-rules, is explained: its file, whose header says what it
|
|
2051
|
+
# reports, what its fix does, and its options. Its id is its path in bend-lint's repo.
|
|
2052
|
+
def Docs.of(+rule: String) -> Maybe<&2, String>:
|
|
2053
|
+
Lists.when(
|
|
2054
|
+
String,
|
|
2055
|
+
String.starts_with(rule, "bend-lint-rules/"),
|
|
2056
|
+
"https://github.com/MattCozendey/bend-lint/blob/main/" ++ rule ++ ".bend"
|
|
2057
|
+
)
|
|
2058
|
+
|
|
1970
2059
|
def Object.finding(resolved: Resolved, +places: Map<&2, Text.Position>) -> Json.Json:
|
|
1971
2060
|
match resolved:
|
|
1972
2061
|
case Resolved{
|
|
1973
|
-
Lint.Found{rule, Lint.Finding{severity, Lint.Span{+path, beg, end}, message, fixes}},
|
|
2062
|
+
Lint.Found{+rule, Lint.Finding{severity, Lint.Span{+path, beg, end}, message, fixes}},
|
|
1974
2063
|
edits
|
|
1975
2064
|
}:
|
|
1976
2065
|
Json.JObject{
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
Json.
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2066
|
+
Json.JField{
|
|
2067
|
+
"code",
|
|
2068
|
+
Json.JString{rule}
|
|
2069
|
+
}
|
|
2070
|
+
<> Lists.push(
|
|
2071
|
+
Json.JField,
|
|
2072
|
+
Maybe.map(
|
|
2073
|
+
&2,
|
|
2074
|
+
String,
|
|
2075
|
+
Json.JField,
|
|
2076
|
+
d => Json.JField{"docs", Json.JString{d}},
|
|
2077
|
+
Docs.of(rule)
|
|
2078
|
+
),
|
|
2079
|
+
[
|
|
2080
|
+
Json.JField{"severity", Json.JString{Lint.Severity.show(severity)}},
|
|
2081
|
+
Json.JField{"message", Json.JString{message}},
|
|
2082
|
+
Json.JField{"path", Json.JString{path}},
|
|
2083
|
+
Json.JField{"range", Object.range(places, path, beg, end)},
|
|
2084
|
+
Json.JField{"fixes", Json.JArray{Object.fixes(fixes, edits, places, [])}}
|
|
2085
|
+
]
|
|
2086
|
+
)
|
|
1985
2087
|
}
|
|
1986
2088
|
|
|
1987
2089
|
def Shown.one(json: Bool, resolved: Resolved, places: Map<&2, Text.Position>) -> Shown:
|
|
@@ -2042,25 +2144,28 @@ def Declarations.of(need: Bool, sources: List<&2, Lint.Source>) -> List<&2, Lint
|
|
|
2042
2144
|
# ------
|
|
2043
2145
|
|
|
2044
2146
|
# What linting named files gave: how many findings stay; those to show at the end, and those directives suppressed
|
|
2045
|
-
# (both last first); whether a file fails the run;
|
|
2147
|
+
# (both last first); whether a file fails the run; how many fixes the run made; and why rules did not run on some
|
|
2148
|
+
# files, in order, as JSON (see Skipped).
|
|
2046
2149
|
type Outcome is Data:
|
|
2047
2150
|
Outcome{
|
|
2048
2151
|
count: Nat,
|
|
2049
2152
|
findings: List<&2, Shown>,
|
|
2050
2153
|
suppressed: List<&2, Shown>,
|
|
2051
2154
|
failed: Bool,
|
|
2052
|
-
fixed: U32
|
|
2155
|
+
fixed: U32,
|
|
2156
|
+
skipped: List<&2, Json.Json>
|
|
2053
2157
|
}
|
|
2054
2158
|
|
|
2055
2159
|
def Outcome.add(a: Outcome, b: Outcome) -> Outcome:
|
|
2056
2160
|
match a b:
|
|
2057
|
-
case Outcome{n, f, s, e, x} Outcome{m, g, t, d, y}:
|
|
2161
|
+
case Outcome{n, f, s, e, x, w} Outcome{m, g, t, d, y, z}:
|
|
2058
2162
|
Outcome{
|
|
2059
2163
|
Nat.add(n, m),
|
|
2060
2164
|
Lists.append(&2, Shown, g, f),
|
|
2061
2165
|
Lists.append(&2, Shown, t, s),
|
|
2062
2166
|
e || d,
|
|
2063
|
-
(x + y: U32)
|
|
2167
|
+
(x + y: U32),
|
|
2168
|
+
Lists.append(&2, Json.Json, w, z)
|
|
2064
2169
|
}
|
|
2065
2170
|
|
|
2066
2171
|
def summary.fixed(fixing: Bool, fixed: U32) -> String:
|
|
@@ -2120,15 +2225,15 @@ def end.json(failed: Bool, summary: String) -> IO(Unit):
|
|
|
2120
2225
|
|
|
2121
2226
|
def end(json: Bool, total: Outcome, show: Bool, fixing: Bool) -> IO(Unit):
|
|
2122
2227
|
match json total:
|
|
2123
|
-
case False{} Outcome{n, f, s, True{}, x}:
|
|
2228
|
+
case False{} Outcome{n, f, s, True{}, x, w}:
|
|
2124
2229
|
do IO<Unit>:
|
|
2125
2230
|
say(suppressed.lines(show, s))
|
|
2126
2231
|
IO.die(Unit, 1, "bend-lint: FAIL")
|
|
2127
|
-
case False{} Outcome{n, f, +s, False{}, x}:
|
|
2232
|
+
case False{} Outcome{n, f, +s, False{}, x, w}:
|
|
2128
2233
|
do IO<Unit>:
|
|
2129
2234
|
say(suppressed.lines(show, s))
|
|
2130
2235
|
IO.print(summary(n, Lists.length(&2, Shown, s), summary.fixed(fixing, x)))
|
|
2131
|
-
case True{} Outcome{n, f, +s, +failed, x}:
|
|
2236
|
+
case True{} Outcome{n, f, +s, +failed, x, w}:
|
|
2132
2237
|
do IO<Unit>:
|
|
2133
2238
|
IO.print(
|
|
2134
2239
|
Json.show(
|
|
@@ -2136,7 +2241,8 @@ def end(json: Bool, total: Outcome, show: Bool, fixing: Bool) -> IO(Unit):
|
|
|
2136
2241
|
[
|
|
2137
2242
|
Json.JField{"ok", Bool.pick(Json.Json, failed, Json.JFalse{}, Json.JTrue{})},
|
|
2138
2243
|
Json.JField{"findings", Json.JArray{Shown.values(f, [])}},
|
|
2139
|
-
Json.JField{"suppressed", Json.JArray{Shown.values(s, [])}}
|
|
2244
|
+
Json.JField{"suppressed", Json.JArray{Shown.values(s, [])}},
|
|
2245
|
+
Json.JField{"skipped", Json.JArray{w}}
|
|
2140
2246
|
]
|
|
2141
2247
|
}
|
|
2142
2248
|
)
|
|
@@ -2147,14 +2253,15 @@ def end(json: Bool, total: Outcome, show: Bool, fixing: Bool) -> IO(Unit):
|
|
|
2147
2253
|
# (first first; a line as a JSON string, an object as itself).
|
|
2148
2254
|
def Outcome.json(total: Outcome) -> Json.Json:
|
|
2149
2255
|
match total:
|
|
2150
|
-
case Outcome{n, f, s, failed, x}:
|
|
2256
|
+
case Outcome{n, f, s, failed, x, w}:
|
|
2151
2257
|
Json.JArray{
|
|
2152
2258
|
[
|
|
2153
2259
|
Json.JNumber{Nat.show(n)},
|
|
2154
2260
|
Json.JNumber{U32.show(x)},
|
|
2155
2261
|
Bool.pick(Json.Json, failed, Json.JTrue{}, Json.JFalse{}),
|
|
2156
2262
|
Json.JArray{Shown.values(f, [])},
|
|
2157
|
-
Json.JArray{Shown.values(s, [])}
|
|
2263
|
+
Json.JArray{Shown.values(s, [])},
|
|
2264
|
+
Json.JArray{w}
|
|
2158
2265
|
]
|
|
2159
2266
|
}
|
|
2160
2267
|
|
|
@@ -2172,11 +2279,12 @@ def Outcome.of(
|
|
|
2172
2279
|
n: Maybe<&2, U32>,
|
|
2173
2280
|
x: Maybe<&2, U32>,
|
|
2174
2281
|
f: List<&2, Json.Json>,
|
|
2175
|
-
s: List<&2, Json.Json
|
|
2282
|
+
s: List<&2, Json.Json>,
|
|
2283
|
+
w: List<&2, Json.Json>
|
|
2176
2284
|
) -> Maybe<&2, Outcome>:
|
|
2177
2285
|
match n x:
|
|
2178
2286
|
case Some{n} Some{x}:
|
|
2179
|
-
Some{Outcome{U32.to_nat(n), Shown.back(f, []), Shown.back(s, []), failed, x}}
|
|
2287
|
+
Some{Outcome{U32.to_nat(n), Shown.back(f, []), Shown.back(s, []), failed, x, w}}
|
|
2180
2288
|
case _ _:
|
|
2181
2289
|
None{}
|
|
2182
2290
|
|
|
@@ -2187,20 +2295,26 @@ def Outcome.read(answer: Maybe<&2, Json.Json>) -> Maybe<&2, Outcome>:
|
|
|
2187
2295
|
Json.JArray{
|
|
2188
2296
|
Con{
|
|
2189
2297
|
Json.JNumber{n},
|
|
2190
|
-
Con{
|
|
2298
|
+
Con{
|
|
2299
|
+
Json.JNumber{x},
|
|
2300
|
+
Con{Json.JTrue{}, Con{Json.JArray{f}, Con{Json.JArray{s}, Con{Json.JArray{w}, Nil{}}}}}
|
|
2301
|
+
}
|
|
2191
2302
|
}
|
|
2192
2303
|
}
|
|
2193
2304
|
}:
|
|
2194
|
-
Outcome.of(True{}, U32.read(n), U32.read(x), f, s)
|
|
2305
|
+
Outcome.of(True{}, U32.read(n), U32.read(x), f, s, w)
|
|
2195
2306
|
case Some{
|
|
2196
2307
|
Json.JArray{
|
|
2197
2308
|
Con{
|
|
2198
2309
|
Json.JNumber{n},
|
|
2199
|
-
Con{
|
|
2310
|
+
Con{
|
|
2311
|
+
Json.JNumber{x},
|
|
2312
|
+
Con{Json.JFalse{}, Con{Json.JArray{f}, Con{Json.JArray{s}, Con{Json.JArray{w}, Nil{}}}}}
|
|
2313
|
+
}
|
|
2200
2314
|
}
|
|
2201
2315
|
}
|
|
2202
2316
|
}:
|
|
2203
|
-
Outcome.of(False{}, U32.read(n), U32.read(x), f, s)
|
|
2317
|
+
Outcome.of(False{}, U32.read(n), U32.read(x), f, s, w)
|
|
2204
2318
|
case _:
|
|
2205
2319
|
None{}
|
|
2206
2320
|
|
|
@@ -2248,7 +2362,8 @@ def outcome(report: Report, +json: Bool, +fixing: Bool, program: Lint.Program) -
|
|
|
2248
2362
|
List.reverse(&2, Shown, Shown.kept(json, List.take(&2, Shown, shown, n))),
|
|
2249
2363
|
List.reverse(&2, Shown, List.drop(&2, Shown, shown, n)),
|
|
2250
2364
|
Lists.any(~Lint.Found, ~Found.is_error, founds),
|
|
2251
|
-
fixed
|
|
2365
|
+
fixed,
|
|
2366
|
+
[]
|
|
2252
2367
|
}
|
|
2253
2368
|
|
|
2254
2369
|
# CLI
|
|
@@ -2405,19 +2520,25 @@ def linted(imports: Bool, path: String, sources: List<&2, Lint.Source>) -> List<
|
|
|
2405
2520
|
[]
|
|
2406
2521
|
)
|
|
2407
2522
|
|
|
2408
|
-
#
|
|
2409
|
-
|
|
2410
|
-
|
|
2523
|
+
# Why rules did not run on a file: Bend does not load it, so none ran (`Unloaded`); its check fails, so those that
|
|
2524
|
+
# need its facts did not (`Unchecked`); or an entry that reads it fails its check, so it was linted without it, and the
|
|
2525
|
+
# rules whose findings every entry must find report nothing on it (`Unentered`).
|
|
2526
|
+
type Skip is Data:
|
|
2527
|
+
Unloaded{}
|
|
2528
|
+
Unchecked{rules: List<&2, String>}
|
|
2529
|
+
Unentered{entry: String, rules: List<&2, String>}
|
|
2530
|
+
|
|
2531
|
+
# Why rules did not run on the file at `path`.
|
|
2532
|
+
type Skipped is Data:
|
|
2533
|
+
Skipped{path: String, skip: Skip}
|
|
2534
|
+
|
|
2535
|
+
# The skip, if the named file's check failed (`failed`), of the rules on that need its facts (`facts`), if any.
|
|
2536
|
+
def unchecked(failed: Bool, facts: List<&2, String>, path: String) -> List<&2, Skipped>:
|
|
2537
|
+
match failed facts:
|
|
2411
2538
|
case True{} Con{id, rest}:
|
|
2412
|
-
|
|
2413
|
-
"bend-lint: Bend's check of "
|
|
2414
|
-
++ path
|
|
2415
|
-
++ " fails, so the rules that need its facts did not run on it: "
|
|
2416
|
-
++ String.join(Con{id, rest}, ", ")
|
|
2417
|
-
++ "; `bend <file> --check-only` says why"
|
|
2418
|
-
)
|
|
2539
|
+
[Skipped{path, Unchecked{Con{id, rest}}}]
|
|
2419
2540
|
case _ _:
|
|
2420
|
-
|
|
2541
|
+
[]
|
|
2421
2542
|
|
|
2422
2543
|
# What a note on a failed entry adds of the rules on whose findings every entry must find (`every`): they report
|
|
2423
2544
|
# nothing.
|
|
@@ -2429,30 +2550,99 @@ def silenced(every: List<&2, String>) -> String:
|
|
|
2429
2550
|
", and the rules whose findings every entry must find report nothing on it: "
|
|
2430
2551
|
++ String.join(Con{id, rest}, ", ")
|
|
2431
2552
|
|
|
2432
|
-
#
|
|
2433
|
-
#
|
|
2434
|
-
#
|
|
2553
|
+
# The skips, for each entry of `failing` that reads the named file and failed its check, of the rules whose findings
|
|
2554
|
+
# every entry must find (`every`), onto `out` (last first). If the file's own check failed too (`failed`), `unchecked`
|
|
2555
|
+
# gives its skip instead.
|
|
2435
2556
|
def unentered(
|
|
2436
2557
|
failed: Bool,
|
|
2437
2558
|
failing: List<&2, String>,
|
|
2438
2559
|
+every: List<&2, String>,
|
|
2439
|
-
+path: String
|
|
2440
|
-
|
|
2560
|
+
+path: String,
|
|
2561
|
+
out: List<&2, Skipped>
|
|
2562
|
+
) -> List<&2, Skipped>:
|
|
2441
2563
|
match failed failing:
|
|
2442
2564
|
case False{} Con{entry, rest}:
|
|
2443
|
-
|
|
2444
|
-
IO.print_err(
|
|
2445
|
-
"bend-lint: Bend's check of the entry "
|
|
2446
|
-
++ entry
|
|
2447
|
-
++ " fails, so "
|
|
2448
|
-
++ path
|
|
2449
|
-
++ " was linted without it"
|
|
2450
|
-
++ silenced(every)
|
|
2451
|
-
++ "; `bend <file> --check-only` says why"
|
|
2452
|
-
)
|
|
2453
|
-
unentered(False{}, rest, every, path)
|
|
2565
|
+
unentered(False{}, rest, every, path, Skipped{path, Unentered{entry, every}} <> out)
|
|
2454
2566
|
case _ _:
|
|
2455
|
-
|
|
2567
|
+
out
|
|
2568
|
+
|
|
2569
|
+
def Skipped.text(skipped: Skipped) -> String:
|
|
2570
|
+
match skipped:
|
|
2571
|
+
case Skipped{path, Unloaded{}}:
|
|
2572
|
+
"bend-lint: Bend does not load "
|
|
2573
|
+
++ path
|
|
2574
|
+
++ ", so no rule ran on it; `bend <file> --check-only` says why"
|
|
2575
|
+
case Skipped{path, Unchecked{rules}}:
|
|
2576
|
+
"bend-lint: Bend's check of "
|
|
2577
|
+
++ path
|
|
2578
|
+
++ " fails, so the rules that need its facts did not run on it: "
|
|
2579
|
+
++ String.join(rules, ", ")
|
|
2580
|
+
++ "; `bend <file> --check-only` says why"
|
|
2581
|
+
case Skipped{path, Unentered{entry, rules}}:
|
|
2582
|
+
"bend-lint: Bend's check of the entry "
|
|
2583
|
+
++ entry
|
|
2584
|
+
++ " fails, so "
|
|
2585
|
+
++ path
|
|
2586
|
+
++ " was linted without it"
|
|
2587
|
+
++ silenced(rules)
|
|
2588
|
+
++ "; `bend <file> --check-only` says why"
|
|
2589
|
+
|
|
2590
|
+
def Skipped.strings(texts: List<&2, String>, out: List<&2, Json.Json>) -> Json.Json:
|
|
2591
|
+
match texts:
|
|
2592
|
+
case Nil{}:
|
|
2593
|
+
Json.JArray{List.reverse(&2, Json.Json, out)}
|
|
2594
|
+
case Con{t, rest}:
|
|
2595
|
+
Skipped.strings(rest, Json.JString{t} <> out)
|
|
2596
|
+
|
|
2597
|
+
# A skip as a JSON run gives it: the file, why (`reason`), the rules it is about, the entry, if Unentered, and what the
|
|
2598
|
+
# text run says.
|
|
2599
|
+
def Skipped.json(+skipped: Skipped) -> Json.Json:
|
|
2600
|
+
match skipped:
|
|
2601
|
+
case Skipped{+path, Unloaded{}}:
|
|
2602
|
+
Json.JObject{
|
|
2603
|
+
[
|
|
2604
|
+
Json.JField{"path", Json.JString{path}},
|
|
2605
|
+
Json.JField{"reason", Json.JString{"unloaded"}},
|
|
2606
|
+
Json.JField{"message", Json.JString{Skipped.text(skipped)}}
|
|
2607
|
+
]
|
|
2608
|
+
}
|
|
2609
|
+
case Skipped{+path, Unchecked{+rules}}:
|
|
2610
|
+
Json.JObject{
|
|
2611
|
+
[
|
|
2612
|
+
Json.JField{"path", Json.JString{path}},
|
|
2613
|
+
Json.JField{"reason", Json.JString{"unchecked"}},
|
|
2614
|
+
Json.JField{"rules", Skipped.strings(rules, [])},
|
|
2615
|
+
Json.JField{"message", Json.JString{Skipped.text(skipped)}}
|
|
2616
|
+
]
|
|
2617
|
+
}
|
|
2618
|
+
case Skipped{+path, Unentered{+entry, +rules}}:
|
|
2619
|
+
Json.JObject{
|
|
2620
|
+
[
|
|
2621
|
+
Json.JField{"path", Json.JString{path}},
|
|
2622
|
+
Json.JField{"reason", Json.JString{"unentered"}},
|
|
2623
|
+
Json.JField{"entry", Json.JString{entry}},
|
|
2624
|
+
Json.JField{"rules", Skipped.strings(rules, [])},
|
|
2625
|
+
Json.JField{"message", Json.JString{Skipped.text(skipped)}}
|
|
2626
|
+
]
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
# Says each of `skips` on stderr; gives them, in order, as JSON.
|
|
2630
|
+
def Skipped.said(skips: List<&2, Skipped>, out: List<&2, Json.Json>) -> IO(List<&2, Json.Json>):
|
|
2631
|
+
match skips:
|
|
2632
|
+
case Nil{}:
|
|
2633
|
+
IO.pure(List<&2, Json.Json>, List.reverse(&2, Json.Json, out))
|
|
2634
|
+
case Con{+s, rest}:
|
|
2635
|
+
IO.bind(
|
|
2636
|
+
Unit,
|
|
2637
|
+
List<&2, Json.Json>,
|
|
2638
|
+
IO.print_err(Skipped.text(s)),
|
|
2639
|
+
u => Skipped.said(rest, Skipped.json(s) <> out)
|
|
2640
|
+
)
|
|
2641
|
+
|
|
2642
|
+
def Outcome.skip(outcome: Outcome, skipped: List<&2, Json.Json>) -> Outcome:
|
|
2643
|
+
match outcome:
|
|
2644
|
+
case Outcome{n, f, s, e, x, w}:
|
|
2645
|
+
Outcome{n, f, s, e, x, Lists.append(&2, Json.Json, skipped, w)}
|
|
2456
2646
|
|
|
2457
2647
|
def checked(
|
|
2458
2648
|
~rules: List<&1, Rule>,
|
|
@@ -2466,9 +2656,24 @@ def checked(
|
|
|
2466
2656
|
match checks:
|
|
2467
2657
|
case Checks{Con{Lint.Program{+sources}, rest}, +failing, +failed, forget}:
|
|
2468
2658
|
+on = About.on(About.all(rules, []), settings, [])
|
|
2659
|
+
skips = Lists.append(
|
|
2660
|
+
&2,
|
|
2661
|
+
Skipped,
|
|
2662
|
+
unchecked(failed, About.ids(List.filter(~About, ~About.needs_facts, on), []), path),
|
|
2663
|
+
List.reverse(
|
|
2664
|
+
&2,
|
|
2665
|
+
Skipped,
|
|
2666
|
+
unentered(
|
|
2667
|
+
failed,
|
|
2668
|
+
failing,
|
|
2669
|
+
About.ids(List.filter(~About, ~About.needs_every, on), []),
|
|
2670
|
+
path,
|
|
2671
|
+
[]
|
|
2672
|
+
)
|
|
2673
|
+
)
|
|
2674
|
+
)
|
|
2469
2675
|
do IO<Outcome>:
|
|
2470
|
-
|
|
2471
|
-
unentered(failed, failing, About.ids(List.filter(~About, ~About.needs_every, on), []), path)
|
|
2676
|
+
skipped: List<&2, Json.Json> <- Skipped.said(skips, [])
|
|
2472
2677
|
o: Outcome <- lint(
|
|
2473
2678
|
~rules,
|
|
2474
2679
|
linted(imports, path, sources),
|
|
@@ -2479,15 +2684,11 @@ def checked(
|
|
|
2479
2684
|
json,
|
|
2480
2685
|
fixing
|
|
2481
2686
|
)
|
|
2482
|
-
forgotten(forget, o)
|
|
2687
|
+
forgotten(forget, Outcome.skip(o, skipped))
|
|
2483
2688
|
case Checks{Nil{}, failing, failed, forget}:
|
|
2484
2689
|
do IO<Outcome>:
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
++ path
|
|
2488
|
-
++ ", so no rule ran on it; `bend <file> --check-only` says why"
|
|
2489
|
-
)
|
|
2490
|
-
return Outcome{0n, [], [], False{}, 0}
|
|
2690
|
+
skipped: List<&2, Json.Json> <- Skipped.said([Skipped{path, Unloaded{}}], [])
|
|
2691
|
+
return Outcome{0n, [], [], False{}, 0, skipped}
|
|
2491
2692
|
|
|
2492
2693
|
# The named file's own check, as the checks to lint it in, after the entries that read it and failed (`failing`). If
|
|
2493
2694
|
# it fails, the rules that need no facts run on what it parsed, if it parsed the files.
|
|
@@ -2601,10 +2802,10 @@ def Check.of(imports: Bool, entries: List<&2, String>, +path: String, +facts: Bo
|
|
|
2601
2802
|
c => IO.pure(Checks, Check.own([], c))
|
|
2602
2803
|
)
|
|
2603
2804
|
|
|
2604
|
-
def Options.checked(problem: Maybe<&2, String>) -> IO(Unit):
|
|
2805
|
+
def Options.checked(json: Bool, problem: Maybe<&2, String>) -> IO(Unit):
|
|
2605
2806
|
match problem:
|
|
2606
2807
|
case Some{why}:
|
|
2607
|
-
|
|
2808
|
+
Error.end(Unit, json, 2, "", "bend-lint config: " ++ why)
|
|
2608
2809
|
case None{}:
|
|
2609
2810
|
IO.pure(Unit, Unit{})
|
|
2610
2811
|
|
|
@@ -2626,6 +2827,11 @@ type Options is Data:
|
|
|
2626
2827
|
jobs: Jobs
|
|
2627
2828
|
}
|
|
2628
2829
|
|
|
2830
|
+
def Options.is_json(options: Options) -> Bool:
|
|
2831
|
+
match options:
|
|
2832
|
+
case Options{inputs, fixing, shown, json, worker, jobs}:
|
|
2833
|
+
json
|
|
2834
|
+
|
|
2629
2835
|
# Whether a run with the fix flag `fixing` makes its fixes: `--fix-dangerously` does. `--fix` makes none until Bend's
|
|
2630
2836
|
# checker in Bend can prove that a fix keeps the program, and says so (`Options.note`).
|
|
2631
2837
|
def Options.fixes(fixing: Maybe<&2, Bool>) -> Bool:
|
|
@@ -2641,10 +2847,10 @@ def Options.note(fixing: Maybe<&2, Bool>) -> IO(Unit):
|
|
|
2641
2847
|
IO.pure(Unit, Unit{})
|
|
2642
2848
|
|
|
2643
2849
|
# Stops the run if it lints the imports of more than one file: a run with imports has one root.
|
|
2644
|
-
def Options.single(imports: Bool, many: Bool) -> IO(Unit):
|
|
2850
|
+
def Options.single(json: Bool, imports: Bool, many: Bool) -> IO(Unit):
|
|
2645
2851
|
match imports many:
|
|
2646
2852
|
case True{} True{}:
|
|
2647
|
-
|
|
2853
|
+
Error.end(Unit, json, 2, "", "bend-lint: a config with \"imports\" lints one file at a time")
|
|
2648
2854
|
case _ _:
|
|
2649
2855
|
IO.pure(Unit, Unit{})
|
|
2650
2856
|
|
|
@@ -2678,11 +2884,11 @@ def configured(
|
|
|
2678
2884
|
project: Project
|
|
2679
2885
|
) -> IO(Outcome):
|
|
2680
2886
|
match options project:
|
|
2681
|
-
case Options{inputs, fixing, shown, json, worker, jobs} Project{entries, from, +settings}:
|
|
2887
|
+
case Options{inputs, fixing, shown, +json, worker, jobs} Project{entries, from, +settings}:
|
|
2682
2888
|
+on = Maybe.default(&2, Bool, from, False{})
|
|
2683
2889
|
do IO<Outcome>:
|
|
2684
|
-
Options.checked(About.problem(About.all(rules, []), settings))
|
|
2685
|
-
Options.single(on, many)
|
|
2890
|
+
Options.checked(json, About.problem(About.all(rules, []), settings))
|
|
2891
|
+
Options.single(json, on, many)
|
|
2686
2892
|
settled(
|
|
2687
2893
|
~rules,
|
|
2688
2894
|
path,
|
|
@@ -2788,11 +2994,11 @@ def Options.read(args: List<String>, options: Maybe<&2, Options>, jobs: Bool) ->
|
|
|
2788
2994
|
String.eq(a, "--jobs")
|
|
2789
2995
|
)
|
|
2790
2996
|
|
|
2791
|
-
def File.lint(~rules: List<&1, Rule>, path: String, options: Options, many: Bool) -> IO(Outcome):
|
|
2997
|
+
def File.lint(~rules: List<&1, Rule>, path: String, +options: Options, many: Bool) -> IO(Outcome):
|
|
2792
2998
|
IO.bind(
|
|
2793
2999
|
Project,
|
|
2794
3000
|
Outcome,
|
|
2795
|
-
Config.load(),
|
|
3001
|
+
Config.load(Options.is_json(options)),
|
|
2796
3002
|
project => configured(~rules, path, options, many, project)
|
|
2797
3003
|
)
|
|
2798
3004
|
|
|
@@ -2823,27 +3029,84 @@ def Paths.lint(
|
|
|
2823
3029
|
o => Paths.lint(~rules, rest, options, many, Outcome.add(total, o))
|
|
2824
3030
|
)
|
|
2825
3031
|
|
|
2826
|
-
|
|
3032
|
+
# The paths the server holds an overlay for (`--overlays`, see seam.ts), one a line in BEND_LINT_OVERLAYS: none, if it
|
|
3033
|
+
# gives none.
|
|
3034
|
+
def Overlays.of(given: Result<&1, &1, U32 & String, String>) -> List<&2, String>:
|
|
3035
|
+
match given:
|
|
3036
|
+
case Done{text}:
|
|
3037
|
+
List.filter(~String, ~(n => Bool.not(String.is_empty(n))), String.split(text, '
|
|
3038
|
+
'))
|
|
3039
|
+
case Fail{why}:
|
|
3040
|
+
[]
|
|
3041
|
+
|
|
3042
|
+
# Stops a run that makes fixes (`fixes`) to files with overlays: a fix writes the file on disk, not the text it linted.
|
|
3043
|
+
def Overlays.fixing(json: Bool, fixes: Bool, overlays: List<&2, String>) -> IO(Unit):
|
|
3044
|
+
match fixes overlays:
|
|
3045
|
+
case True{} Con{o, rest}:
|
|
3046
|
+
Error.end(
|
|
3047
|
+
Unit,
|
|
3048
|
+
json,
|
|
3049
|
+
2,
|
|
3050
|
+
"",
|
|
3051
|
+
"bend-lint: --fix-dangerously does not go with --overlays: a fix writes the file on disk"
|
|
3052
|
+
)
|
|
3053
|
+
case _ _:
|
|
3054
|
+
IO.pure(Unit, Unit{})
|
|
3055
|
+
|
|
3056
|
+
def files.of(
|
|
3057
|
+
json: Bool,
|
|
3058
|
+
kept: List<&2, String>,
|
|
3059
|
+
found: Result<&2, &2, String, List<&2, String>>
|
|
3060
|
+
) -> IO(List<&2, String>):
|
|
2827
3061
|
match found:
|
|
2828
3062
|
case Done{paths}:
|
|
2829
|
-
IO.pure(List<&2, String>, paths)
|
|
3063
|
+
IO.pure(List<&2, String>, Lists.append(&2, String, kept, paths))
|
|
2830
3064
|
case Fail{why}:
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
# The
|
|
2834
|
-
|
|
2835
|
-
|
|
3065
|
+
Error.end(List<&2, String>, json, 2, "", "bend-lint: " ++ why)
|
|
3066
|
+
|
|
3067
|
+
# The inputs that name a file with an overlay (`overlays`), onto `kept`, and the others, onto `rest` (both last first).
|
|
3068
|
+
def files.sort(
|
|
3069
|
+
inputs: List<&2, String>,
|
|
3070
|
+
+overlays: List<&2, String>,
|
|
3071
|
+
kept: List<&2, String>,
|
|
3072
|
+
rest: List<&2, String>
|
|
3073
|
+
) -> List<&2, String>
|
|
3074
|
+
& List<&2, String>:
|
|
2836
3075
|
match inputs:
|
|
2837
3076
|
case Nil{}:
|
|
2838
|
-
|
|
2839
|
-
case Con{
|
|
3077
|
+
(kept, rest)
|
|
3078
|
+
case Con{+input, more}:
|
|
3079
|
+
+hit = List.contains(~String, ~String.eq, overlays, input)
|
|
3080
|
+
files.sort(
|
|
3081
|
+
more,
|
|
3082
|
+
overlays,
|
|
3083
|
+
Lists.push(String, Lists.when(String, hit, input), kept),
|
|
3084
|
+
Lists.push(String, Lists.when(String, Bool.not(hit), input), rest)
|
|
3085
|
+
)
|
|
3086
|
+
|
|
3087
|
+
# The files of `sorted` (see files.sort): those with an overlay, which need not be on disk, then those the others name.
|
|
3088
|
+
def files.named(json: Bool, sorted: List<&2, String> & List<&2, String>) -> IO(List<&2, String>):
|
|
3089
|
+
match sorted:
|
|
3090
|
+
case Tuple{kept, Nil{}}:
|
|
3091
|
+
IO.pure(List<&2, String>, List.reverse(&2, String, kept))
|
|
3092
|
+
case Tuple{kept, Con{r, rest}}:
|
|
2840
3093
|
IO.bind(
|
|
2841
3094
|
Result<&2, &2, String, List<&2, String>>,
|
|
2842
3095
|
List<&2, String>,
|
|
2843
|
-
Files.named(Con{
|
|
2844
|
-
found => files.of(found)
|
|
3096
|
+
Files.named(List.reverse(&2, String, Con{r, rest})),
|
|
3097
|
+
found => files.of(json, List.reverse(&2, String, kept), found)
|
|
2845
3098
|
)
|
|
2846
3099
|
|
|
3100
|
+
# The files the inputs name; with none, the .bend files under the current folder (see Files.listed). An input that
|
|
3101
|
+
# names none ends the run with a usage error (in JSON if `json`); one that names a file with an overlay (in `overlays`) names it, on disk
|
|
3102
|
+
# or not.
|
|
3103
|
+
def files(json: Bool, inputs: List<&2, String>, overlays: List<&2, String>) -> IO(List<&2, String>):
|
|
3104
|
+
match inputs:
|
|
3105
|
+
case Nil{}:
|
|
3106
|
+
Files.listed("*.bend")
|
|
3107
|
+
case Con{first, more}:
|
|
3108
|
+
files.named(json, files.sort(Con{first, more}, overlays, [], []))
|
|
3109
|
+
|
|
2847
3110
|
# Workers
|
|
2848
3111
|
# -------
|
|
2849
3112
|
|
|
@@ -2947,8 +3210,10 @@ def Worker.flag(on: Bool, flag: String) -> List<String>:
|
|
|
2947
3210
|
def Worker.args(options: Options, path: String, threads: String) -> List<String>:
|
|
2948
3211
|
match options:
|
|
2949
3212
|
case Options{inputs, fixing, shown, json, worker, jobs}:
|
|
2950
|
-
"
|
|
3213
|
+
"+RTS"
|
|
3214
|
+
<> "--threads"
|
|
2951
3215
|
<> threads
|
|
3216
|
+
<> "-RTS"
|
|
2952
3217
|
<> "--worker"
|
|
2953
3218
|
<> List.append(
|
|
2954
3219
|
&1,
|
|
@@ -3051,7 +3316,7 @@ def Workers.run(
|
|
|
3051
3316
|
paths,
|
|
3052
3317
|
[]
|
|
3053
3318
|
)
|
|
3054
|
-
total: Outcome <- Workers.collect(workers, Outcome{0n, [], [], False{}, 0})
|
|
3319
|
+
total: Outcome <- Workers.collect(workers, Outcome{0n, [], [], False{}, 0, []})
|
|
3055
3320
|
ended(options, total)
|
|
3056
3321
|
|
|
3057
3322
|
# Lints `paths` in workers of `program`, as `options` say.
|
|
@@ -3106,7 +3371,7 @@ def Paths.go(
|
|
|
3106
3371
|
paths,
|
|
3107
3372
|
options,
|
|
3108
3373
|
Options.is_many(options, paths),
|
|
3109
|
-
Outcome{0n, [], [], False{}, 0}
|
|
3374
|
+
Outcome{0n, [], [], False{}, 0, []}
|
|
3110
3375
|
)
|
|
3111
3376
|
|
|
3112
3377
|
# Lints `paths` as `options` say: in workers, if the run can (see Workers), else here.
|
|
@@ -3120,13 +3385,16 @@ def Paths.run(
|
|
|
3120
3385
|
|
|
3121
3386
|
def checking(~rules: List<&1, Rule>, program: String, options: Maybe<&2, Options>) -> IO(Unit):
|
|
3122
3387
|
match options:
|
|
3123
|
-
case Some{Options{+inputs, +fixing, shown, json, worker, jobs}}:
|
|
3388
|
+
case Some{Options{+inputs, +fixing, shown, +json, worker, jobs}}:
|
|
3124
3389
|
do IO<Unit>:
|
|
3125
3390
|
Options.note(fixing)
|
|
3391
|
+
given: Result<&1, &1, U32 & String, String> <- IO.get_env("BEND_LINT_OVERLAYS")
|
|
3392
|
+
+overlays: List<&2, String> = Overlays.of(given)
|
|
3393
|
+
Overlays.fixing(json, Options.fixes(fixing), overlays)
|
|
3126
3394
|
IO.bind(
|
|
3127
3395
|
List<&2, String>,
|
|
3128
3396
|
Unit,
|
|
3129
|
-
files(inputs),
|
|
3397
|
+
files(json, inputs, overlays),
|
|
3130
3398
|
+paths => Paths.run(
|
|
3131
3399
|
~rules,
|
|
3132
3400
|
program,
|
|
@@ -3138,7 +3406,7 @@ def checking(~rules: List<&1, Rule>, program: String, options: Maybe<&2, Options
|
|
|
3138
3406
|
IO.die(
|
|
3139
3407
|
Unit,
|
|
3140
3408
|
2,
|
|
3141
|
-
"usage: bend-lint [<file or glob>...] [--fix | --fix-dangerously] [--show-suppressed] [--json] [--jobs <number or max>]"
|
|
3409
|
+
"usage: bend-lint [<file or glob>...] [--fix | --fix-dangerously] [--show-suppressed] [--json] [--jobs <number or max>] [--overlays] [--report <file>]"
|
|
3142
3410
|
)
|
|
3143
3411
|
|
|
3144
3412
|
# Runs the command line `args`: the program as invoked, then its arguments.
|