@@ -46,64 +46,37 @@ func typeImplements(t types.Type, iface *types.Interface) bool {
4646
4747// ResolveInterfaceTypeByName takes an interface name as a string and resolves it to an interface type.
4848func ResolveInterfaceTypeByName (name string ) (* types.Interface , error ) {
49- // Handle built-in types.
50- if obj := types .Universe .Lookup (name ); obj != nil {
51- typeObj , ok := obj .(* types.TypeName )
52- if ! ok {
53- return nil , fmt .Errorf ("object %s is not a type name but a %T" , name , obj )
54- }
55-
56- typ := typeObj .Type ()
57- if ! types .IsInterface (typ ) {
58- return nil , fmt .Errorf ("type %s is not an interface" , name )
59- }
49+ pkgPath , typeName := SplitPackageAndName (name )
6050
61- t , ok := typ .Underlying ().(* types.Interface )
62- if ! ok {
63- return nil , fmt .Errorf ("type %s is not an interface" , name )
51+ if pkgPath == "" {
52+ // Handle built-in types or unqualified names.
53+ scope := types .Universe
54+ obj := scope .Lookup (typeName )
55+ if obj == nil {
56+ // Not found in universe scope.
57+ return nil , fmt .Errorf ("interface %q not found (not a built-in or unqualified)" , typeName )
6458 }
65-
66- return t , nil
59+ // Found in universe, now validate it's an interface type name.
60+ return validateTypeNameIsInterface ( obj , name , pkgPath , typeName )
6761 }
6862
6963 // Handle package-qualified types (e.g., "io.Writer").
70- pkgPath , typeName := SplitPackageAndName (name )
71- if pkgPath == "" {
72- // If not built-in and no package path, it's likely an undefined or local type
73- // that importer won't find directly without context. Assume invalid for now.
74- return nil , fmt .Errorf ("invalid or unqualified interface name: %s" , name )
75- }
76-
77- // Import the package
7864 imp := importer .Default ()
7965 pkg , err := imp .Import (pkgPath )
8066 if err != nil {
67+ // Specific error for import failure.
8168 return nil , fmt .Errorf ("failed to import package %q: %w" , pkgPath , err )
8269 }
8370
84- // Look up the type in the package's scope
85- obj := pkg . Scope () .Lookup (typeName )
71+ scope := pkg . Scope ()
72+ obj := scope .Lookup (typeName )
8673 if obj == nil {
74+ // Not found within the imported package's scope.
8775 return nil , fmt .Errorf ("type %q not found in package %q" , typeName , pkgPath )
8876 }
8977
90- typeObj , ok := obj .(* types.TypeName )
91- if ! ok {
92- return nil , fmt .Errorf ("object %s.%s is not a type name but a %T" , pkgPath , typeName , obj )
93- }
94-
95- typ := typeObj .Type ()
96- if ! types .IsInterface (typ ) {
97- return nil , fmt .Errorf ("type %s is not an interface" , name )
98- }
99-
100- t , ok := typ .Underlying ().(* types.Interface )
101- if ! ok {
102- // This should ideally not happen if types.IsInterface passed, but check defensively.
103- return nil , fmt .Errorf ("type %s is an interface but failed to get underlying *types.Interface" , name )
104- }
105-
106- return t , nil
78+ // Found in package scope, now validate it's an interface type name.
79+ return validateTypeNameIsInterface (obj , name , pkgPath , typeName )
10780}
10881
10982// SplitPackageAndName splits a fully qualified type name like "io.Reader" or "example.com/pkg.Type"
@@ -120,3 +93,29 @@ func SplitPackageAndName(fullName string) (pkgPath string, localName string) {
12093 localName = fullName [lastDot + 1 :]
12194 return pkgPath , localName
12295}
96+
97+ // validateTypeNameIsInterface checks if a successfully looked-up types.Object represents
98+ // a type name that resolves to an interface. It assumes obj is not nil.
99+ func validateTypeNameIsInterface (obj types.Object , fullName string , pkgPath string , typeName string ) (* types.Interface , error ) {
100+ typeObj , ok := obj .(* types.TypeName )
101+ if ! ok {
102+ // Provide context whether it was expected to be built-in or package-qualified.
103+ if pkgPath == "" {
104+ return nil , fmt .Errorf ("object %q is not a type name but a %T" , typeName , obj )
105+ }
106+ return nil , fmt .Errorf ("object %s.%s is not a type name but a %T" , pkgPath , typeName , obj )
107+ }
108+
109+ typ := typeObj .Type ()
110+ if ! types .IsInterface (typ ) {
111+ // Use the original full name in the error message for clarity.
112+ return nil , fmt .Errorf ("type %s is not an interface" , fullName )
113+ }
114+
115+ // Since types.IsInterface passed, we can safely cast typ.Underlying() to *types.Interface
116+ t , ok := typ .Underlying ().(* types.Interface )
117+ if ! ok {
118+ return nil , fmt .Errorf ("type %s is not an interface" , fullName )
119+ }
120+ return t , nil
121+ }
0 commit comments