github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1001/sa1001.go (about)

     1  package sa1001
     2  
     3  import (
     4  	"go/ast"
     5  	htmltemplate "html/template"
     6  	"strings"
     7  	texttemplate "text/template"
     8  
     9  	"github.com/amarpal/go-tools/analysis/code"
    10  	"github.com/amarpal/go-tools/analysis/lint"
    11  	"github.com/amarpal/go-tools/analysis/report"
    12  	"github.com/amarpal/go-tools/knowledge"
    13  
    14  	"golang.org/x/tools/go/analysis"
    15  	"golang.org/x/tools/go/analysis/passes/inspect"
    16  )
    17  
    18  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
    19  	Analyzer: &analysis.Analyzer{
    20  		Name:     "SA1001",
    21  		Run:      run,
    22  		Requires: []*analysis.Analyzer{inspect.Analyzer},
    23  	},
    24  	Doc: &lint.Documentation{
    25  		Title:    `Invalid template`,
    26  		Since:    "2017.1",
    27  		Severity: lint.SeverityError,
    28  		MergeIf:  lint.MergeIfAny,
    29  	},
    30  })
    31  
    32  var Analyzer = SCAnalyzer.Analyzer
    33  
    34  func run(pass *analysis.Pass) (interface{}, error) {
    35  	fn := func(node ast.Node) {
    36  		call := node.(*ast.CallExpr)
    37  		// OPT(dh): use integer for kind
    38  		var kind string
    39  		switch code.CallName(pass, call) {
    40  		case "(*text/template.Template).Parse":
    41  			kind = "text"
    42  		case "(*html/template.Template).Parse":
    43  			kind = "html"
    44  		default:
    45  			return
    46  		}
    47  		sel := call.Fun.(*ast.SelectorExpr)
    48  		if !code.IsCallToAny(pass, sel.X, "text/template.New", "html/template.New") {
    49  			// TODO(dh): this is a cheap workaround for templates with
    50  			// different delims. A better solution with less false
    51  			// negatives would use data flow analysis to see where the
    52  			// template comes from and where it has been
    53  			return
    54  		}
    55  		s, ok := code.ExprToString(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")])
    56  		if !ok {
    57  			return
    58  		}
    59  		var err error
    60  		switch kind {
    61  		case "text":
    62  			_, err = texttemplate.New("").Parse(s)
    63  		case "html":
    64  			_, err = htmltemplate.New("").Parse(s)
    65  		}
    66  		if err != nil {
    67  			// TODO(dominikh): whitelist other parse errors, if any
    68  			if strings.Contains(err.Error(), "unexpected") ||
    69  				strings.Contains(err.Error(), "bad character") {
    70  				report.Report(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")], err.Error())
    71  			}
    72  		}
    73  	}
    74  	code.Preorder(pass, fn, (*ast.CallExpr)(nil))
    75  	return nil, nil
    76  }