Skip to content

Commit ec50330

Browse files
authored
refactor(injector/typed): Refactor ResolveInterfaceTypeByName for improved clarity and error handling (#616)
This update refactors the `ResolveInterfaceTypeByName` function to enhance its structure and error reporting. The function now separates the logic for handling built-in types and package-qualified types, improving readability. A new helper function, `validateTypeNameIsInterface`, has been introduced to encapsulate the validation of interface types, ensuring consistent error messages and reducing code duplication. Additionally, specific error messages have been added for import failures and type lookups, providing clearer context for users. Signed-off-by: Kemal Akkoyun <kemal.akkoyun@datadoghq.com> --------- Signed-off-by: Kemal Akkoyun <kemal.akkoyun@datadoghq.com>
1 parent b3088d9 commit ec50330

1 file changed

Lines changed: 42 additions & 43 deletions

File tree

internal/injector/typed/typecheck.go

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
4848
func 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

Comments
 (0)