...

Source file src/github.com/bazelbuild/buildtools/warn/docs/docs.go

Documentation: github.com/bazelbuild/buildtools/warn/docs

     1  /*
     2  Copyright 2021 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Documentation generator
    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  	// Table of contents
    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  	// Misc
    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  	// Individual warnings
   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  		// Header
   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  		// Name(s)
   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  		// Bazel --incompatible flag
   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  		// Automatic fix
   141  		fix := "no"
   142  		if w.Autofix {
   143  			fix = "yes"
   144  		}
   145  		fmt.Fprintf(&b, "  * Automatic fix: %s\n", fix)
   146  
   147  		// Disabled by default
   148  		if isDisabledWarning(w.Name[0]) {
   149  			b.WriteString("  * [Disabled by default](buildifier/README.md#linter)\n")
   150  		}
   151  
   152  		// Non-existent
   153  		if !isExistingWarning(w.Name[0]) {
   154  			b.WriteString("  * Not supported by the latest version of Buildifier\n")
   155  		}
   156  
   157  		// Suppress the warning
   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  		// Description
   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