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