106 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			106 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| package checkcompilerdirectives
 | |
| 
 | |
| import (
 | |
| 	"strings"
 | |
| 
 | |
| 	"golang.org/x/tools/go/analysis"
 | |
| )
 | |
| 
 | |
| func Analyzer() *analysis.Analyzer {
 | |
| 	return &analysis.Analyzer{
 | |
| 		Name: "gocheckcompilerdirectives",
 | |
| 		Doc:  "Checks that go compiler directive comments (//go:) are valid.",
 | |
| 		Run:  run,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func run(pass *analysis.Pass) (interface{}, error) {
 | |
| 	for _, file := range pass.Files {
 | |
| 		for _, group := range file.Comments {
 | |
| 			for _, comment := range group.List {
 | |
| 				text := comment.Text
 | |
| 				if !strings.HasPrefix(text, "//") {
 | |
| 					continue
 | |
| 				}
 | |
| 				start := 2
 | |
| 				spaces := 0
 | |
| 				for _, c := range text[start:] {
 | |
| 					if c == ' ' {
 | |
| 						spaces++
 | |
| 						continue
 | |
| 					}
 | |
| 					break
 | |
| 				}
 | |
| 				start += spaces
 | |
| 				if !strings.HasPrefix(text[start:], "go:") {
 | |
| 					continue
 | |
| 				}
 | |
| 				start += 3
 | |
| 				end := strings.Index(text[start:], " ")
 | |
| 				if end == -1 {
 | |
| 					continue
 | |
| 				}
 | |
| 				directive := text[start : start+end]
 | |
| 				if len(directive) == 0 {
 | |
| 					continue
 | |
| 				}
 | |
| 				prefix := text[:start+end]
 | |
| 				// Leading whitespace will cause the go directive to be ignored
 | |
| 				// by the compiler with no error, causing it not to work. This
 | |
| 				// is an easy mistake.
 | |
| 				if spaces > 0 {
 | |
| 					pass.ReportRangef(comment, "compiler directive contains space: %s", prefix)
 | |
| 				}
 | |
| 				// If the directive is unknown it will be ignored by the
 | |
| 				// compiler with no error. This is an easy mistake to make,
 | |
| 				// especially if you typo a directive.
 | |
| 				if !isKnown(directive) {
 | |
| 					pass.ReportRangef(comment, "compiler directive unrecognized: %s", prefix)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil, nil
 | |
| }
 | |
| 
 | |
| func isKnown(directive string) bool {
 | |
| 	for _, k := range known {
 | |
| 		if directive == k {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| var known = []string{
 | |
| 	// Found by running the following command on the source of go.
 | |
| 	// git grep -o -E -h '//go:[a-z_]+' -- ':!**/*_test.go' ':!test/' ':!**/testdata/**' | sort -u
 | |
| 	"binary",
 | |
| 	"build",
 | |
| 	"buildsomethingelse",
 | |
| 	"cgo_dynamic_linker",
 | |
| 	"cgo_export_dynamic",
 | |
| 	"cgo_export_static",
 | |
| 	"cgo_import_dynamic",
 | |
| 	"cgo_import_static",
 | |
| 	"cgo_ldflag",
 | |
| 	"cgo_unsafe_args",
 | |
| 	"embed",
 | |
| 	"generate",
 | |
| 	"linkname",
 | |
| 	"name",
 | |
| 	"nocheckptr",
 | |
| 	"noescape",
 | |
| 	"noinline",
 | |
| 	"nointerface",
 | |
| 	"norace",
 | |
| 	"nosplit",
 | |
| 	"notinheap",
 | |
| 	"nowritebarrier",
 | |
| 	"nowritebarrierrec",
 | |
| 	"systemstack",
 | |
| 	"uintptrescapes",
 | |
| 	"uintptrkeepalive",
 | |
| 	"yeswritebarrierrec",
 | |
| }
 |