generics.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. //go:build go1.18
  2. // +build go1.18
  3. package swag
  4. import (
  5. "errors"
  6. "fmt"
  7. "github.com/go-openapi/spec"
  8. "go/ast"
  9. "strings"
  10. "sync"
  11. "unicode"
  12. )
  13. var genericDefinitionsMutex = &sync.RWMutex{}
  14. var genericsDefinitions = map[*TypeSpecDef]map[string]*TypeSpecDef{}
  15. type genericTypeSpec struct {
  16. ArrayDepth int
  17. TypeSpec *TypeSpecDef
  18. Name string
  19. }
  20. func (s *genericTypeSpec) Type() ast.Expr {
  21. if s.TypeSpec != nil {
  22. return &ast.SelectorExpr{
  23. X: &ast.Ident{Name: ""},
  24. Sel: &ast.Ident{Name: s.Name},
  25. }
  26. }
  27. return &ast.Ident{Name: s.Name}
  28. }
  29. func (s *genericTypeSpec) TypeDocName() string {
  30. if s.TypeSpec != nil {
  31. return strings.Replace(TypeDocName(s.TypeSpec.FullName(), s.TypeSpec.TypeSpec), "-", "_", -1)
  32. }
  33. return s.Name
  34. }
  35. func typeSpecFullName(typeSpecDef *TypeSpecDef) string {
  36. fullName := typeSpecDef.FullName()
  37. if typeSpecDef.TypeSpec.TypeParams != nil {
  38. fullName = fullName + "["
  39. for i, typeParam := range typeSpecDef.TypeSpec.TypeParams.List {
  40. if i > 0 {
  41. fullName = fullName + "-"
  42. }
  43. fullName = fullName + typeParam.Names[0].Name
  44. }
  45. fullName = fullName + "]"
  46. }
  47. return fullName
  48. }
  49. func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
  50. genericDefinitionsMutex.RLock()
  51. tSpec, ok := genericsDefinitions[original][fullGenericForm]
  52. genericDefinitionsMutex.RUnlock()
  53. if ok {
  54. return tSpec
  55. }
  56. pkgName := strings.Split(fullGenericForm, ".")[0]
  57. genericTypeName, genericParams := splitStructName(fullGenericForm)
  58. if genericParams == nil {
  59. return nil
  60. }
  61. genericParamTypeDefs := map[string]*genericTypeSpec{}
  62. if len(genericParams) != len(original.TypeSpec.TypeParams.List) {
  63. return nil
  64. }
  65. for i, genericParam := range genericParams {
  66. arrayDepth := 0
  67. for {
  68. if len(genericParam) <= 2 || genericParam[:2] != "[]" {
  69. break
  70. }
  71. genericParam = genericParam[2:]
  72. arrayDepth++
  73. }
  74. tdef := pkgDefs.FindTypeSpec(genericParam, file, parseDependency)
  75. if tdef != nil && !strings.Contains(genericParam, ".") {
  76. genericParam = fullTypeName(file.Name.Name, genericParam)
  77. }
  78. genericParamTypeDefs[original.TypeSpec.TypeParams.List[i].Names[0].Name] = &genericTypeSpec{
  79. ArrayDepth: arrayDepth,
  80. TypeSpec: tdef,
  81. Name: genericParam,
  82. }
  83. }
  84. parametrizedTypeSpec := &TypeSpecDef{
  85. File: original.File,
  86. PkgPath: original.PkgPath,
  87. TypeSpec: &ast.TypeSpec{
  88. Doc: original.TypeSpec.Doc,
  89. Comment: original.TypeSpec.Comment,
  90. Assign: original.TypeSpec.Assign,
  91. },
  92. }
  93. ident := &ast.Ident{
  94. NamePos: original.TypeSpec.Name.NamePos,
  95. Obj: original.TypeSpec.Name.Obj,
  96. }
  97. if strings.Contains(genericTypeName, ".") {
  98. genericTypeName = strings.Split(genericTypeName, ".")[1]
  99. }
  100. var typeName = []string{TypeDocName(fullTypeName(pkgName, genericTypeName), parametrizedTypeSpec.TypeSpec)}
  101. for _, def := range original.TypeSpec.TypeParams.List {
  102. if specDef, ok := genericParamTypeDefs[def.Names[0].Name]; ok {
  103. var prefix = ""
  104. if specDef.ArrayDepth > 0 {
  105. prefix = "array_"
  106. if specDef.ArrayDepth > 1 {
  107. prefix = fmt.Sprintf("array%d_", specDef.ArrayDepth)
  108. }
  109. }
  110. typeName = append(typeName, prefix+specDef.TypeDocName())
  111. }
  112. }
  113. ident.Name = strings.Join(typeName, "-")
  114. ident.Name = strings.Replace(ident.Name, ".", "_", -1)
  115. pkgNamePrefix := pkgName + "_"
  116. if strings.HasPrefix(ident.Name, pkgNamePrefix) {
  117. ident.Name = fullTypeName(pkgName, ident.Name[len(pkgNamePrefix):])
  118. }
  119. ident.Name = string(IgnoreNameOverridePrefix) + ident.Name
  120. parametrizedTypeSpec.TypeSpec.Name = ident
  121. newType := pkgDefs.resolveGenericType(original.File, original.TypeSpec.Type, genericParamTypeDefs, parseDependency)
  122. genericDefinitionsMutex.Lock()
  123. defer genericDefinitionsMutex.Unlock()
  124. parametrizedTypeSpec.TypeSpec.Type = newType
  125. if genericsDefinitions[original] == nil {
  126. genericsDefinitions[original] = map[string]*TypeSpecDef{}
  127. }
  128. genericsDefinitions[original][fullGenericForm] = parametrizedTypeSpec
  129. return parametrizedTypeSpec
  130. }
  131. // splitStructName splits a generic struct name in his parts
  132. func splitStructName(fullGenericForm string) (string, []string) {
  133. //remove all spaces character
  134. fullGenericForm = strings.Map(func(r rune) rune {
  135. if unicode.IsSpace(r) {
  136. return -1
  137. }
  138. return r
  139. }, fullGenericForm)
  140. // split only at the first '[' and remove the last ']'
  141. if fullGenericForm[len(fullGenericForm)-1] != ']' {
  142. return "", nil
  143. }
  144. genericParams := strings.SplitN(fullGenericForm[:len(fullGenericForm)-1], "[", 2)
  145. if len(genericParams) == 1 {
  146. return "", nil
  147. }
  148. // generic type name
  149. genericTypeName := genericParams[0]
  150. depth := 0
  151. genericParams = strings.FieldsFunc(genericParams[1], func(r rune) bool {
  152. if r == '[' {
  153. depth++
  154. } else if r == ']' {
  155. depth--
  156. } else if r == ',' && depth == 0 {
  157. return true
  158. }
  159. return false
  160. })
  161. if depth != 0 {
  162. return "", nil
  163. }
  164. return genericTypeName, genericParams
  165. }
  166. func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec, parseDependency bool) ast.Expr {
  167. switch astExpr := expr.(type) {
  168. case *ast.Ident:
  169. if genTypeSpec, ok := genericParamTypeDefs[astExpr.Name]; ok {
  170. retType := genTypeSpec.Type()
  171. for i := 0; i < genTypeSpec.ArrayDepth; i++ {
  172. retType = &ast.ArrayType{Elt: retType}
  173. }
  174. return retType
  175. }
  176. case *ast.ArrayType:
  177. return &ast.ArrayType{
  178. Elt: pkgDefs.resolveGenericType(file, astExpr.Elt, genericParamTypeDefs, parseDependency),
  179. Len: astExpr.Len,
  180. Lbrack: astExpr.Lbrack,
  181. }
  182. case *ast.StarExpr:
  183. return &ast.StarExpr{
  184. Star: astExpr.Star,
  185. X: pkgDefs.resolveGenericType(file, astExpr.X, genericParamTypeDefs, parseDependency),
  186. }
  187. case *ast.IndexExpr, *ast.IndexListExpr:
  188. fullGenericName, _ := getGenericFieldType(file, expr, genericParamTypeDefs)
  189. typeDef := pkgDefs.findGenericTypeSpec(fullGenericName, file, parseDependency)
  190. if typeDef != nil {
  191. return typeDef.TypeSpec.Type
  192. }
  193. case *ast.StructType:
  194. newStructTypeDef := &ast.StructType{
  195. Struct: astExpr.Struct,
  196. Incomplete: astExpr.Incomplete,
  197. Fields: &ast.FieldList{
  198. Opening: astExpr.Fields.Opening,
  199. Closing: astExpr.Fields.Closing,
  200. },
  201. }
  202. for _, field := range astExpr.Fields.List {
  203. newField := &ast.Field{
  204. Type: field.Type,
  205. Doc: field.Doc,
  206. Names: field.Names,
  207. Tag: field.Tag,
  208. Comment: field.Comment,
  209. }
  210. newField.Type = pkgDefs.resolveGenericType(file, field.Type, genericParamTypeDefs, parseDependency)
  211. newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField)
  212. }
  213. return newStructTypeDef
  214. }
  215. return expr
  216. }
  217. func getExtendedGenericFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) {
  218. switch fieldType := field.(type) {
  219. case *ast.ArrayType:
  220. fieldName, err := getExtendedGenericFieldType(file, fieldType.Elt, genericParamTypeDefs)
  221. return "[]" + fieldName, err
  222. case *ast.StarExpr:
  223. return getExtendedGenericFieldType(file, fieldType.X, genericParamTypeDefs)
  224. case *ast.Ident:
  225. if genericParamTypeDefs != nil {
  226. if typeSpec, ok := genericParamTypeDefs[fieldType.Name]; ok {
  227. return typeSpec.Name, nil
  228. }
  229. }
  230. if fieldType.Obj == nil {
  231. return fieldType.Name, nil
  232. }
  233. tSpec := &TypeSpecDef{
  234. File: file,
  235. TypeSpec: fieldType.Obj.Decl.(*ast.TypeSpec),
  236. PkgPath: file.Name.Name,
  237. }
  238. return tSpec.FullName(), nil
  239. default:
  240. return getFieldType(file, field)
  241. }
  242. }
  243. func getGenericFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) {
  244. var fullName string
  245. var baseName string
  246. var err error
  247. switch fieldType := field.(type) {
  248. case *ast.IndexListExpr:
  249. baseName, err = getGenericTypeName(file, fieldType.X)
  250. if err != nil {
  251. return "", err
  252. }
  253. fullName = baseName + "["
  254. for _, index := range fieldType.Indices {
  255. fieldName, err := getExtendedGenericFieldType(file, index, genericParamTypeDefs)
  256. if err != nil {
  257. return "", err
  258. }
  259. fullName += fieldName + ","
  260. }
  261. fullName = strings.TrimRight(fullName, ",") + "]"
  262. case *ast.IndexExpr:
  263. baseName, err = getGenericTypeName(file, fieldType.X)
  264. if err != nil {
  265. return "", err
  266. }
  267. indexName, err := getExtendedGenericFieldType(file, fieldType.Index, genericParamTypeDefs)
  268. if err != nil {
  269. return "", err
  270. }
  271. fullName = fmt.Sprintf("%s[%s]", baseName, indexName)
  272. }
  273. if fullName == "" {
  274. return "", fmt.Errorf("unknown field type %#v", field)
  275. }
  276. var packageName string
  277. if !strings.Contains(baseName, ".") {
  278. if file.Name == nil {
  279. return "", errors.New("file name is nil")
  280. }
  281. packageName, _ = getFieldType(file, file.Name)
  282. }
  283. return strings.TrimLeft(fmt.Sprintf("%s.%s", packageName, fullName), "."), nil
  284. }
  285. func getGenericTypeName(file *ast.File, field ast.Expr) (string, error) {
  286. switch fieldType := field.(type) {
  287. case *ast.Ident:
  288. if fieldType.Obj == nil {
  289. return fieldType.Name, nil
  290. }
  291. tSpec := &TypeSpecDef{
  292. File: file,
  293. TypeSpec: fieldType.Obj.Decl.(*ast.TypeSpec),
  294. PkgPath: file.Name.Name,
  295. }
  296. return tSpec.FullName(), nil
  297. case *ast.ArrayType:
  298. tSpec := &TypeSpecDef{
  299. File: file,
  300. TypeSpec: fieldType.Elt.(*ast.Ident).Obj.Decl.(*ast.TypeSpec),
  301. PkgPath: file.Name.Name,
  302. }
  303. return tSpec.FullName(), nil
  304. case *ast.SelectorExpr:
  305. return fmt.Sprintf("%s.%s", fieldType.X.(*ast.Ident).Name, fieldType.Sel.Name), nil
  306. }
  307. return "", fmt.Errorf("unknown type %#v", field)
  308. }
  309. func (parser *Parser) parseGenericTypeExpr(file *ast.File, typeExpr ast.Expr) (*spec.Schema, error) {
  310. switch expr := typeExpr.(type) {
  311. // suppress debug messages for these types
  312. case *ast.InterfaceType:
  313. case *ast.StructType:
  314. case *ast.Ident:
  315. case *ast.StarExpr:
  316. case *ast.SelectorExpr:
  317. case *ast.ArrayType:
  318. case *ast.MapType:
  319. case *ast.FuncType:
  320. case *ast.IndexExpr:
  321. name, err := getExtendedGenericFieldType(file, expr, nil)
  322. if err == nil {
  323. if schema, err := parser.getTypeSchema(name, file, false); err == nil {
  324. return schema, nil
  325. }
  326. }
  327. parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead. (%s)\n", typeExpr, err)
  328. default:
  329. parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead.\n", typeExpr)
  330. }
  331. return PrimitiveSchema(OBJECT), nil
  332. }