structs for models

This commit is contained in:
2024-12-29 23:49:46 +07:00
parent a7f82bf8f2
commit b222a44833
5 changed files with 127 additions and 5 deletions

View File

@@ -1 +1,23 @@
package common package common
import (
"go/ast"
"go/token"
)
type Field struct {
Name string
Type ast.Expr
Tags *string
Options []string // contains options like "autoCreateTime" or "null"
Params []string // contains params like "foreignKey:CustomerId" or "constrain:OnDelete:Cascade"
Position token.Pos
Comment string
}
type Model struct {
Name string
Fields map[string]Field
Position token.Pos
Comment string
}

View File

@@ -1 +1,10 @@
package common package common
import "strings"
func NormalizeStructTags(tags string) string {
// todo: process case with check with ';' literal
tagWithoutQuotes := tags[1 : len(tags)-1]
tagWithoutSemicolons := strings.ReplaceAll(tagWithoutQuotes, ";", ",")
return tagWithoutSemicolons
}

View File

@@ -21,12 +21,12 @@ func isGormValueNullable(tags *structtag.Tags) (*bool, error) {
return nil, nil return nil, nil
} }
gormTag.Options = append([]string{gormTag.Name}, gormTag.Options...)
if err != nil { if err != nil {
return nil, nil return nil, nil
} }
gormTag.Options = append([]string{gormTag.Name}, gormTag.Options...)
nullTagExist := gormTag.HasOption("null") nullTagExist := gormTag.HasOption("null")
notNullTagExist := gormTag.HasOption("not null") notNullTagExist := gormTag.HasOption("not null")
@@ -47,8 +47,8 @@ func CheckFieldNullConsistency(field ast.Field, structName string, structTags st
tags, err := structtag.Parse(structTags) tags, err := structtag.Parse(structTags)
if err != nil { if err != nil {
return errors.New(fmt.Sprintf("Invalid structure tag: %s", err)) return errors.New(fmt.Sprintf("Invalid structure tag: %s", err))
} }
if tags == nil { if tags == nil {
return nil return nil
} }

View File

@@ -1,4 +1,4 @@
package analyzers package nullSafetyCheck
import ( import (
"go/ast" "go/ast"
@@ -7,6 +7,7 @@ import (
"strings" "strings"
) )
// NullSafetyAnalyzer todo: add URL for null safety analyzer rules
var NullSafetyAnalyzer = &analysis.Analyzer{ var NullSafetyAnalyzer = &analysis.Analyzer{
Name: "gormNullSafety", Name: "gormNullSafety",
Doc: "reports problems with nullable fields with unsatisfied tag", Doc: "reports problems with nullable fields with unsatisfied tag",
@@ -24,7 +25,6 @@ func run(pass *analysis.Pass) (any, error) {
if !ok { if !ok {
return true return true
} }
pass.Fset.Position(structure.Pos())
if err := common.CheckUnnamedModel(*typeSpec); err != nil { if err := common.CheckUnnamedModel(*typeSpec); err != nil {
pass.Reportf(structure.Pos(), err.Error()) pass.Reportf(structure.Pos(), err.Error())

View File

@@ -1 +1,92 @@
package referencesCheck package referencesCheck
import (
"github.com/fatih/structtag"
"go/ast"
"golang.org/x/tools/go/analysis"
"gormlint/common"
"strings"
)
// ReferenceAnalyzer todo: add URL for rule
var ReferenceAnalyzer = &analysis.Analyzer{
Name: "gormReferencesCheck",
Doc: "report about invalid references in models",
Run: run,
}
var models map[string]common.Model
func init() {
models = make(map[string]common.Model)
}
func run(pass *analysis.Pass) (any, error) {
for _, file := range pass.Files {
ast.Inspect(file, func(node ast.Node) bool {
typeSpec, ok := node.(*ast.TypeSpec)
if !ok {
return true
}
structure, ok := typeSpec.Type.(*ast.StructType)
if !ok {
return true
}
if err := common.CheckUnnamedModel(*typeSpec); err != nil {
pass.Reportf(structure.Pos(), err.Error())
return false
}
var model common.Model
model.Name = typeSpec.Name.Name
model.Comment = typeSpec.Comment.Text()
model.Position = structure.Pos()
model.Fields = make(map[string]common.Field)
for _, field := range structure.Fields.List {
var structField common.Field
if err := common.CheckUnnamedField(typeSpec.Name.Name, *field); err != nil {
pass.Reportf(field.Pos(), err.Error())
return false
}
structField.Name = field.Names[0].Name
structField.Position = field.Pos()
structField.Comment = field.Comment.Text()
structField.Type = field.Type
if field.Tag != nil {
structField.Tags = &field.Tag.Value
tags, err := structtag.Parse(common.NormalizeStructTags(field.Tag.Value))
if err != nil {
pass.Reportf(field.Pos(), "Invalid structure tag: %s\n", err)
return false
}
if tags != nil {
gormTag, parseErr := tags.Get("gorm")
if gormTag != nil && parseErr == nil {
gormTag.Options = append([]string{gormTag.Name}, gormTag.Options...)
for _, opt := range gormTag.Options {
if strings.Contains(opt, ":") {
structField.Params = append(structField.Options, opt)
} else {
structField.Options = append(structField.Options, opt)
}
}
}
if parseErr != nil {
pass.Reportf(field.Pos(), "Invalid structure tag: %s\n", parseErr)
return false
}
}
model.Fields[structField.Name] = structField
}
models[model.Name] = model
}
return false
})
}
return nil, nil
}