@@ -3,11 +3,6 @@ Copyright (c) 2023 Mario Carneiro. All rights reserved.
3
3
Released under Apache 2.0 license as described in the file LICENSE.
4
4
Authors: Mario Carneiro
5
5
-/
6
- import Lake.Util.Error
7
- import Lean.Environment
8
- import Lean.Parser.Module
9
- import Lean.Util.FoldConsts
10
- import Lean.Util.Paths
11
6
import Lake.CLI.Main
12
7
13
8
/-! # `lake exe shake` command
@@ -221,24 +216,38 @@ def Edits.add (ed : Edits) (src : Name) (tgt : Nat) : Edits :=
221
216
222
217
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
223
218
224
- Returns `(path, inputCtx, headerStx , endPos)` where `headerStx ` is the `Lean.Parser.Module.header`
219
+ Returns `(path, inputCtx, imports , endPos)` where `imports ` is the `Lean.Parser.Module.import` list
225
220
and `endPos` is the position of the end of the header.
226
221
-/
227
- def parseHeader (srcSearchPath : SearchPath) (mod : Name) :
228
- IO (System.FilePath × Parser.InputContext × TSyntax ``Parser.Module.header × String.Pos) := do
229
- -- Parse the input file
230
- let some path ← srcSearchPath.findModuleWithExt "lean" mod
231
- | throw <| .userError "error: failed to find source file for {mod}"
232
- let text ← IO.FS.readFile path
233
- let inputCtx := Parser.mkInputContext text path.toString
222
+ def parseHeaderFromString (text path : String) :
223
+ IO (System.FilePath × Parser.InputContext ×
224
+ TSyntaxArray ``Parser.Module.import × String.Pos) := do
225
+ let inputCtx := Parser.mkInputContext text path
234
226
let (header, parserState, msgs) ← Parser.parseHeader inputCtx
235
227
if !msgs.toList.isEmpty then -- skip this file if there are parse errors
236
228
msgs.forM fun msg => msg.toString >>= IO.println
237
229
throw <| .userError "parse errors in file"
238
230
-- the insertion point for `add` is the first newline after the imports
239
231
let insertion := header.raw.getTailPos?.getD parserState.pos
240
232
let insertion := text.findAux (· == '\n ' ) text.endPos insertion + ⟨1 ⟩
241
- pure (path, inputCtx, header, insertion)
233
+ pure (path, inputCtx, .mk header.raw[2 ].getArgs, insertion)
234
+
235
+ /-- Parse a source file to extract the location of the import lines, for edits and error messages.
236
+
237
+ Returns `(path, inputCtx, imports, endPos)` where `imports` is the `Lean.Parser.Module.import` list
238
+ and `endPos` is the position of the end of the header.
239
+ -/
240
+ def parseHeader (srcSearchPath : SearchPath) (mod : Name) :
241
+ IO (System.FilePath × Parser.InputContext ×
242
+ TSyntaxArray ``Parser.Module.import × String.Pos) := do
243
+ -- Parse the input file
244
+ let some path ← srcSearchPath.findModuleWithExt "lean" mod
245
+ | throw <| .userError "error: failed to find source file for {mod}"
246
+ let text ← IO.FS.readFile path
247
+ parseHeaderFromString text path.toString
248
+
249
+ /-- Gets the name `Foo` in `import Foo`. -/
250
+ def importId (stx : TSyntax ``Parser.Module.import) : Name := stx.raw[3 ].getId
242
251
243
252
/-- Analyze and report issues from module `i`. Arguments:
244
253
@@ -291,10 +300,10 @@ def visitModule (s : State) (srcSearchPath : SearchPath) (ignoreImps : Bitset)
291
300
edits.remove s.modNames[i]! s.modNames[n]!
292
301
if githubStyle then
293
302
try
294
- let (path, inputCtx, header , endHeader) ← parseHeader srcSearchPath s.modNames[i]!
295
- for stx in header.raw[ 2 ].getArgs do
296
- if toRemove.any fun i => s.modNames[i]! == stx[ 2 ].getId then
297
- let pos := inputCtx.fileMap.toPosition stx.getPos?.get!
303
+ let (path, inputCtx, imports , endHeader) ← parseHeader srcSearchPath s.modNames[i]!
304
+ for stx in imports do
305
+ if toRemove.any fun i => s.modNames[i]! == importId stx then
306
+ let pos := inputCtx.fileMap.toPosition stx.raw. getPos?.get!
298
307
println! "{path}:{pos.line}:{pos.column+1}: warning: unused import \
299
308
(use `lake exe shake --fix` to fix this, or `lake exe shake --update` to ignore)"
300
309
if !toAdd.isEmpty then
@@ -595,7 +604,7 @@ def main (args : List String) : IO UInt32 := do
595
604
out.qsort Name.lt
596
605
597
606
-- Parse the input file
598
- let (path, inputCtx, header , insertion) ←
607
+ let (path, inputCtx, imports , insertion) ←
599
608
try parseHeader srcSearchPath mod
600
609
catch e => println! e.toString; return count
601
610
let text := inputCtx.input
@@ -604,12 +613,12 @@ def main (args : List String) : IO UInt32 := do
604
613
let mut pos : String.Pos := 0
605
614
let mut out : String := ""
606
615
let mut seen : NameSet := {}
607
- for stx in header.raw[ 2 ].getArgs do
608
- let mod := stx[ 2 ].getId
616
+ for stx in imports do
617
+ let mod := importId stx
609
618
if remove.contains mod || seen.contains mod then
610
- out := out ++ text.extract pos stx.getPos?.get!
619
+ out := out ++ text.extract pos stx.raw. getPos?.get!
611
620
-- We use the end position of the syntax, but include whitespace up to the first newline
612
- pos := text.findAux (· == '\n ' ) text.endPos stx.getTailPos?.get! + ⟨1 ⟩
621
+ pos := text.findAux (· == '\n ' ) text.endPos stx.raw. getTailPos?.get! + ⟨1 ⟩
613
622
seen := seen.insert mod
614
623
out := out ++ text.extract pos insertion
615
624
for mod in add do
@@ -628,3 +637,10 @@ def main (args : List String) : IO UInt32 := do
628
637
else
629
638
println! "No edits required."
630
639
return 0
640
+
641
+ -- self-test so that future grammar changes cause a build failure
642
+ /-- info: #[ `Lake.CLI.Main ] -/
643
+ #guard_msgs (whitespace := lax) in
644
+ #eval show MetaM _ from do
645
+ let (_, _, imports, _) ← parseHeaderFromString (← getFileMap).source (← getFileName)
646
+ return imports.map importId
0 commit comments