in tool/preprocess/match.go [227:370]
func (rm *ruleMatcher) match(cmdArgs []string) *resource.RuleBundle {
importPath := findFlagValue(cmdArgs, util.BuildPattern)
util.Assert(importPath != "", "sanity check")
if config.GetConf().Verbose {
util.Log("RunMatch: %v (%v)", importPath, cmdArgs)
}
availables := make([]resource.InstRule, len(rm.availableRules[importPath]))
// Okay, we are interested in these candidates, let's read it and match with
// the instrumentation rule, but first we need to check if the package name
// are already registered, to avoid futile effort
copy(availables, rm.availableRules[importPath])
if len(availables) == 0 {
return nil // fast fail
}
parsedAst := make(map[string]*dst.File)
bundle := resource.NewRuleBundle(importPath)
goVersion := findFlagValue(cmdArgs, util.BuildGoVer)
util.Assert(goVersion != "", "sanity check")
util.Assert(strings.HasPrefix(goVersion, "go"), "sanity check")
goVersion = strings.Replace(goVersion, "go", "v", 1)
for _, candidate := range cmdArgs {
// It's not a go file, ignore silently
if !util.IsGoFile(candidate) {
continue
}
file := candidate
// If it's a vendor build, we need to extract the version of the module
// from vendor/modules.txt, otherwise we find the version from source
// code file path
version := extractVersion(file)
if rm.moduleVersions != nil {
recorded := findVendorModuleVersion(rm.moduleVersions, importPath)
if recorded != "" {
version = recorded
}
}
for i := len(availables) - 1; i >= 0; i-- {
rule := availables[i]
// Check if the version is supported
matched, err := matchVersion(version, rule.GetVersion())
if err != nil {
util.Log("Bad match: file %s, rule %s, version %s",
file, rule, version)
continue
}
if !matched {
continue
}
// Check if the rule requires a specific Go version(range)
if rule.GetGoVersion() != "" {
matched, err = matchVersion(goVersion, rule.GetGoVersion())
if err != nil {
util.Log("Bad match: file %s, rule %s, go version %s",
file, rule, goVersion)
continue
}
if !matched {
continue
}
}
// Check if it matches with file rule early as we try to avoid
// parsing the file content, which is time consuming
if _, ok := rule.(*resource.InstFileRule); ok {
ast, err := util.ParseAstFromFileOnlyPackage(file)
if ast == nil || err != nil {
util.Log("Failed to parse %s: %v", file, err)
continue
}
util.Log("Match file rule %s", rule)
bundle.AddFileRule(rule.(*resource.InstFileRule))
bundle.SetPackageName(ast.Name.Name)
availables = append(availables[:i], availables[i+1:]...)
continue
}
// Fair enough, parse the file content
var tree *dst.File
if _, ok := parsedAst[file]; !ok {
fileAst, err := util.ParseAstFromFileFast(file)
if fileAst == nil || err != nil {
util.Log("failed to parse file %s: %v", file, err)
continue
}
parsedAst[file] = fileAst
util.Assert(fileAst.Name.Name != "", "empty package name")
bundle.SetPackageName(fileAst.Name.Name)
tree = fileAst
} else {
tree = parsedAst[file]
}
if tree == nil {
// Failed to parse the file, stop here and log only
// sicne it's a tolerant failure
util.Log("Failed to parse file %s", file)
continue
}
// Let's match with the rule precisely
valid := false
for _, decl := range tree.Decls {
if genDecl, ok := decl.(*dst.GenDecl); ok {
if rl, ok := rule.(*resource.InstStructRule); ok {
if util.MatchStructDecl(genDecl, rl.StructType) {
util.Log("Match struct rule %s with %v",
rule, cmdArgs)
err = bundle.AddFile2StructRule(file, rl)
if err != nil {
util.Log("Failed to add struct rule: %v", err)
continue
}
valid = true
break
}
}
} else if funcDecl, ok := decl.(*dst.FuncDecl); ok {
if rl, ok := rule.(*resource.InstFuncRule); ok {
if util.MatchFuncDecl(funcDecl, rl.Function, rl.ReceiverType) {
util.Log("Match func rule %s with %v", rule, cmdArgs)
err = bundle.AddFile2FuncRule(file, rl)
if err != nil {
util.Log("Failed to add func rule: %v", err)
continue
}
valid = true
break
}
}
}
}
if valid {
// Remove the rule from the available rules
availables = append(availables[:i], availables[i+1:]...)
}
}
}
return bundle
}