1
16
17
18 package main
19
20 import (
21 "bytes"
22 "flag"
23 "fmt"
24 "io/ioutil"
25 "os"
26 "sort"
27 "strings"
28
29 "github.com/bazelbuild/buildtools/warn"
30 "github.com/golang/protobuf/proto"
31
32 docspb "github.com/bazelbuild/buildtools/warn/docs/proto"
33 )
34
35 func readWarningsFromFile(path string) (*docspb.Warnings, error) {
36 content, err := ioutil.ReadFile(path)
37 if err != nil {
38 return nil, err
39 }
40 warnings := &docspb.Warnings{}
41 if err := proto.UnmarshalText(string(content), warnings); err != nil {
42 return nil, err
43 }
44 return warnings, nil
45 }
46
47 func isExistingWarning(name string) bool {
48 for _, n := range warn.AllWarnings {
49 if n == name {
50 return true
51 }
52 }
53 return false
54 }
55
56 func isDisabledWarning(name string) bool {
57 if !isExistingWarning(name) {
58 return false
59 }
60 for _, n := range warn.DefaultWarnings {
61 if n == name {
62 return false
63 }
64 }
65 return true
66 }
67
68 func generateWarningsDocs(warnings *docspb.Warnings) string {
69 var b bytes.Buffer
70
71 b.WriteString(`# Buildifier warnings
72
73 Warning categories supported by buildifier's linter:
74
75 `)
76
77
78 var names []string
79 for _, w := range warnings.Warnings {
80 names = append(names, w.Name...)
81 }
82 sort.Strings(names)
83 for _, n := range names {
84 fmt.Fprintf(&b, " * [`%s`](#%s)\n", n, n)
85 }
86
87
88 b.WriteString(`
89 ### <a name="suppress"></a>How to disable warnings
90
91 All warnings can be disabled / suppressed / ignored by adding a special comment ` + "`" + `# buildifier: disable=<category_name>` + "`" + ` to
92 the expression that causes the warning. Historically comments with ` + "`" + `buildozer` + "`" + ` instead of
93 ` + "`" + `buildifier` + "`" + ` are also supported, they are equivalent.
94
95 #### Examples
96
97 ` + "```" + `python
98 # buildifier: disable=no-effect
99 """
100 A multiline comment as a string literal.
101
102 Docstrings don't trigger the warning if they are first statements of a file or a function.
103 """
104
105 if debug:
106 print("Debug information:", foo) # buildifier: disable=print
107 ` + "```\n")
108
109
110 sort.Slice(warnings.Warnings, func(i, j int) bool {
111 return strings.Compare(warnings.Warnings[i].Name[0], warnings.Warnings[j].Name[0]) < 0
112 })
113 for _, w := range warnings.Warnings {
114
115 b.WriteString("\n--------------------------------------------------------------------------------\n\n## ")
116 for _, n := range w.Name {
117 fmt.Fprintf(&b, "<a name=%q></a>", n)
118 }
119 fmt.Fprintf(&b, "%s\n\n", w.Header)
120
121
122 if len(w.Name) == 1 {
123 fmt.Fprintf(&b, " * Category name: `%s`\n", w.Name[0])
124 } else {
125 b.WriteString(" * Category names:\n")
126 for _, n := range w.Name {
127 fmt.Fprintf(&b, " * `%s`\n", n)
128 }
129 }
130
131
132 if w.BazelFlag != "" {
133 label := fmt.Sprintf("`%s`", w.BazelFlag)
134 if w.BazelFlagLink != "" {
135 label = fmt.Sprintf("[%s](%s)", label, w.BazelFlagLink)
136 }
137 fmt.Fprintf(&b, " * Flag in Bazel: %s\n", label)
138 }
139
140
141 fix := "no"
142 if w.Autofix {
143 fix = "yes"
144 }
145 fmt.Fprintf(&b, " * Automatic fix: %s\n", fix)
146
147
148 if isDisabledWarning(w.Name[0]) {
149 b.WriteString(" * [Disabled by default](buildifier/README.md#linter)\n")
150 }
151
152
153 if !isExistingWarning(w.Name[0]) {
154 b.WriteString(" * Not supported by the latest version of Buildifier\n")
155 }
156
157
158 b.WriteString(" * [Suppress the warning](#suppress): ")
159 for i, n := range w.Name {
160 if i != 0 {
161 b.WriteString(", ")
162 }
163 fmt.Fprintf(&b, "`# buildifier: disable=%s`", n)
164 }
165 b.WriteString("\n")
166
167
168 fmt.Fprintf(&b, "\n%s\n", w.Description)
169 }
170 return b.String()
171 }
172
173 func writeWarningsDocs(docs, path string) error {
174 f, err := os.Create(path)
175 if err != nil {
176 return err
177 }
178 if _, err := f.WriteString(docs); err != nil {
179 return err
180 }
181 return f.Close()
182 }
183
184 func main() {
185 flag.Parse()
186 warnings, err := readWarningsFromFile(flag.Arg(0))
187 if err != nil {
188 fmt.Fprintln(os.Stderr, err)
189 os.Exit(1)
190 }
191 docs := generateWarningsDocs(warnings)
192 if err := writeWarningsDocs(docs, flag.Arg(1)); err != nil {
193 fmt.Fprintln(os.Stderr, err)
194 os.Exit(1)
195 }
196 }
197
View as plain text