sa1001.go raw

   1  package sa1001
   2  
   3  import (
   4  	"go/ast"
   5  	htmltemplate "html/template"
   6  	"strings"
   7  	texttemplate "text/template"
   8  
   9  	"honnef.co/go/tools/analysis/code"
  10  	"honnef.co/go/tools/analysis/lint"
  11  	"honnef.co/go/tools/analysis/report"
  12  	"honnef.co/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.RawDocumentation{
  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  }
  77