@@ -330,3 +330,208 @@ func handler(r *http.Request) {
330330 assert .False (t , hasBinding , "no binding for x (not a parameter named r)" )
331331 }
332332}
333+
334+ // TestParamAwareRHSInference_PackageAlias ensures package-qualified calls like
335+ // http.NewRequest() are NOT intercepted by param-aware inference (they're already
336+ // handled by inferTypeFromFunctionCall).
337+ func TestParamAwareRHSInference_PackageAlias (t * testing.T ) {
338+ code := `package main
339+
340+ import "net/http"
341+
342+ func makeReq() {
343+ req, _ := http.NewRequest("GET", "/", nil)
344+ _ = req
345+ }
346+ `
347+ reg := core .NewGoModuleRegistry ()
348+ reg .ModulePath = "github.com/example/app"
349+ reg .DirToImport = map [string ]string {"/test" : "main" }
350+
351+ loader := & mockStdlibLoaderWithTypes {
352+ stdlibPkgs : map [string ]bool {"net/http" : true },
353+ functions : map [string ]* core.GoStdlibFunction {
354+ "net/http.NewRequest" : {
355+ Name : "NewRequest" ,
356+ Returns : []* core.GoReturnValue {{Type : "*Request" }, {Type : "error" }},
357+ },
358+ },
359+ types : map [string ]* core.GoStdlibType {},
360+ }
361+ reg .StdlibLoader = loader
362+
363+ importMap := & core.GoImportMap {Imports : map [string ]string {"http" : "net/http" }}
364+ typeEngine := resolution .NewGoTypeInferenceEngine (reg )
365+
366+ callGraph := & core.CallGraph {
367+ Functions : map [string ]* graph.Node {
368+ "main.makeReq" : {
369+ ID : "makeReq" ,
370+ Name : "makeReq" ,
371+ Type : "function_declaration" ,
372+ File : "/test/main.go" ,
373+ },
374+ },
375+ }
376+
377+ err := ExtractGoVariableAssignments ("/test/main.go" , []byte (code ), typeEngine , reg , importMap , callGraph )
378+ assert .NoError (t , err )
379+
380+ // req should be resolved via inferTypeFromFunctionCall (stdlib path), not param-aware
381+ scope := typeEngine .GetScope ("main.makeReq" )
382+ assert .NotNil (t , scope )
383+ bindings , ok := scope .Variables ["req" ]
384+ assert .True (t , ok , "req should have a binding from stdlib lookup" )
385+ if assert .Len (t , bindings , 1 ) {
386+ assert .Equal (t , "net/http.Request" , bindings [0 ].Type .TypeFQN )
387+ }
388+ }
389+
390+ // TestParamAwareRHSInference_MethodNotFound ensures no binding is created when the
391+ // type is known but does not have the called method.
392+ func TestParamAwareRHSInference_MethodNotFound (t * testing.T ) {
393+ code := `package main
394+
395+ import "net/http"
396+
397+ func handler(r *http.Request) {
398+ v := r.UnknownMethod()
399+ _ = v
400+ }
401+ `
402+ reg := core .NewGoModuleRegistry ()
403+ reg .ModulePath = "github.com/example/app"
404+ reg .DirToImport = map [string ]string {"/test" : "main" }
405+
406+ loader := & mockStdlibLoaderWithTypes {
407+ stdlibPkgs : map [string ]bool {"net/http" : true },
408+ functions : map [string ]* core.GoStdlibFunction {},
409+ types : map [string ]* core.GoStdlibType {
410+ "net/http.Request" : {
411+ Name : "Request" ,
412+ Methods : map [string ]* core.GoStdlibFunction {},
413+ },
414+ },
415+ }
416+ reg .StdlibLoader = loader
417+
418+ importMap := & core.GoImportMap {Imports : map [string ]string {"http" : "net/http" }}
419+ typeEngine := resolution .NewGoTypeInferenceEngine (reg )
420+
421+ callGraph := & core.CallGraph {
422+ Functions : map [string ]* graph.Node {
423+ "main.handler" : {
424+ ID : "handler" ,
425+ Name : "handler" ,
426+ Type : "function_declaration" ,
427+ File : "/test/main.go" ,
428+ MethodArgumentsValue : []string {"r" },
429+ MethodArgumentsType : []string {"r: *http.Request" },
430+ },
431+ },
432+ }
433+
434+ err := ExtractGoVariableAssignments ("/test/main.go" , []byte (code ), typeEngine , reg , importMap , callGraph )
435+ assert .NoError (t , err )
436+
437+ scope := typeEngine .GetScope ("main.handler" )
438+ if scope != nil {
439+ _ , hasBinding := scope .Variables ["v" ]
440+ assert .False (t , hasBinding , "no binding when method is not in type's method set" )
441+ }
442+ }
443+
444+ // TestParamAwareRHSInference_ErrorOnlyReturns ensures no binding is created when
445+ // the method only returns error (no non-error return value to infer from).
446+ func TestParamAwareRHSInference_ErrorOnlyReturns (t * testing.T ) {
447+ code := `package main
448+
449+ import "net/http"
450+
451+ func handler(r *http.Request) {
452+ err := r.ParseForm()
453+ _ = err
454+ }
455+ `
456+ reg := core .NewGoModuleRegistry ()
457+ reg .ModulePath = "github.com/example/app"
458+ reg .DirToImport = map [string ]string {"/test" : "main" }
459+
460+ loader := & mockStdlibLoaderWithTypes {
461+ stdlibPkgs : map [string ]bool {"net/http" : true },
462+ functions : map [string ]* core.GoStdlibFunction {},
463+ types : map [string ]* core.GoStdlibType {
464+ "net/http.Request" : {
465+ Name : "Request" ,
466+ Methods : map [string ]* core.GoStdlibFunction {
467+ "ParseForm" : {
468+ Name : "ParseForm" ,
469+ Returns : []* core.GoReturnValue {{Type : "error" }},
470+ },
471+ },
472+ },
473+ },
474+ }
475+ reg .StdlibLoader = loader
476+
477+ importMap := & core.GoImportMap {Imports : map [string ]string {"http" : "net/http" }}
478+ typeEngine := resolution .NewGoTypeInferenceEngine (reg )
479+
480+ callGraph := & core.CallGraph {
481+ Functions : map [string ]* graph.Node {
482+ "main.handler" : {
483+ ID : "handler" ,
484+ Name : "handler" ,
485+ Type : "function_declaration" ,
486+ File : "/test/main.go" ,
487+ MethodArgumentsValue : []string {"r" },
488+ MethodArgumentsType : []string {"r: *http.Request" },
489+ },
490+ },
491+ }
492+
493+ err := ExtractGoVariableAssignments ("/test/main.go" , []byte (code ), typeEngine , reg , importMap , callGraph )
494+ assert .NoError (t , err )
495+
496+ scope := typeEngine .GetScope ("main.handler" )
497+ if scope != nil {
498+ _ , hasBinding := scope .Variables ["err" ]
499+ assert .False (t , hasBinding , "no binding when method only returns error" )
500+ }
501+ }
502+
503+ // TestParamAwareRHSInference_UnqualifiedParamType verifies graceful handling when
504+ // the parameter type is unqualified (no package prefix, e.g. "MyStruct").
505+ func TestParamAwareRHSInference_UnqualifiedParamType (t * testing.T ) {
506+ code := `package main
507+
508+ func process(s MyStruct) {
509+ v := s.Compute()
510+ _ = v
511+ }
512+ `
513+ reg := core .NewGoModuleRegistry ()
514+ reg .ModulePath = "github.com/example/app"
515+ reg .DirToImport = map [string ]string {"/test" : "main" }
516+
517+ importMap := & core.GoImportMap {Imports : map [string ]string {}}
518+ typeEngine := resolution .NewGoTypeInferenceEngine (reg )
519+
520+ callGraph := & core.CallGraph {
521+ Functions : map [string ]* graph.Node {
522+ "main.process" : {
523+ ID : "process" ,
524+ Name : "process" ,
525+ Type : "function_declaration" ,
526+ File : "/test/main.go" ,
527+ MethodArgumentsValue : []string {"s" },
528+ MethodArgumentsType : []string {"s: MyStruct" },
529+ },
530+ },
531+ }
532+
533+ // Must not panic; type has no dot, so extractionSplitGoTypeFQN returns false.
534+ assert .NotPanics (t , func () {
535+ _ = ExtractGoVariableAssignments ("/test/main.go" , []byte (code ), typeEngine , reg , importMap , callGraph )
536+ })
537+ }
0 commit comments