Skip to content

Instantly share code, notes, and snippets.

@podhmo
Last active September 30, 2025 11:28
Show Gist options
  • Save podhmo/10efd01be20479325485c075451fd546 to your computer and use it in GitHub Desktop.
Save podhmo/10efd01be20479325485c075451fd546 to your computer and use it in GitHub Desktop.
go-scanのコードを真面目に読んで理解する

問題

  • そもそもsymgoで--mode libを指定したときに遅いのと失敗している。
    • メモ化的なものを取り入れたい https://github.com/podhmo/go-scan/blob/develop/docs/plan-find-orphans-memoize.md
    • find-orphansのようなものはメモ化できるがdocgenのようなものは補助関数をメモ化されると困る
    • メモ化する時には戻り値を気にしないと、メソッド呼び出しをtraceできない
    • ⚠️ そもそもメモ化効いてなくない?
  • podhmo/go-scan#850 でローカル内の型定義が解決された
  • しかし問題が現れた。それはderiving-allのe2eが壊れたこと。 podhmo/go-scan#853
    • 順序を無視したInterfaceの取得をone-pathのscanメソッドでは上手く解析できない
    • これは、そもそもインターフェイスのIsImplements()を複雑なものに書き換えたせい podhmo/go-scan#837
    • ⚠️ cacheが壊れる場合がある?
    • そもそもScanPackage()とScanPackageByImport()の間に差異があるらしい。#853のドキュメントでは勘違いしているが同じ挙動にしたい
  • ScanPackage()をScanPackageByImport()で置き換えられない?
    • どうやらsymgoで異なる位置のmainを扱う時に問題が出てきてしまうらしい。 (integration_test/cross_main_package_test.go)

これらは大体全部解決した

問題2

  • type-switchの後のフィールドを見れていない
  • ifの中のtype-assertionを見ていない
@podhmo
Copy link
Author

podhmo commented Sep 19, 2025

別の話としてScanPackage(filePath), ScanPackageByImport(pkgPath) ってわかりにくくない?(あとByPos()というのもあるらしい)

  • ScanPackageはScanPackageByImportに依存させる
  • ScanPackageByPosは消す

@podhmo
Copy link
Author

podhmo commented Sep 19, 2025

全然別の話としてゴミみたいなメソッドが存在する

  • ScannerForSymgo()

そもそもこのためにfind-orphansを作ったのだった

$ find-orphans --include-tests --workspace-root . --mode lib
...

-- Orphans --
(github.com/podhmo/go-scan.*collectingVisitor).Visit
  $HOME/ghq/github.com/podhmo/go-scan/goscan_walk_test.go:41:1
github.com/podhmo/go-scan.tempRootDir
  $HOME/ghq/github.com/podhmo/go-scan/cache_test.go:24:1
github.com/podhmo/go-scan.pathsEqual
  $HOME/ghq/github.com/podhmo/go-scan/goscan_test.go:433:1
(github.com/podhmo/go-scan.*symbolCache).setFileMetadata
  $HOME/ghq/github.com/podhmo/go-scan/cache.go:225:1
(github.com/podhmo/go-scan.*symbolCache).removeSymbolsForFile
  $HOME/ghq/github.com/podhmo/go-scan/cache.go:250:1
(github.com/podhmo/go-scan.*symbolCache).makeRelative
  $HOME/ghq/github.com/podhmo/go-scan/cache.go:260:1
github.com/podhmo/go-scan.getSymbolNameFromKey
  $HOME/ghq/github.com/podhmo/go-scan/cache.go:347:1
(github.com/podhmo/go-scan.*symbolCache).getFilesToScan
  $HOME/ghq/github.com/podhmo/go-scan/cache.go:377:1
(github.com/podhmo/go-scan.*Scanner).Locator
  $HOME/ghq/github.com/podhmo/go-scan/goscan.go:77:1
(github.com/podhmo/go-scan.*Scanner).IsWorkspace
  $HOME/ghq/github.com/podhmo/go-scan/goscan.go:85:1
(github.com/podhmo/go-scan.*Scanner).ModuleRoots
  $HOME/ghq/github.com/podhmo/go-scan/goscan.go:90:1
(github.com/podhmo/go-scan.*Scanner).ScannerForSymgo
  $HOME/ghq/github.com/podhmo/go-scan/goscan.go:167:1
github.com/podhmo/go-scan.WithDeclarationsOnlyPackages
  $HOME/ghq/github.com/podhmo/go-scan/goscan.go:393:1
(github.com/podhmo/go-scan.*Scanner).FindSymbolDefinitionLocation
  $HOME/ghq/github.com/podhmo/go-scan/goscan.go:1099:1

cache関係は使っていないな確かに。あとテストを含めた場合にテストファイルの関数が出るのか。

@podhmo
Copy link
Author

podhmo commented Sep 19, 2025

zed, vscode

  • zedにはtoggle test fileがない。
  • zedのタブ補完でなんとなく末尾のカーソルをぶっ壊す

@podhmo
Copy link
Author

podhmo commented Sep 19, 2025

なんだかなー。

こんな感じで雑にposを与えると一瞬で終わる

diff --git a/symgo/evaluator/evaluator.go b/symgo/evaluator/evaluator.go
index 5fa8871..f4dafa2 100644
--- a/symgo/evaluator/evaluator.go
+++ b/symgo/evaluator/evaluator.go
@@ -76,7 +76,7 @@ type Evaluator struct {
 
 	// memoization
 	memoize          bool
-	memoizationCache map[*object.Function]object.Object
+	memoizationCache map[token.Pos]object.Object
 }
 
 type callFrame struct {
@@ -111,7 +111,7 @@ func WithMaxSteps(n int) Option {
 func WithMemoization() Option {
 	return func(e *Evaluator) {
 		e.memoize = true
-		e.memoizationCache = make(map[*object.Function]object.Object)
+		e.memoizationCache = make(map[token.Pos]object.Object)
 	}
 }
 
@@ -3179,9 +3179,9 @@ func (v inspectValuer) LogValue() slog.Value {
 
 func (e *Evaluator) applyFunction(ctx context.Context, fn object.Object, args []object.Object, pkg *scan.PackageInfo, callPos token.Pos) object.Object {
 	if f, ok := fn.(*object.Function); ok {
-		if e.memoize {
-			if cachedResult, found := e.memoizationCache[f]; found {
-				e.logc(ctx, slog.LevelDebug, "returning memoized result for function", "function", f.Name)
+		if e.memoize && f.Decl != nil {
+			if cachedResult, found := e.memoizationCache[f.Decl.Pos()]; found {
+				// slog.InfoContext(ctx, "@@returning memoized result for function", "function", f.Name)
 				return cachedResult
 			}
 		}
@@ -3191,8 +3191,13 @@ func (e *Evaluator) applyFunction(ctx context.Context, fn object.Object, args []
 
 	if f, ok := fn.(*object.Function); ok {
 		if e.memoize && !isError(result) {
-			e.logc(ctx, slog.LevelDebug, "caching result for function", "function", f.Name)
-			e.memoizationCache[f] = result
+			// e.logc(ctx, slog.LevelDebug, "caching result for function", "function", f.Name)
+			slog.InfoContext(ctx, "!!caching result for function", "function", f.Name)
+			if f.Decl != nil {
+				e.memoizationCache[f.Decl.Pos()] = result
+			} else {
+				slog.InfoContext(ctx, "##function has no declaration", "function", f.Name)
+			}
 		}
 	}
 

@podhmo
Copy link
Author

podhmo commented Sep 19, 2025

あと、memoryの使用量を測っているgithub actionsがうまく動いていない気がする。。

@podhmo
Copy link
Author

podhmo commented Sep 20, 2025

メソッド名のリファクタリングをしたい

goscan.Scanner

func New(options ...ScannerOption) (*Scanner, error)
func (s *Scanner) Locator() *locator.Locator
func (s *Scanner) Fset() *token.FileSet
func (s *Scanner) IsWorkspace() bool
func (s *Scanner) ModuleRoots() []string
func (s *Scanner) Modules() []*scanner.ModuleInfo
func (s *Scanner) ModulePath() string
func (s *Scanner) RootDir() string

func (s *Scanner) LookupOverride(qualifiedName string) (*scanner.TypeInfo, bool)
func (s *Scanner) SetExternalTypeOverrides(ctx context.Context, overrides scanner.ExternalTypeOverride)
func (s *Scanner) AddDeclarationsOnlyPackages(importPaths []string)

func (s *Scanner) BuildImportLookup(file *ast.File) map[string]string
func (s *Scanner) TypeInfoFromExpr(ctx context.Context, expr ast.Expr, currentTypeParams []*scanner.TypeParamInfo, ...) *scanner.FieldType


func (s *Scanner) Scan(ctx context.Context, patterns ...string) ([]*Package, error)
    func (s *Scanner) ScanPackageFromFilePath(ctx context.Context, pkgPath string) (*scanner.PackageInfo, error)
    func (s *Scanner) ScanPackageFromImportPath(ctx context.Context, importPath string) (*scanner.PackageInfo, error)
    func (s *Scanner) ScanFiles(ctx context.Context, filePaths []string) (*scanner.PackageInfo, error)

func (s *Scanner) AllSeenPackages() map[string]*Package
func (s *Scanner) UnscannedGoFiles(packagePathOrImportPath string) ([]string, error)

func (s *Scanner) FindSymbolInPackage(ctx context.Context, importPath string, symbolName string) (*scanner.PackageInfo, error)
func (s *Scanner) FindSymbolDefinitionLocation(ctx context.Context, symbolFullName string) (string, error)
    func (s *Scanner) ScanPackageFromImportPath(ctx context.Context, importPath string) (*scanner.PackageInfo, error)


func (s *Scanner) Implements(ctx context.Context, structCandidate *scanner.TypeInfo, ...) bool

func (s *Scanner) ListExportedSymbols(ctx context.Context, pkgPath string) ([]string, error)
    func (s *Scanner) ScanPackageFromImportPath(ctx context.Context, importPath string) (*scanner.PackageInfo, error)

func (s *Scanner) ResolveType(ctx context.Context, fieldType *scanner.FieldType) (*scanner.TypeInfo, error)
func (s *Scanner) SaveSymbolCache(ctx context.Context) error
  • Scan, ScanFilesは古い依存を追いかけないものだったはず
  • ScanPackage, ScanPackageByImportの名前がわかりにくいので変えたい。 -> 変えた
  • goinspectのような何か作るか

@podhmo
Copy link
Author

podhmo commented Sep 20, 2025

cache使ってないし消す?

@podhmo
Copy link
Author

podhmo commented Sep 26, 2025

symgoで以下のようなインターフェイスとgenerics関数を解析できるようにしてください。この場合はFooとBar両方の物として扱えば良いと思います。

type Loginable interface {
	*Foo | *Bar
}

func WithLogin[T Loginable](req *http.Request, loginable T) *http.Request {
...
}

@podhmo
Copy link
Author

podhmo commented Sep 26, 2025

  • workspace-rootをつけるとfind-orphansがno orphansになる?
  • 相互再帰でpanicする?

@podhmo
Copy link
Author

podhmo commented Sep 27, 2025

go-scan

ポリシー外のもののエラー

  • identifier not found "github.com/alecthomas/kingpin/v2" とかなどでkingpinが見つからない
  • undefined method or field: UsageTemplate for pointer type INSTANCE kingpin.Applicationのembeddedのメソッドが見つからない

  • undefined method or field: Validate for pointer type MAP
  • unary operator - not supported for type RETURN_VALUE
  • not a function: NIL fn != nil
  • expected a package, instance, or pointer on the left side of selector, but got SLICE

@podhmo
Copy link
Author

podhmo commented Sep 30, 2025

find-orphans

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment