xref: /aosp_15_r20/external/boringssl/src/util/convert_comments.go (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1*8fb009dcSAndroid Build Coastguard Worker// Copyright (c) 2017, Google Inc.
2*8fb009dcSAndroid Build Coastguard Worker//
3*8fb009dcSAndroid Build Coastguard Worker// Permission to use, copy, modify, and/or distribute this software for any
4*8fb009dcSAndroid Build Coastguard Worker// purpose with or without fee is hereby granted, provided that the above
5*8fb009dcSAndroid Build Coastguard Worker// copyright notice and this permission notice appear in all copies.
6*8fb009dcSAndroid Build Coastguard Worker//
7*8fb009dcSAndroid Build Coastguard Worker// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8*8fb009dcSAndroid Build Coastguard Worker// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9*8fb009dcSAndroid Build Coastguard Worker// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10*8fb009dcSAndroid Build Coastguard Worker// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11*8fb009dcSAndroid Build Coastguard Worker// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12*8fb009dcSAndroid Build Coastguard Worker// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13*8fb009dcSAndroid Build Coastguard Worker// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14*8fb009dcSAndroid Build Coastguard Worker
15*8fb009dcSAndroid Build Coastguard Worker//go:build ignore
16*8fb009dcSAndroid Build Coastguard Worker
17*8fb009dcSAndroid Build Coastguard Workerpackage main
18*8fb009dcSAndroid Build Coastguard Worker
19*8fb009dcSAndroid Build Coastguard Workerimport (
20*8fb009dcSAndroid Build Coastguard Worker	"bytes"
21*8fb009dcSAndroid Build Coastguard Worker	"fmt"
22*8fb009dcSAndroid Build Coastguard Worker	"os"
23*8fb009dcSAndroid Build Coastguard Worker	"strings"
24*8fb009dcSAndroid Build Coastguard Worker)
25*8fb009dcSAndroid Build Coastguard Worker
26*8fb009dcSAndroid Build Coastguard Worker// convert_comments.go converts C-style block comments to C++-style line
27*8fb009dcSAndroid Build Coastguard Worker// comments. A block comment is converted if all of the following are true:
28*8fb009dcSAndroid Build Coastguard Worker//
29*8fb009dcSAndroid Build Coastguard Worker//   * The comment begins after the first blank line, to leave the license
30*8fb009dcSAndroid Build Coastguard Worker//     blocks alone.
31*8fb009dcSAndroid Build Coastguard Worker//
32*8fb009dcSAndroid Build Coastguard Worker//   * There are no characters between the '*/' and the end of the line.
33*8fb009dcSAndroid Build Coastguard Worker//
34*8fb009dcSAndroid Build Coastguard Worker//   * Either one of the following are true:
35*8fb009dcSAndroid Build Coastguard Worker//
36*8fb009dcSAndroid Build Coastguard Worker//     - The comment fits on one line.
37*8fb009dcSAndroid Build Coastguard Worker//
38*8fb009dcSAndroid Build Coastguard Worker//     - Each line the comment spans begins with N spaces, followed by '/*' for
39*8fb009dcSAndroid Build Coastguard Worker//       the initial line or ' *' for subsequent lines, where N is the same for
40*8fb009dcSAndroid Build Coastguard Worker//       each line.
41*8fb009dcSAndroid Build Coastguard Worker//
42*8fb009dcSAndroid Build Coastguard Worker// This tool is a heuristic. While it gets almost all cases correct, the final
43*8fb009dcSAndroid Build Coastguard Worker// output should still be looked over and fixed up as needed.
44*8fb009dcSAndroid Build Coastguard Worker
45*8fb009dcSAndroid Build Coastguard Worker// allSpaces returns true if |s| consists entirely of spaces.
46*8fb009dcSAndroid Build Coastguard Workerfunc allSpaces(s string) bool {
47*8fb009dcSAndroid Build Coastguard Worker	return strings.IndexFunc(s, func(r rune) bool { return r != ' ' }) == -1
48*8fb009dcSAndroid Build Coastguard Worker}
49*8fb009dcSAndroid Build Coastguard Worker
50*8fb009dcSAndroid Build Coastguard Worker// isContinuation returns true if |s| is a continuation line for a multi-line
51*8fb009dcSAndroid Build Coastguard Worker// comment indented to the specified column.
52*8fb009dcSAndroid Build Coastguard Workerfunc isContinuation(s string, column int) bool {
53*8fb009dcSAndroid Build Coastguard Worker	if len(s) < column+2 {
54*8fb009dcSAndroid Build Coastguard Worker		return false
55*8fb009dcSAndroid Build Coastguard Worker	}
56*8fb009dcSAndroid Build Coastguard Worker	if !allSpaces(s[:column]) {
57*8fb009dcSAndroid Build Coastguard Worker		return false
58*8fb009dcSAndroid Build Coastguard Worker	}
59*8fb009dcSAndroid Build Coastguard Worker	return s[column:column+2] == " *"
60*8fb009dcSAndroid Build Coastguard Worker}
61*8fb009dcSAndroid Build Coastguard Worker
62*8fb009dcSAndroid Build Coastguard Worker// indexFrom behaves like strings.Index but only reports matches starting at
63*8fb009dcSAndroid Build Coastguard Worker// |idx|.
64*8fb009dcSAndroid Build Coastguard Workerfunc indexFrom(s, sep string, idx int) int {
65*8fb009dcSAndroid Build Coastguard Worker	ret := strings.Index(s[idx:], sep)
66*8fb009dcSAndroid Build Coastguard Worker	if ret < 0 {
67*8fb009dcSAndroid Build Coastguard Worker		return -1
68*8fb009dcSAndroid Build Coastguard Worker	}
69*8fb009dcSAndroid Build Coastguard Worker	return idx + ret
70*8fb009dcSAndroid Build Coastguard Worker}
71*8fb009dcSAndroid Build Coastguard Worker
72*8fb009dcSAndroid Build Coastguard Worker// A lineGroup is a contiguous group of lines with an eligible comment at the
73*8fb009dcSAndroid Build Coastguard Worker// same column. Any trailing '*/'s will already be removed.
74*8fb009dcSAndroid Build Coastguard Workertype lineGroup struct {
75*8fb009dcSAndroid Build Coastguard Worker	// column is the column where the eligible comment begins. line[column]
76*8fb009dcSAndroid Build Coastguard Worker	// and line[column+1] will both be replaced with '/'. It is -1 if this
77*8fb009dcSAndroid Build Coastguard Worker	// group is not to be converted.
78*8fb009dcSAndroid Build Coastguard Worker	column int
79*8fb009dcSAndroid Build Coastguard Worker	lines  []string
80*8fb009dcSAndroid Build Coastguard Worker}
81*8fb009dcSAndroid Build Coastguard Worker
82*8fb009dcSAndroid Build Coastguard Workerfunc addLine(groups *[]lineGroup, line string, column int) {
83*8fb009dcSAndroid Build Coastguard Worker	if len(*groups) == 0 || (*groups)[len(*groups)-1].column != column {
84*8fb009dcSAndroid Build Coastguard Worker		*groups = append(*groups, lineGroup{column, nil})
85*8fb009dcSAndroid Build Coastguard Worker	}
86*8fb009dcSAndroid Build Coastguard Worker	(*groups)[len(*groups)-1].lines = append((*groups)[len(*groups)-1].lines, line)
87*8fb009dcSAndroid Build Coastguard Worker}
88*8fb009dcSAndroid Build Coastguard Worker
89*8fb009dcSAndroid Build Coastguard Worker// writeLine writes |line| to |out|, followed by a newline.
90*8fb009dcSAndroid Build Coastguard Workerfunc writeLine(out *bytes.Buffer, line string) {
91*8fb009dcSAndroid Build Coastguard Worker	out.WriteString(line)
92*8fb009dcSAndroid Build Coastguard Worker	out.WriteByte('\n')
93*8fb009dcSAndroid Build Coastguard Worker}
94*8fb009dcSAndroid Build Coastguard Worker
95*8fb009dcSAndroid Build Coastguard Workerfunc convertComments(path string, in []byte) []byte {
96*8fb009dcSAndroid Build Coastguard Worker	lines := strings.Split(string(in), "\n")
97*8fb009dcSAndroid Build Coastguard Worker
98*8fb009dcSAndroid Build Coastguard Worker	// Account for the trailing newline.
99*8fb009dcSAndroid Build Coastguard Worker	if len(lines) > 0 && len(lines[len(lines)-1]) == 0 {
100*8fb009dcSAndroid Build Coastguard Worker		lines = lines[:len(lines)-1]
101*8fb009dcSAndroid Build Coastguard Worker	}
102*8fb009dcSAndroid Build Coastguard Worker
103*8fb009dcSAndroid Build Coastguard Worker	// First pass: identify all comments to be converted. Group them into
104*8fb009dcSAndroid Build Coastguard Worker	// lineGroups with the same column.
105*8fb009dcSAndroid Build Coastguard Worker	var groups []lineGroup
106*8fb009dcSAndroid Build Coastguard Worker
107*8fb009dcSAndroid Build Coastguard Worker	// Find the license block separator.
108*8fb009dcSAndroid Build Coastguard Worker	for len(lines) > 0 {
109*8fb009dcSAndroid Build Coastguard Worker		line := lines[0]
110*8fb009dcSAndroid Build Coastguard Worker		lines = lines[1:]
111*8fb009dcSAndroid Build Coastguard Worker		addLine(&groups, line, -1)
112*8fb009dcSAndroid Build Coastguard Worker		if len(line) == 0 {
113*8fb009dcSAndroid Build Coastguard Worker			break
114*8fb009dcSAndroid Build Coastguard Worker		}
115*8fb009dcSAndroid Build Coastguard Worker	}
116*8fb009dcSAndroid Build Coastguard Worker
117*8fb009dcSAndroid Build Coastguard Worker	// inComment is true if we are in the middle of a comment.
118*8fb009dcSAndroid Build Coastguard Worker	var inComment bool
119*8fb009dcSAndroid Build Coastguard Worker	// comment is the currently buffered multi-line comment to convert. If
120*8fb009dcSAndroid Build Coastguard Worker	// |inComment| is true and it is nil, the current multi-line comment is
121*8fb009dcSAndroid Build Coastguard Worker	// not convertable and we copy lines to |out| as-is.
122*8fb009dcSAndroid Build Coastguard Worker	var comment []string
123*8fb009dcSAndroid Build Coastguard Worker	// column is the column offset of |comment|.
124*8fb009dcSAndroid Build Coastguard Worker	var column int
125*8fb009dcSAndroid Build Coastguard Worker	for len(lines) > 0 {
126*8fb009dcSAndroid Build Coastguard Worker		line := lines[0]
127*8fb009dcSAndroid Build Coastguard Worker		lines = lines[1:]
128*8fb009dcSAndroid Build Coastguard Worker
129*8fb009dcSAndroid Build Coastguard Worker		var idx int
130*8fb009dcSAndroid Build Coastguard Worker		if inComment {
131*8fb009dcSAndroid Build Coastguard Worker			// Stop buffering if this comment isn't eligible.
132*8fb009dcSAndroid Build Coastguard Worker			if comment != nil && !isContinuation(line, column) {
133*8fb009dcSAndroid Build Coastguard Worker				for _, l := range comment {
134*8fb009dcSAndroid Build Coastguard Worker					addLine(&groups, l, -1)
135*8fb009dcSAndroid Build Coastguard Worker				}
136*8fb009dcSAndroid Build Coastguard Worker				comment = nil
137*8fb009dcSAndroid Build Coastguard Worker			}
138*8fb009dcSAndroid Build Coastguard Worker
139*8fb009dcSAndroid Build Coastguard Worker			// Look for the end of the current comment.
140*8fb009dcSAndroid Build Coastguard Worker			idx = strings.Index(line, "*/")
141*8fb009dcSAndroid Build Coastguard Worker			if idx < 0 {
142*8fb009dcSAndroid Build Coastguard Worker				if comment != nil {
143*8fb009dcSAndroid Build Coastguard Worker					comment = append(comment, line)
144*8fb009dcSAndroid Build Coastguard Worker				} else {
145*8fb009dcSAndroid Build Coastguard Worker					addLine(&groups, line, -1)
146*8fb009dcSAndroid Build Coastguard Worker				}
147*8fb009dcSAndroid Build Coastguard Worker				continue
148*8fb009dcSAndroid Build Coastguard Worker			}
149*8fb009dcSAndroid Build Coastguard Worker
150*8fb009dcSAndroid Build Coastguard Worker			inComment = false
151*8fb009dcSAndroid Build Coastguard Worker			if comment != nil {
152*8fb009dcSAndroid Build Coastguard Worker				if idx == len(line)-2 {
153*8fb009dcSAndroid Build Coastguard Worker					// This is a convertable multi-line comment.
154*8fb009dcSAndroid Build Coastguard Worker					if idx >= column+2 {
155*8fb009dcSAndroid Build Coastguard Worker						// |idx| may be equal to
156*8fb009dcSAndroid Build Coastguard Worker						// |column| + 1, if the line is
157*8fb009dcSAndroid Build Coastguard Worker						// a '*/' on its own. In that
158*8fb009dcSAndroid Build Coastguard Worker						// case, we discard the line.
159*8fb009dcSAndroid Build Coastguard Worker						comment = append(comment, line[:idx])
160*8fb009dcSAndroid Build Coastguard Worker					}
161*8fb009dcSAndroid Build Coastguard Worker					for _, l := range comment {
162*8fb009dcSAndroid Build Coastguard Worker						addLine(&groups, l, column)
163*8fb009dcSAndroid Build Coastguard Worker					}
164*8fb009dcSAndroid Build Coastguard Worker					comment = nil
165*8fb009dcSAndroid Build Coastguard Worker					continue
166*8fb009dcSAndroid Build Coastguard Worker				}
167*8fb009dcSAndroid Build Coastguard Worker
168*8fb009dcSAndroid Build Coastguard Worker				// Flush the buffered comment unmodified.
169*8fb009dcSAndroid Build Coastguard Worker				for _, l := range comment {
170*8fb009dcSAndroid Build Coastguard Worker					addLine(&groups, l, -1)
171*8fb009dcSAndroid Build Coastguard Worker				}
172*8fb009dcSAndroid Build Coastguard Worker				comment = nil
173*8fb009dcSAndroid Build Coastguard Worker			}
174*8fb009dcSAndroid Build Coastguard Worker			idx += 2
175*8fb009dcSAndroid Build Coastguard Worker		}
176*8fb009dcSAndroid Build Coastguard Worker
177*8fb009dcSAndroid Build Coastguard Worker		// Parse starting from |idx|, looking for either a convertable
178*8fb009dcSAndroid Build Coastguard Worker		// line comment or a multi-line comment.
179*8fb009dcSAndroid Build Coastguard Worker		for {
180*8fb009dcSAndroid Build Coastguard Worker			idx = indexFrom(line, "/*", idx)
181*8fb009dcSAndroid Build Coastguard Worker			if idx < 0 {
182*8fb009dcSAndroid Build Coastguard Worker				addLine(&groups, line, -1)
183*8fb009dcSAndroid Build Coastguard Worker				break
184*8fb009dcSAndroid Build Coastguard Worker			}
185*8fb009dcSAndroid Build Coastguard Worker
186*8fb009dcSAndroid Build Coastguard Worker			endIdx := indexFrom(line, "*/", idx)
187*8fb009dcSAndroid Build Coastguard Worker			if endIdx < 0 {
188*8fb009dcSAndroid Build Coastguard Worker				// The comment is, so far, eligible for conversion.
189*8fb009dcSAndroid Build Coastguard Worker				inComment = true
190*8fb009dcSAndroid Build Coastguard Worker				column = idx
191*8fb009dcSAndroid Build Coastguard Worker				comment = []string{line}
192*8fb009dcSAndroid Build Coastguard Worker				break
193*8fb009dcSAndroid Build Coastguard Worker			}
194*8fb009dcSAndroid Build Coastguard Worker
195*8fb009dcSAndroid Build Coastguard Worker			if endIdx != len(line)-2 {
196*8fb009dcSAndroid Build Coastguard Worker				// Continue parsing for more comments in this line.
197*8fb009dcSAndroid Build Coastguard Worker				idx = endIdx + 2
198*8fb009dcSAndroid Build Coastguard Worker				continue
199*8fb009dcSAndroid Build Coastguard Worker			}
200*8fb009dcSAndroid Build Coastguard Worker
201*8fb009dcSAndroid Build Coastguard Worker			addLine(&groups, line[:endIdx], idx)
202*8fb009dcSAndroid Build Coastguard Worker			break
203*8fb009dcSAndroid Build Coastguard Worker		}
204*8fb009dcSAndroid Build Coastguard Worker	}
205*8fb009dcSAndroid Build Coastguard Worker
206*8fb009dcSAndroid Build Coastguard Worker	// Second pass: convert the lineGroups, adjusting spacing as needed.
207*8fb009dcSAndroid Build Coastguard Worker	var out bytes.Buffer
208*8fb009dcSAndroid Build Coastguard Worker	var lineNo int
209*8fb009dcSAndroid Build Coastguard Worker	for _, group := range groups {
210*8fb009dcSAndroid Build Coastguard Worker		if group.column < 0 {
211*8fb009dcSAndroid Build Coastguard Worker			for _, line := range group.lines {
212*8fb009dcSAndroid Build Coastguard Worker				writeLine(&out, line)
213*8fb009dcSAndroid Build Coastguard Worker			}
214*8fb009dcSAndroid Build Coastguard Worker		} else {
215*8fb009dcSAndroid Build Coastguard Worker			// Google C++ style prefers two spaces before a comment
216*8fb009dcSAndroid Build Coastguard Worker			// if it is on the same line as code, but clang-format
217*8fb009dcSAndroid Build Coastguard Worker			// has been placing one space for block comments. All
218*8fb009dcSAndroid Build Coastguard Worker			// comments within a group should be adjusted by the
219*8fb009dcSAndroid Build Coastguard Worker			// same amount.
220*8fb009dcSAndroid Build Coastguard Worker			var adjust string
221*8fb009dcSAndroid Build Coastguard Worker			for _, line := range group.lines {
222*8fb009dcSAndroid Build Coastguard Worker				if !allSpaces(line[:group.column]) && line[group.column-1] != '(' {
223*8fb009dcSAndroid Build Coastguard Worker					if line[group.column-1] != ' ' {
224*8fb009dcSAndroid Build Coastguard Worker						if len(adjust) < 2 {
225*8fb009dcSAndroid Build Coastguard Worker							adjust = "  "
226*8fb009dcSAndroid Build Coastguard Worker						}
227*8fb009dcSAndroid Build Coastguard Worker					} else if line[group.column-2] != ' ' {
228*8fb009dcSAndroid Build Coastguard Worker						if len(adjust) < 1 {
229*8fb009dcSAndroid Build Coastguard Worker							adjust = " "
230*8fb009dcSAndroid Build Coastguard Worker						}
231*8fb009dcSAndroid Build Coastguard Worker					}
232*8fb009dcSAndroid Build Coastguard Worker				}
233*8fb009dcSAndroid Build Coastguard Worker			}
234*8fb009dcSAndroid Build Coastguard Worker
235*8fb009dcSAndroid Build Coastguard Worker			for i, line := range group.lines {
236*8fb009dcSAndroid Build Coastguard Worker				// The OpenSSL style writes multiline block comments with a
237*8fb009dcSAndroid Build Coastguard Worker				// blank line at the top and bottom, like so:
238*8fb009dcSAndroid Build Coastguard Worker				//
239*8fb009dcSAndroid Build Coastguard Worker				//   /*
240*8fb009dcSAndroid Build Coastguard Worker				//    * Some multi-line
241*8fb009dcSAndroid Build Coastguard Worker				//    * comment
242*8fb009dcSAndroid Build Coastguard Worker				//    */
243*8fb009dcSAndroid Build Coastguard Worker				//
244*8fb009dcSAndroid Build Coastguard Worker				// The trailing lines are already removed above, when buffering.
245*8fb009dcSAndroid Build Coastguard Worker				// Remove the leading lines here. (The leading lines cannot be
246*8fb009dcSAndroid Build Coastguard Worker				// removed when buffering because we may discover the comment is
247*8fb009dcSAndroid Build Coastguard Worker				// not convertible in later lines.)
248*8fb009dcSAndroid Build Coastguard Worker				//
249*8fb009dcSAndroid Build Coastguard Worker				// Note the leading line cannot be easily removed if there is
250*8fb009dcSAndroid Build Coastguard Worker				// code before it, such as the following. Skip those cases.
251*8fb009dcSAndroid Build Coastguard Worker				//
252*8fb009dcSAndroid Build Coastguard Worker				//   foo(); /*
253*8fb009dcSAndroid Build Coastguard Worker				//           * Some multi-line
254*8fb009dcSAndroid Build Coastguard Worker				//           * comment
255*8fb009dcSAndroid Build Coastguard Worker				//           */
256*8fb009dcSAndroid Build Coastguard Worker				if i == 0 && allSpaces(line[:group.column]) && len(line) == group.column+2 {
257*8fb009dcSAndroid Build Coastguard Worker					continue
258*8fb009dcSAndroid Build Coastguard Worker				}
259*8fb009dcSAndroid Build Coastguard Worker				newLine := fmt.Sprintf("%s%s//%s", line[:group.column], adjust, strings.TrimRight(line[group.column+2:], " "))
260*8fb009dcSAndroid Build Coastguard Worker				if len(newLine) > 80 {
261*8fb009dcSAndroid Build Coastguard Worker					fmt.Fprintf(os.Stderr, "%s:%d: Line is now longer than 80 characters\n", path, lineNo+i+1)
262*8fb009dcSAndroid Build Coastguard Worker				}
263*8fb009dcSAndroid Build Coastguard Worker				writeLine(&out, newLine)
264*8fb009dcSAndroid Build Coastguard Worker			}
265*8fb009dcSAndroid Build Coastguard Worker
266*8fb009dcSAndroid Build Coastguard Worker		}
267*8fb009dcSAndroid Build Coastguard Worker		lineNo += len(group.lines)
268*8fb009dcSAndroid Build Coastguard Worker	}
269*8fb009dcSAndroid Build Coastguard Worker	return out.Bytes()
270*8fb009dcSAndroid Build Coastguard Worker}
271*8fb009dcSAndroid Build Coastguard Worker
272*8fb009dcSAndroid Build Coastguard Workerfunc main() {
273*8fb009dcSAndroid Build Coastguard Worker	for _, arg := range os.Args[1:] {
274*8fb009dcSAndroid Build Coastguard Worker		in, err := os.ReadFile(arg)
275*8fb009dcSAndroid Build Coastguard Worker		if err != nil {
276*8fb009dcSAndroid Build Coastguard Worker			panic(err)
277*8fb009dcSAndroid Build Coastguard Worker		}
278*8fb009dcSAndroid Build Coastguard Worker		if err := os.WriteFile(arg, convertComments(arg, in), 0666); err != nil {
279*8fb009dcSAndroid Build Coastguard Worker			panic(err)
280*8fb009dcSAndroid Build Coastguard Worker		}
281*8fb009dcSAndroid Build Coastguard Worker	}
282*8fb009dcSAndroid Build Coastguard Worker}
283