...

Source file src/go/printer/gobuild.go

Documentation: go/printer

		 1  // Copyright 2020 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 printer
		 6  
		 7  import (
		 8  	"go/build/constraint"
		 9  	"sort"
		10  	"text/tabwriter"
		11  )
		12  
		13  func (p *printer) fixGoBuildLines() {
		14  	if len(p.goBuild)+len(p.plusBuild) == 0 {
		15  		return
		16  	}
		17  
		18  	// Find latest possible placement of //go:build and // +build comments.
		19  	// That's just after the last blank line before we find a non-comment.
		20  	// (We'll add another blank line after our comment block.)
		21  	// When we start dropping // +build comments, we can skip over /* */ comments too.
		22  	// Note that we are processing tabwriter input, so every comment
		23  	// begins and ends with a tabwriter.Escape byte.
		24  	// And some newlines have turned into \f bytes.
		25  	insert := 0
		26  	for pos := 0; ; {
		27  		// Skip leading space at beginning of line.
		28  		blank := true
		29  		for pos < len(p.output) && (p.output[pos] == ' ' || p.output[pos] == '\t') {
		30  			pos++
		31  		}
		32  		// Skip over // comment if any.
		33  		if pos+3 < len(p.output) && p.output[pos] == tabwriter.Escape && p.output[pos+1] == '/' && p.output[pos+2] == '/' {
		34  			blank = false
		35  			for pos < len(p.output) && !isNL(p.output[pos]) {
		36  				pos++
		37  			}
		38  		}
		39  		// Skip over \n at end of line.
		40  		if pos >= len(p.output) || !isNL(p.output[pos]) {
		41  			break
		42  		}
		43  		pos++
		44  
		45  		if blank {
		46  			insert = pos
		47  		}
		48  	}
		49  
		50  	// If there is a //go:build comment before the place we identified,
		51  	// use that point instead. (Earlier in the file is always fine.)
		52  	if len(p.goBuild) > 0 && p.goBuild[0] < insert {
		53  		insert = p.goBuild[0]
		54  	} else if len(p.plusBuild) > 0 && p.plusBuild[0] < insert {
		55  		insert = p.plusBuild[0]
		56  	}
		57  
		58  	var x constraint.Expr
		59  	switch len(p.goBuild) {
		60  	case 0:
		61  		// Synthesize //go:build expression from // +build lines.
		62  		for _, pos := range p.plusBuild {
		63  			y, err := constraint.Parse(p.commentTextAt(pos))
		64  			if err != nil {
		65  				x = nil
		66  				break
		67  			}
		68  			if x == nil {
		69  				x = y
		70  			} else {
		71  				x = &constraint.AndExpr{X: x, Y: y}
		72  			}
		73  		}
		74  	case 1:
		75  		// Parse //go:build expression.
		76  		x, _ = constraint.Parse(p.commentTextAt(p.goBuild[0]))
		77  	}
		78  
		79  	var block []byte
		80  	if x == nil {
		81  		// Don't have a valid //go:build expression to treat as truth.
		82  		// Bring all the lines together but leave them alone.
		83  		// Note that these are already tabwriter-escaped.
		84  		for _, pos := range p.goBuild {
		85  			block = append(block, p.lineAt(pos)...)
		86  		}
		87  		for _, pos := range p.plusBuild {
		88  			block = append(block, p.lineAt(pos)...)
		89  		}
		90  	} else {
		91  		block = append(block, tabwriter.Escape)
		92  		block = append(block, "//go:build "...)
		93  		block = append(block, x.String()...)
		94  		block = append(block, tabwriter.Escape, '\n')
		95  		if len(p.plusBuild) > 0 {
		96  			lines, err := constraint.PlusBuildLines(x)
		97  			if err != nil {
		98  				lines = []string{"// +build error: " + err.Error()}
		99  			}
	 100  			for _, line := range lines {
	 101  				block = append(block, tabwriter.Escape)
	 102  				block = append(block, line...)
	 103  				block = append(block, tabwriter.Escape, '\n')
	 104  			}
	 105  		}
	 106  	}
	 107  	block = append(block, '\n')
	 108  
	 109  	// Build sorted list of lines to delete from remainder of output.
	 110  	toDelete := append(p.goBuild, p.plusBuild...)
	 111  	sort.Ints(toDelete)
	 112  
	 113  	// Collect output after insertion point, with lines deleted, into after.
	 114  	var after []byte
	 115  	start := insert
	 116  	for _, end := range toDelete {
	 117  		if end < start {
	 118  			continue
	 119  		}
	 120  		after = appendLines(after, p.output[start:end])
	 121  		start = end + len(p.lineAt(end))
	 122  	}
	 123  	after = appendLines(after, p.output[start:])
	 124  	if n := len(after); n >= 2 && isNL(after[n-1]) && isNL(after[n-2]) {
	 125  		after = after[:n-1]
	 126  	}
	 127  
	 128  	p.output = p.output[:insert]
	 129  	p.output = append(p.output, block...)
	 130  	p.output = append(p.output, after...)
	 131  }
	 132  
	 133  // appendLines is like append(x, y...)
	 134  // but it avoids creating doubled blank lines,
	 135  // which would not be gofmt-standard output.
	 136  // It assumes that only whole blocks of lines are being appended,
	 137  // not line fragments.
	 138  func appendLines(x, y []byte) []byte {
	 139  	if len(y) > 0 && isNL(y[0]) && // y starts in blank line
	 140  		(len(x) == 0 || len(x) >= 2 && isNL(x[len(x)-1]) && isNL(x[len(x)-2])) { // x is empty or ends in blank line
	 141  		y = y[1:] // delete y's leading blank line
	 142  	}
	 143  	return append(x, y...)
	 144  }
	 145  
	 146  func (p *printer) lineAt(start int) []byte {
	 147  	pos := start
	 148  	for pos < len(p.output) && !isNL(p.output[pos]) {
	 149  		pos++
	 150  	}
	 151  	if pos < len(p.output) {
	 152  		pos++
	 153  	}
	 154  	return p.output[start:pos]
	 155  }
	 156  
	 157  func (p *printer) commentTextAt(start int) string {
	 158  	if start < len(p.output) && p.output[start] == tabwriter.Escape {
	 159  		start++
	 160  	}
	 161  	pos := start
	 162  	for pos < len(p.output) && p.output[pos] != tabwriter.Escape && !isNL(p.output[pos]) {
	 163  		pos++
	 164  	}
	 165  	return string(p.output[start:pos])
	 166  }
	 167  
	 168  func isNL(b byte) bool {
	 169  	return b == '\n' || b == '\f'
	 170  }
	 171  

View as plain text