Repository: google/go-flow-levee
Pull request #171 (2020-11-05 14:26:20)

Use fieldtags analyzer in sourceinfer analyzer
This PR introduces the fieldtags analyzer in the sourceinfer analyzer. This allows sources identified by field tags to be used to infer further sources derived from them.

This PR depends on #173.

- [x] Tests pass
- (N/A) [ ] Running against a large codebase such as [Kubernetes](https://github.com/kubernetes/kubernetes) does not error out.
- (N/A) [ ] Appropriate changes to README are included in PR

mlevesquedion: I have opened a PR at #173 that makes changes to the `fieldtags` analyzer that are required for proper integration with the `sourceinfer` analyzer. The current PR expands upon #173.
PurelyApplied: Converted to draft until #173 merges.
mlevesquedion: I have reverted the changes to `fieldtags`, and I have extracted the bit of code that was making the conditional feel especially heavy. I think the conditional is reasonable now.

This is ready for review.

Patch:
diff --git a/internal/pkg/sourceinfer/analyzer.go b/internal/pkg/sourceinfer/analyzer.go
index 4f3e836..9ace505 100644
--- a/internal/pkg/sourceinfer/analyzer.go
+++ b/internal/pkg/sourceinfer/analyzer.go
@@ -25,6 +25,7 @@ import (
 	"reflect"
 
 	"github.com/google/go-flow-levee/internal/pkg/config"
+	"github.com/google/go-flow-levee/internal/pkg/fieldtags"
 	"github.com/google/go-flow-levee/internal/pkg/utils"
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
@@ -70,6 +71,7 @@ type Quux Qux
 `,
 	Run: run,
 	Requires: []*analysis.Analyzer{
+		fieldtags.Analyzer,
 		inspect.Analyzer,
 	},
 	ResultType: reflect.TypeOf(new(ResultType)).Elem(),
@@ -85,10 +87,11 @@ func run(pass *analysis.Pass) (interface{}, error) {
 	}
 
 	ins := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+	ft := pass.ResultOf[fieldtags.Analyzer].(fieldtags.ResultType)
 
 	objectGraph := createObjectGraph(pass, ins)
 
-	inferredSources := inferSources(pass, conf, objectGraph)
+	inferredSources := inferSources(pass, conf, ft, objectGraph)
 
 	return inferredSources, nil
 }
@@ -213,7 +216,7 @@ func findNamedTypes(t types.Type) map[*types.Named]bool {
 	return namedTypes
 }
 
-func inferSources(pass *analysis.Pass, conf *config.Config, objGraph objectGraph) ResultType {
+func inferSources(pass *analysis.Pass, conf *config.Config, ft fieldtags.ResultType, objGraph objectGraph) ResultType {
 	inferredSources := ResultType{}
 
 	order := topoSort(objGraph)
@@ -224,9 +227,10 @@ func inferSources(pass *analysis.Pass, conf *config.Config, objGraph objectGraph
 		if seen[o] {
 			continue
 		}
-		if !(isSourceType(conf, o.Type()) || pass.ImportObjectFact(o, &inferredSourceFact{})) {
+		if !(isSourceType(conf, o.Type()) || isTaggedField(ft, o) || pass.ImportObjectFact(o, &inferredSourceFact{})) {
 			continue
 		}
+
 		seen[o] = true
 		stack := []types.Object{o}
 		for len(stack) > 0 {
@@ -249,6 +253,13 @@ func inferSources(pass *analysis.Pass, conf *config.Config, objGraph objectGraph
 	return inferredSources
 }
 
+func isTaggedField(ft fieldtags.ResultType, o types.Object) bool {
+	if v, ok := o.(*types.Var); ok {
+		return ft.IsSource(v)
+	}
+	return false
+}
+
 // topoSort produces the topological order of a given graph.
 // If the graph contains cycles, the order within a cycle is not specified,
 // but the order of nodes outside the cycle will not be affected.


Test patch:
diff --git a/internal/pkg/sourceinfer/testdata/src/example.com/source/source.go b/internal/pkg/sourceinfer/testdata/src/example.com/source/source.go
index ca0aaa7..b80190f 100644
--- a/internal/pkg/sourceinfer/testdata/src/example.com/source/source.go
+++ b/internal/pkg/sourceinfer/testdata/src/example.com/source/source.go
@@ -16,4 +16,8 @@ package source
 
 type Source struct{}
 
+type Tagged struct { // want Tagged:"inferred source"
+	secret string `levee:"source"`
+}
+
 type NotSource struct{}
diff --git a/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/field.go b/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/field.go
index 0a4a1b7..116dfeb 100644
--- a/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/field.go
+++ b/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/field.go
@@ -47,6 +47,10 @@ type (
 	SourceHolderHolder struct { // want SourceHolderHolder:"inferred source"
 		sh SourceHolder
 	}
+
+	TaggedHolder struct { // want TaggedHolder:"inferred source"
+		t source.Tagged
+	}
 )
 
 type (
diff --git a/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/typedef.go b/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/typedef.go
index ae9c6da..3417990 100644
--- a/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/typedef.go
+++ b/internal/pkg/sourceinfer/testdata/src/example.com/tests/core/typedef.go
@@ -26,6 +26,7 @@ type (
 	SourceMap          map[string]source.Source                // want SourceMap:"inferred source"
 	SourceNested       map[string][]map[string][]source.Source // want SourceNested:"inferred source"
 	DefinedSourceSlice []DefinedSource                         // want DefinedSourceSlice:"inferred source"
+	DefinedFromTagged  source.Tagged                           // want DefinedFromTagged:"inferred source"
 )
 
 type (