{"record":{"id":"05a4e4ee3daa1ada","repo":"cilium/cilium","slug":"expected-value-as-callexpr","errorCode":null,"errorMessage":"expected Value as CallExpr","messagePattern":"expected Value as CallExpr","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"tools/metricslint/pkg/analyzer/analyzer.go","lineNumber":115,"sourceCode":"\t\treturn 0, fmt.Errorf(\"unsupported nested varlen array: %w\", err)\n\t}\n\treturn len(nestedSlice.Elts), nil\n}\n\nfunc filterRelevantConstructors(node ast.Node) (object, constructor string, argCount int, err error) {\n\t// Look for an initializer with key-value expressions that call another\n\t// function to initialize the field.\n\tkv, ok := node.(*ast.KeyValueExpr)\n\tif !ok {\n\t\treturn \"\", \"\", 0, fmt.Errorf(\"expected KeyValueExpr\")\n\t}\n\tkey, ok := kv.Key.(*ast.Ident)\n\tif !ok {\n\t\treturn \"\", \"\", 0, fmt.Errorf(\"expected Key as Ident\")\n\t}\n\tcall, ok := kv.Value.(*ast.CallExpr)\n\tif !ok {\n\t\treturn \"\", \"\", 0, fmt.Errorf(\"expected Value as CallExpr\")\n\t}\n\n\t// Look for a function with at least two args, where the last arg is a\n\t// composite literal (such as a slice). Example:\n\t//\n\t//     metric.NewCounterVec(opts, []string{...})\n\tif len(call.Args) < 2 {\n\t\treturn \"\", \"\", 0, fmt.Errorf(\"expected 2+ arguments to constructor\")\n\t}\n\tlastArg, ok := call.Args[len(call.Args)-1].(*ast.CompositeLit)\n\tif !ok {\n\t\treturn \"\", \"\", 0, fmt.Errorf(\"expected last arg as CompositeLit\")\n\t}\n\n\t// Store the object and the initializer function. Assume there's just\n\t// one which has a composite literal as the last parameter.\n\tobject = key.Name\n\targCount = len(lastArg.Elts)","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/tools/metricslint/pkg/analyzer/analyzer.go#L97-L133","documentation":"The KeyValueExpr's value is not a function call (*ast.CallExpr). The analyzer only tracks initializer fields whose value directly calls a *Vec constructor, so non-call values (literals, identifiers, selector expressions) are rejected with this sentinel and skipped.","triggerScenarios":"Struct fields initialized with plain values: counter: someVar, counter: pkg.PreexistingCounter, or counter: &struct{}{...} inside a composite literal scanned by metricslint.","commonSituations":"Assigning pre-built metrics from other packages or variables; initializing non-metric fields of the same struct; wrapping constructors in helper closures.","solutions":["No action needed — this is an expected skip for non-constructor initializers","If the field is a metric, call the Vec constructor inline in the initializer rather than assigning a pre-built variable"],"exampleFix":"// before\nvar c = metric.NewCounterVec(opts, labels)\ns := structA{counter: c}\n// after\ns := structA{counter: metric.NewCounterVec(opts, labels)}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"call, ok := kv.Value.(*ast.CallExpr)\nif !ok {\n    return // value is not a constructor call; skipped\n}","tryCatchPattern":null,"preventionTips":["Call the Vec constructor inline in the field initializer","Avoid assigning pre-built metrics variables in initializers you want linted"],"tags":["go","static-analysis","ast","linter"],"backgroundTag":"unsupported-ast-pattern","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}