{"record":{"id":"a89fac8df673f39f","repo":"cilium/cilium","slug":"expected-function-to-be-a-method","errorCode":null,"errorMessage":"expected function to be a method","messagePattern":"expected function to be a method","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"tools/metricslint/pkg/analyzer/analyzer.go","lineNumber":160,"sourceCode":"\t\treturn \"\", \"\", 0, fmt.Errorf(\"ignoring noisy constructor name\")\n\t}\n\n\treturn object, constructor, argCount, nil\n}\n\nfunc filterRelevantMethods(call *ast.CallExpr) (object, method string, err error) {\n\tfn, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn \"\", \"\", fmt.Errorf(\"expected SelectorExpr\")\n\t}\n\n\tif !strings.HasPrefix(fn.Sel.Name, \"With\") {\n\t\treturn \"\", \"\", fmt.Errorf(\"ignoring noisy method name\")\n\t}\n\n\tobj, ok := fn.X.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn \"\", \"\", fmt.Errorf(\"expected function to be a method\")\n\t}\n\n\treturn obj.Sel.Name, fn.Sel.Name, nil\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tinsp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)\n\n\t// Collect objects that are initialized with a variable length slice\n\t// parameter as the last argument to a constructor. Map object name to\n\t// parameter count, and store the constructor name for later reference.\n\tobjs := make(map[string]int)\n\tconstructors := make(map[string]string)\n\tinsp.Preorder([]ast.Node{\n\t\t(*ast.KeyValueExpr)(nil),\n\t}, func(node ast.Node) {\n\t\tobj, constructor, args, err := filterRelevantConstructors(node)\n\t\tif err != nil {","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/tools/metricslint/pkg/analyzer/analyzer.go#L142-L178","documentation":"filterRelevantMethods requires the receiver of a \"With*\" call to itself be a selector expression (a method call on some object, e.g. a.WithX() where a is itself a selector). If fn.X is not a *ast.SelectorExpr — for example a plain identifier like m.WithX() or a function literal — this error is returned. It encodes the tool's assumption that it only analyzes method chains.","triggerScenarios":"A call like pkgVar.WithFoo() where pkgVar is a simple identifier (not a selector), or fn.X is any non-selector expression such as a parenthesized expression or index expression.","commonSituations":"Calling With-style builders on local struct variables or package-level identifiers that the analyzer's chain-heuristic does not recognize.","solutions":["Verify the call site: the receiver must be a method/field access chain (obj.Method.WithX(...)).","Ignore if the call is a direct method call on a local variable that the tool intentionally does not analyze.","Extend the analyzer to also accept *ast.Ident receivers if that pattern should be covered."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"sel, ok := call.Fun.(*ast.SelectorExpr); if ok { _, okRecv := sel.X.(*ast.SelectorExpr); if !okRecv { /* will be skipped */ } }","typeGuard":"func isMethodChain(call *ast.CallExpr) bool {\n\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\t_, ok = sel.X.(*ast.SelectorExpr)\n\treturn ok\n}","tryCatchPattern":null,"preventionTips":["Call With-style builders on method chains (obj.Method.WithX) rather than bare identifiers if you want the analyzer to see them.","Remember package-level identifiers (pkg.WithX) are not analyzed by this heuristic.","Filter call sites with a receiver type guard before analysis."],"tags":["go","static-analysis","ast","analyzer"],"backgroundTag":"static-analysis-skip","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}