Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / rogpeppe / go-internal / modfile / rule.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package modfile
6
7 import (
8         "bytes"
9         "errors"
10         "fmt"
11         "path/filepath"
12         "regexp"
13         "sort"
14         "strconv"
15         "strings"
16         "unicode"
17
18         "github.com/rogpeppe/go-internal/module"
19         "github.com/rogpeppe/go-internal/semver"
20 )
21
22 // A File is the parsed, interpreted form of a go.mod file.
23 type File struct {
24         Module  *Module
25         Go      *Go
26         Require []*Require
27         Exclude []*Exclude
28         Replace []*Replace
29
30         Syntax *FileSyntax
31 }
32
33 // A Module is the module statement.
34 type Module struct {
35         Mod    module.Version
36         Syntax *Line
37 }
38
39 // A Go is the go statement.
40 type Go struct {
41         Version string // "1.23"
42         Syntax  *Line
43 }
44
45 // A Require is a single require statement.
46 type Require struct {
47         Mod      module.Version
48         Indirect bool // has "// indirect" comment
49         Syntax   *Line
50 }
51
52 // An Exclude is a single exclude statement.
53 type Exclude struct {
54         Mod    module.Version
55         Syntax *Line
56 }
57
58 // A Replace is a single replace statement.
59 type Replace struct {
60         Old    module.Version
61         New    module.Version
62         Syntax *Line
63 }
64
65 func (f *File) AddModuleStmt(path string) error {
66         if f.Syntax == nil {
67                 f.Syntax = new(FileSyntax)
68         }
69         if f.Module == nil {
70                 f.Module = &Module{
71                         Mod:    module.Version{Path: path},
72                         Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)),
73                 }
74         } else {
75                 f.Module.Mod.Path = path
76                 f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path))
77         }
78         return nil
79 }
80
81 func (f *File) AddComment(text string) {
82         if f.Syntax == nil {
83                 f.Syntax = new(FileSyntax)
84         }
85         f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{
86                 Comments: Comments{
87                         Before: []Comment{
88                                 {
89                                         Token: text,
90                                 },
91                         },
92                 },
93         })
94 }
95
96 type VersionFixer func(path, version string) (string, error)
97
98 // Parse parses the data, reported in errors as being from file,
99 // into a File struct. It applies fix, if non-nil, to canonicalize all module versions found.
100 func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
101         return parseToFile(file, data, fix, true)
102 }
103
104 // ParseLax is like Parse but ignores unknown statements.
105 // It is used when parsing go.mod files other than the main module,
106 // under the theory that most statement types we add in the future will
107 // only apply in the main module, like exclude and replace,
108 // and so we get better gradual deployments if old go commands
109 // simply ignore those statements when found in go.mod files
110 // in dependencies.
111 func ParseLax(file string, data []byte, fix VersionFixer) (*File, error) {
112         return parseToFile(file, data, fix, false)
113 }
114
115 func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (*File, error) {
116         fs, err := parse(file, data)
117         if err != nil {
118                 return nil, err
119         }
120         f := &File{
121                 Syntax: fs,
122         }
123
124         var errs bytes.Buffer
125         for _, x := range fs.Stmt {
126                 switch x := x.(type) {
127                 case *Line:
128                         f.add(&errs, x, x.Token[0], x.Token[1:], fix, strict)
129
130                 case *LineBlock:
131                         if len(x.Token) > 1 {
132                                 if strict {
133                                         fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
134                                 }
135                                 continue
136                         }
137                         switch x.Token[0] {
138                         default:
139                                 if strict {
140                                         fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
141                                 }
142                                 continue
143                         case "module", "require", "exclude", "replace":
144                                 for _, l := range x.Line {
145                                         f.add(&errs, l, x.Token[0], l.Token, fix, strict)
146                                 }
147                         }
148                 }
149         }
150
151         if errs.Len() > 0 {
152                 return nil, errors.New(strings.TrimRight(errs.String(), "\n"))
153         }
154         return f, nil
155 }
156
157 var goVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`)
158
159 func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
160         // If strict is false, this module is a dependency.
161         // We ignore all unknown directives as well as main-module-only
162         // directives like replace and exclude. It will work better for
163         // forward compatibility if we can depend on modules that have unknown
164         // statements (presumed relevant only when acting as the main module)
165         // and simply ignore those statements.
166         if !strict {
167                 switch verb {
168                 case "module", "require", "go":
169                         // want these even for dependency go.mods
170                 default:
171                         return
172                 }
173         }
174
175         switch verb {
176         default:
177                 fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb)
178
179         case "go":
180                 if f.Go != nil {
181                         fmt.Fprintf(errs, "%s:%d: repeated go statement\n", f.Syntax.Name, line.Start.Line)
182                         return
183                 }
184                 if len(args) != 1 || !goVersionRE.MatchString(args[0]) {
185                         fmt.Fprintf(errs, "%s:%d: usage: go 1.23\n", f.Syntax.Name, line.Start.Line)
186                         return
187                 }
188                 f.Go = &Go{Syntax: line}
189                 f.Go.Version = args[0]
190         case "module":
191                 if f.Module != nil {
192                         fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line)
193                         return
194                 }
195                 f.Module = &Module{Syntax: line}
196                 if len(args) != 1 {
197
198                         fmt.Fprintf(errs, "%s:%d: usage: module module/path [version]\n", f.Syntax.Name, line.Start.Line)
199                         return
200                 }
201                 s, err := parseString(&args[0])
202                 if err != nil {
203                         fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
204                         return
205                 }
206                 f.Module.Mod = module.Version{Path: s}
207         case "require", "exclude":
208                 if len(args) != 2 {
209                         fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3\n", f.Syntax.Name, line.Start.Line, verb)
210                         return
211                 }
212                 s, err := parseString(&args[0])
213                 if err != nil {
214                         fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
215                         return
216                 }
217                 old := args[1]
218                 v, err := parseVersion(s, &args[1], fix)
219                 if err != nil {
220                         fmt.Fprintf(errs, "%s:%d: invalid module version %q: %v\n", f.Syntax.Name, line.Start.Line, old, err)
221                         return
222                 }
223                 pathMajor, err := modulePathMajor(s)
224                 if err != nil {
225                         fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
226                         return
227                 }
228                 if !module.MatchPathMajor(v, pathMajor) {
229                         if pathMajor == "" {
230                                 pathMajor = "v0 or v1"
231                         }
232                         fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v)
233                         return
234                 }
235                 if verb == "require" {
236                         f.Require = append(f.Require, &Require{
237                                 Mod:      module.Version{Path: s, Version: v},
238                                 Syntax:   line,
239                                 Indirect: isIndirect(line),
240                         })
241                 } else {
242                         f.Exclude = append(f.Exclude, &Exclude{
243                                 Mod:    module.Version{Path: s, Version: v},
244                                 Syntax: line,
245                         })
246                 }
247         case "replace":
248                 arrow := 2
249                 if len(args) >= 2 && args[1] == "=>" {
250                         arrow = 1
251                 }
252                 if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" {
253                         fmt.Fprintf(errs, "%s:%d: usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory\n", f.Syntax.Name, line.Start.Line, verb, verb)
254                         return
255                 }
256                 s, err := parseString(&args[0])
257                 if err != nil {
258                         fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
259                         return
260                 }
261                 pathMajor, err := modulePathMajor(s)
262                 if err != nil {
263                         fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
264                         return
265                 }
266                 var v string
267                 if arrow == 2 {
268                         old := args[1]
269                         v, err = parseVersion(s, &args[1], fix)
270                         if err != nil {
271                                 fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
272                                 return
273                         }
274                         if !module.MatchPathMajor(v, pathMajor) {
275                                 if pathMajor == "" {
276                                         pathMajor = "v0 or v1"
277                                 }
278                                 fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v)
279                                 return
280                         }
281                 }
282                 ns, err := parseString(&args[arrow+1])
283                 if err != nil {
284                         fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
285                         return
286                 }
287                 nv := ""
288                 if len(args) == arrow+2 {
289                         if !IsDirectoryPath(ns) {
290                                 fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)\n", f.Syntax.Name, line.Start.Line)
291                                 return
292                         }
293                         if filepath.Separator == '/' && strings.Contains(ns, `\`) {
294                                 fmt.Fprintf(errs, "%s:%d: replacement directory appears to be Windows path (on a non-windows system)\n", f.Syntax.Name, line.Start.Line)
295                                 return
296                         }
297                 }
298                 if len(args) == arrow+3 {
299                         old := args[arrow+1]
300                         nv, err = parseVersion(ns, &args[arrow+2], fix)
301                         if err != nil {
302                                 fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
303                                 return
304                         }
305                         if IsDirectoryPath(ns) {
306                                 fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version\n", f.Syntax.Name, line.Start.Line, ns)
307                                 return
308                         }
309                 }
310                 f.Replace = append(f.Replace, &Replace{
311                         Old:    module.Version{Path: s, Version: v},
312                         New:    module.Version{Path: ns, Version: nv},
313                         Syntax: line,
314                 })
315         }
316 }
317
318 // isIndirect reports whether line has a "// indirect" comment,
319 // meaning it is in go.mod only for its effect on indirect dependencies,
320 // so that it can be dropped entirely once the effective version of the
321 // indirect dependency reaches the given minimum version.
322 func isIndirect(line *Line) bool {
323         if len(line.Suffix) == 0 {
324                 return false
325         }
326         f := strings.Fields(line.Suffix[0].Token)
327         return (len(f) == 2 && f[1] == "indirect" || len(f) > 2 && f[1] == "indirect;") && f[0] == "//"
328 }
329
330 // setIndirect sets line to have (or not have) a "// indirect" comment.
331 func setIndirect(line *Line, indirect bool) {
332         if isIndirect(line) == indirect {
333                 return
334         }
335         if indirect {
336                 // Adding comment.
337                 if len(line.Suffix) == 0 {
338                         // New comment.
339                         line.Suffix = []Comment{{Token: "// indirect", Suffix: true}}
340                         return
341                 }
342                 // Insert at beginning of existing comment.
343                 com := &line.Suffix[0]
344                 space := " "
345                 if len(com.Token) > 2 && com.Token[2] == ' ' || com.Token[2] == '\t' {
346                         space = ""
347                 }
348                 com.Token = "// indirect;" + space + com.Token[2:]
349                 return
350         }
351
352         // Removing comment.
353         f := strings.Fields(line.Suffix[0].Token)
354         if len(f) == 2 {
355                 // Remove whole comment.
356                 line.Suffix = nil
357                 return
358         }
359
360         // Remove comment prefix.
361         com := &line.Suffix[0]
362         i := strings.Index(com.Token, "indirect;")
363         com.Token = "//" + com.Token[i+len("indirect;"):]
364 }
365
366 // IsDirectoryPath reports whether the given path should be interpreted
367 // as a directory path. Just like on the go command line, relative paths
368 // and rooted paths are directory paths; the rest are module paths.
369 func IsDirectoryPath(ns string) bool {
370         // Because go.mod files can move from one system to another,
371         // we check all known path syntaxes, both Unix and Windows.
372         return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") ||
373                 strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) ||
374                 len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':'
375 }
376
377 // MustQuote reports whether s must be quoted in order to appear as
378 // a single token in a go.mod line.
379 func MustQuote(s string) bool {
380         for _, r := range s {
381                 if !unicode.IsPrint(r) || r == ' ' || r == '"' || r == '\'' || r == '`' {
382                         return true
383                 }
384         }
385         return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*")
386 }
387
388 // AutoQuote returns s or, if quoting is required for s to appear in a go.mod,
389 // the quotation of s.
390 func AutoQuote(s string) string {
391         if MustQuote(s) {
392                 return strconv.Quote(s)
393         }
394         return s
395 }
396
397 func parseString(s *string) (string, error) {
398         t := *s
399         if strings.HasPrefix(t, `"`) {
400                 var err error
401                 if t, err = strconv.Unquote(t); err != nil {
402                         return "", err
403                 }
404         } else if strings.ContainsAny(t, "\"'`") {
405                 // Other quotes are reserved both for possible future expansion
406                 // and to avoid confusion. For example if someone types 'x'
407                 // we want that to be a syntax error and not a literal x in literal quotation marks.
408                 return "", fmt.Errorf("unquoted string cannot contain quote")
409         }
410         *s = AutoQuote(t)
411         return t, nil
412 }
413
414 func parseVersion(path string, s *string, fix VersionFixer) (string, error) {
415         t, err := parseString(s)
416         if err != nil {
417                 return "", err
418         }
419         if fix != nil {
420                 var err error
421                 t, err = fix(path, t)
422                 if err != nil {
423                         return "", err
424                 }
425         }
426         if v := module.CanonicalVersion(t); v != "" {
427                 *s = v
428                 return *s, nil
429         }
430         return "", fmt.Errorf("version must be of the form v1.2.3")
431 }
432
433 func modulePathMajor(path string) (string, error) {
434         _, major, ok := module.SplitPathVersion(path)
435         if !ok {
436                 return "", fmt.Errorf("invalid module path")
437         }
438         return major, nil
439 }
440
441 func (f *File) Format() ([]byte, error) {
442         return Format(f.Syntax), nil
443 }
444
445 // Cleanup cleans up the file f after any edit operations.
446 // To avoid quadratic behavior, modifications like DropRequire
447 // clear the entry but do not remove it from the slice.
448 // Cleanup cleans out all the cleared entries.
449 func (f *File) Cleanup() {
450         w := 0
451         for _, r := range f.Require {
452                 if r.Mod.Path != "" {
453                         f.Require[w] = r
454                         w++
455                 }
456         }
457         f.Require = f.Require[:w]
458
459         w = 0
460         for _, x := range f.Exclude {
461                 if x.Mod.Path != "" {
462                         f.Exclude[w] = x
463                         w++
464                 }
465         }
466         f.Exclude = f.Exclude[:w]
467
468         w = 0
469         for _, r := range f.Replace {
470                 if r.Old.Path != "" {
471                         f.Replace[w] = r
472                         w++
473                 }
474         }
475         f.Replace = f.Replace[:w]
476
477         f.Syntax.Cleanup()
478 }
479
480 func (f *File) AddRequire(path, vers string) error {
481         need := true
482         for _, r := range f.Require {
483                 if r.Mod.Path == path {
484                         if need {
485                                 r.Mod.Version = vers
486                                 f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers)
487                                 need = false
488                         } else {
489                                 f.Syntax.removeLine(r.Syntax)
490                                 *r = Require{}
491                         }
492                 }
493         }
494
495         if need {
496                 f.AddNewRequire(path, vers, false)
497         }
498         return nil
499 }
500
501 func (f *File) AddNewRequire(path, vers string, indirect bool) {
502         line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers)
503         setIndirect(line, indirect)
504         f.Require = append(f.Require, &Require{module.Version{Path: path, Version: vers}, indirect, line})
505 }
506
507 func (f *File) SetRequire(req []*Require) {
508         need := make(map[string]string)
509         indirect := make(map[string]bool)
510         for _, r := range req {
511                 need[r.Mod.Path] = r.Mod.Version
512                 indirect[r.Mod.Path] = r.Indirect
513         }
514
515         for _, r := range f.Require {
516                 if v, ok := need[r.Mod.Path]; ok {
517                         r.Mod.Version = v
518                         r.Indirect = indirect[r.Mod.Path]
519                 }
520         }
521
522         var newStmts []Expr
523         for _, stmt := range f.Syntax.Stmt {
524                 switch stmt := stmt.(type) {
525                 case *LineBlock:
526                         if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
527                                 var newLines []*Line
528                                 for _, line := range stmt.Line {
529                                         if p, err := parseString(&line.Token[0]); err == nil && need[p] != "" {
530                                                 line.Token[1] = need[p]
531                                                 delete(need, p)
532                                                 setIndirect(line, indirect[p])
533                                                 newLines = append(newLines, line)
534                                         }
535                                 }
536                                 if len(newLines) == 0 {
537                                         continue // drop stmt
538                                 }
539                                 stmt.Line = newLines
540                         }
541
542                 case *Line:
543                         if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
544                                 if p, err := parseString(&stmt.Token[1]); err == nil && need[p] != "" {
545                                         stmt.Token[2] = need[p]
546                                         delete(need, p)
547                                         setIndirect(stmt, indirect[p])
548                                 } else {
549                                         continue // drop stmt
550                                 }
551                         }
552                 }
553                 newStmts = append(newStmts, stmt)
554         }
555         f.Syntax.Stmt = newStmts
556
557         for path, vers := range need {
558                 f.AddNewRequire(path, vers, indirect[path])
559         }
560         f.SortBlocks()
561 }
562
563 func (f *File) DropRequire(path string) error {
564         for _, r := range f.Require {
565                 if r.Mod.Path == path {
566                         f.Syntax.removeLine(r.Syntax)
567                         *r = Require{}
568                 }
569         }
570         return nil
571 }
572
573 func (f *File) AddExclude(path, vers string) error {
574         var hint *Line
575         for _, x := range f.Exclude {
576                 if x.Mod.Path == path && x.Mod.Version == vers {
577                         return nil
578                 }
579                 if x.Mod.Path == path {
580                         hint = x.Syntax
581                 }
582         }
583
584         f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)})
585         return nil
586 }
587
588 func (f *File) DropExclude(path, vers string) error {
589         for _, x := range f.Exclude {
590                 if x.Mod.Path == path && x.Mod.Version == vers {
591                         f.Syntax.removeLine(x.Syntax)
592                         *x = Exclude{}
593                 }
594         }
595         return nil
596 }
597
598 func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
599         need := true
600         old := module.Version{Path: oldPath, Version: oldVers}
601         new := module.Version{Path: newPath, Version: newVers}
602         tokens := []string{"replace", AutoQuote(oldPath)}
603         if oldVers != "" {
604                 tokens = append(tokens, oldVers)
605         }
606         tokens = append(tokens, "=>", AutoQuote(newPath))
607         if newVers != "" {
608                 tokens = append(tokens, newVers)
609         }
610
611         var hint *Line
612         for _, r := range f.Replace {
613                 if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) {
614                         if need {
615                                 // Found replacement for old; update to use new.
616                                 r.New = new
617                                 f.Syntax.updateLine(r.Syntax, tokens...)
618                                 need = false
619                                 continue
620                         }
621                         // Already added; delete other replacements for same.
622                         f.Syntax.removeLine(r.Syntax)
623                         *r = Replace{}
624                 }
625                 if r.Old.Path == oldPath {
626                         hint = r.Syntax
627                 }
628         }
629         if need {
630                 f.Replace = append(f.Replace, &Replace{Old: old, New: new, Syntax: f.Syntax.addLine(hint, tokens...)})
631         }
632         return nil
633 }
634
635 func (f *File) DropReplace(oldPath, oldVers string) error {
636         for _, r := range f.Replace {
637                 if r.Old.Path == oldPath && r.Old.Version == oldVers {
638                         f.Syntax.removeLine(r.Syntax)
639                         *r = Replace{}
640                 }
641         }
642         return nil
643 }
644
645 func (f *File) SortBlocks() {
646         f.removeDups() // otherwise sorting is unsafe
647
648         for _, stmt := range f.Syntax.Stmt {
649                 block, ok := stmt.(*LineBlock)
650                 if !ok {
651                         continue
652                 }
653                 sort.Slice(block.Line, func(i, j int) bool {
654                         li := block.Line[i]
655                         lj := block.Line[j]
656                         for k := 0; k < len(li.Token) && k < len(lj.Token); k++ {
657                                 if li.Token[k] != lj.Token[k] {
658                                         return li.Token[k] < lj.Token[k]
659                                 }
660                         }
661                         return len(li.Token) < len(lj.Token)
662                 })
663         }
664 }
665
666 func (f *File) removeDups() {
667         have := make(map[module.Version]bool)
668         kill := make(map[*Line]bool)
669         for _, x := range f.Exclude {
670                 if have[x.Mod] {
671                         kill[x.Syntax] = true
672                         continue
673                 }
674                 have[x.Mod] = true
675         }
676         var excl []*Exclude
677         for _, x := range f.Exclude {
678                 if !kill[x.Syntax] {
679                         excl = append(excl, x)
680                 }
681         }
682         f.Exclude = excl
683
684         have = make(map[module.Version]bool)
685         // Later replacements take priority over earlier ones.
686         for i := len(f.Replace) - 1; i >= 0; i-- {
687                 x := f.Replace[i]
688                 if have[x.Old] {
689                         kill[x.Syntax] = true
690                         continue
691                 }
692                 have[x.Old] = true
693         }
694         var repl []*Replace
695         for _, x := range f.Replace {
696                 if !kill[x.Syntax] {
697                         repl = append(repl, x)
698                 }
699         }
700         f.Replace = repl
701
702         var stmts []Expr
703         for _, stmt := range f.Syntax.Stmt {
704                 switch stmt := stmt.(type) {
705                 case *Line:
706                         if kill[stmt] {
707                                 continue
708                         }
709                 case *LineBlock:
710                         var lines []*Line
711                         for _, line := range stmt.Line {
712                                 if !kill[line] {
713                                         lines = append(lines, line)
714                                 }
715                         }
716                         stmt.Line = lines
717                         if len(lines) == 0 {
718                                 continue
719                         }
720                 }
721                 stmts = append(stmts, stmt)
722         }
723         f.Syntax.Stmt = stmts
724 }