Repository: google/go-flow-levee
Pull request #71 (2020-08-27 19:18:52)

Use fieldtags analyzer in sourcetype analyzer
This PR is a successor to #70.

This PR allows the `sourcetype` analyzer to identify source fields via tags by using the results of the `fieldtags` analyzer.

- [x] Tests pass
- [x] Appropriate changes to README are included in PR

mlevesquedion: I've added a test for multi-named fields.

Patch:
diff --git a/internal/pkg/sourcetype/analyzer.go b/internal/pkg/sourcetype/analyzer.go
index afb9fb7..9ec612f 100644
--- a/internal/pkg/sourcetype/analyzer.go
+++ b/internal/pkg/sourcetype/analyzer.go
@@ -24,6 +24,7 @@ import (
 	"sort"
 
 	"github.com/google/go-flow-levee/internal/pkg/config"
+	"github.com/google/go-flow-levee/internal/pkg/fieldtags"
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/buildssa"
 	"golang.org/x/tools/go/ssa"
@@ -78,7 +79,7 @@ var Analyzer = &analysis.Analyzer{
 	Doc:        "This analyzer identifies types.Types values which contain dataflow sources.",
 	Flags:      config.FlagSet,
 	Run:        run,
-	Requires:   []*analysis.Analyzer{buildssa.Analyzer},
+	Requires:   []*analysis.Analyzer{buildssa.Analyzer, fieldtags.Analyzer},
 	ResultType: reflect.TypeOf(new(sourceClassifier)),
 	FactTypes:  []analysis.Fact{new(typeDeclFact), new(fieldDeclFact)},
 }
@@ -86,6 +87,7 @@ var Analyzer = &analysis.Analyzer{
 var Report bool
 
 func run(pass *analysis.Pass) (interface{}, error) {
+	taggedFields := pass.ResultOf[fieldtags.Analyzer].(fieldtags.ResultType)
 	ssaInput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
 	conf, err := config.ReadConfig()
 	if err != nil {
@@ -95,7 +97,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
 	// Members contains all named entities
 	for _, mem := range ssaInput.Pkg.Members {
 		if ssaType, ok := mem.(*ssa.Type); ok && conf.IsSource(ssaType.Type()) {
-			exportSourceFacts(pass, ssaType, conf)
+			exportSourceFacts(pass, ssaType, conf, taggedFields)
 		}
 	}
 
@@ -107,11 +109,15 @@ func run(pass *analysis.Pass) (interface{}, error) {
 	return classifier, nil
 }
 
-func exportSourceFacts(pass *analysis.Pass, ssaType *ssa.Type, conf *config.Config) {
+func exportSourceFacts(pass *analysis.Pass, ssaType *ssa.Type, conf *config.Config, taggedFields fieldtags.ResultType) {
 	pass.ExportObjectFact(ssaType.Object(), &typeDeclFact{})
 	if under, ok := ssaType.Type().Underlying().(*types.Struct); ok {
 		for i := 0; i < under.NumFields(); i++ {
-			if fld := under.Field(i); conf.IsSourceField(ssaType.Type(), fld) && fld.Pkg() == pass.Pkg {
+			fld := under.Field(i)
+			if fld.Pkg() != pass.Pkg {
+				continue
+			}
+			if conf.IsSourceField(ssaType.Type(), fld) || taggedFields.IsSource(fld) {
 				pass.ExportObjectFact(fld, &fieldDeclFact{})
 			}
 		}


Test patch:
diff --git a/internal/pkg/sourcetype/testdata/src/sourcetype/test.go b/internal/pkg/sourcetype/testdata/src/sourcetype/test.go
index 5189af5..b7dd560 100644
--- a/internal/pkg/sourcetype/testdata/src/sourcetype/test.go
+++ b/internal/pkg/sourcetype/testdata/src/sourcetype/test.go
@@ -15,8 +15,10 @@
 package sourcetype
 
 type Source struct { // want Source:"source type"
-	Data string // want Data:"source field"
-	ID   int
+	Data         string // want Data:"source field"
+	Tagged       string `levee:"source"` // want Tagged:"source field"
+	Name1, Name2 string `levee:"source"` // want Name1:"source field" Name2:"source field"
+	ID           int
 }
 
 type AliasStruct = Source // want AliasStruct:"source type"