1336 lines
53 KiB
Go
1336 lines
53 KiB
Go
package provider
|
||
|
||
import (
|
||
"fmt"
|
||
"go/ast"
|
||
"go/parser"
|
||
"go/token"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
)
|
||
|
||
// ASTWalker 处理 Go 抽象语法树(AST)的遍历器,专门用于发现 Provider 相关的结构体
|
||
//
|
||
// 架构设计:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ ASTWalker │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ fileSet: *token.FileSet - 文件集,记录源码位置信息 │
|
||
// │ commentParser: *CommentParser - 注释解析器,处理注解 │
|
||
// │ config: *WalkerConfig - 遍历器配置 │
|
||
// │ visitors: []NodeVisitor - 访问者列表,支持扩展 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 核心功能:
|
||
// - 文件过滤:根据配置跳过测试文件、生成文件等
|
||
// - AST 解析:使用 Go 标准库解析源文件为抽象语法树
|
||
// - 结构化遍历:按照 AST 层次结构进行遍历
|
||
// - 访问者模式:支持多个访问者同时处理不同的分析任务
|
||
// - 错误处理:提供完善的错误处理机制
|
||
//
|
||
// 执行流程:
|
||
// 1. 文件过滤 → 2. AST 解析 → 3. 遍历声明 → 4. 访问者通知 → 5. 结果收集
|
||
//
|
||
// 设计原则:
|
||
// - 单一职责:专注于 AST 遍历,不涉及具体的业务逻辑
|
||
// - 开闭原则:通过访问者模式支持功能扩展,无需修改核心代码
|
||
// - 接口隔离:定义清晰的访问者接口,避免过度依赖
|
||
// - 配置驱动:通过配置控制遍历行为,提高灵活性
|
||
//
|
||
// 使用场景:
|
||
// - Provider 注解发现:查找带有 @provider 注解的结构体
|
||
// - 代码分析:分析代码结构、依赖关系等
|
||
// - 代码生成:基于 AST 分析结果生成代码
|
||
// - 重构工具:基于 AST 进行代码重构操作
|
||
type ASTWalker struct {
|
||
fileSet *token.FileSet
|
||
commentParser *CommentParser
|
||
config *WalkerConfig
|
||
visitors []NodeVisitor
|
||
}
|
||
|
||
// WalkerConfig 配置 AST 遍历器的行为参数
|
||
//
|
||
// 配置选项:
|
||
// - IncludeTestFiles: 是否包含测试文件(_test.go 后缀)
|
||
// - IncludeGeneratedFiles: 是否包含生成的文件(.gen.go 后缀)
|
||
// - MaxFileSize: 最大文件大小限制(字节),防止大文件影响性能
|
||
// - StrictMode: 严格模式,启用更严格的验证和错误检查
|
||
//
|
||
// 使用场景:
|
||
// - 开发环境:包含测试文件,禁用严格模式,便于调试
|
||
// - 生产环境:跳过测试文件,启用严格模式,提高解析质量
|
||
// - 性能优化:设置合理的文件大小限制,跳过不必要的文件
|
||
type WalkerConfig struct {
|
||
IncludeTestFiles bool
|
||
IncludeGeneratedFiles bool
|
||
MaxFileSize int64
|
||
StrictMode bool
|
||
}
|
||
|
||
// NodeVisitor 定义 AST 节点访问者接口,支持在遍历过程中处理不同类型的节点
|
||
//
|
||
// 接口设计原则:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ NodeVisitor Interface │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ VisitFile: 文件级别处理,在文件开始时调用 │
|
||
// │ VisitGenDecl: 通用声明处理,类型、变量、常量声明 │
|
||
// │ VisitTypeSpec: 类型规范处理,具体的类型定义 │
|
||
// │ VisitStructType: 结构体类型处理,结构体类型定义 │
|
||
// │ VisitStructField: 结构体字段处理,结构体中的字段 │
|
||
// │ Complete: 完成处理,在文件处理完成时调用 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 调用时机:
|
||
// 1. VisitFile: 开始处理文件时调用,用于初始化文件级上下文
|
||
// 2. VisitGenDecl: 遇到通用声明时调用(type、var、const)
|
||
// 3. VisitTypeSpec: 遇到类型规范时调用(具体的类型定义)
|
||
// 4. VisitStructType: 遇到结构体类型时调用
|
||
// 5. VisitStructField: 遍历结构体字段时调用
|
||
// 6. Complete: 文件处理完成时调用,用于清理和结果收集
|
||
//
|
||
// 错误处理:
|
||
// - 如果任何方法返回错误,遍历过程会立即停止
|
||
// - 访问者应该根据错误类型决定是否继续处理
|
||
// - 建议使用 fmt.Errorf 包装错误信息,包含足够的上下文
|
||
//
|
||
// 扩展性:
|
||
// - 可以实现多个不同的访问者来处理不同的分析任务
|
||
// - 访问者之间相互独立,不会相互影响
|
||
// - 可以通过组合多个访问者来实现复杂的功能
|
||
type NodeVisitor interface {
|
||
// VisitFile 在开始处理新文件时调用,用于文件级别的初始化
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前处理的文件路径
|
||
// - node: 解析后的 AST 文件节点
|
||
//
|
||
// 返回值:
|
||
// - error: 如果返回错误,遍历过程会立即停止
|
||
//
|
||
// 使用场景:
|
||
// - 初始化文件级上下文信息
|
||
// - 记录文件级别的统计信息
|
||
// - 设置文件特定的配置
|
||
VisitFile(filePath string, node *ast.File) error
|
||
|
||
// VisitGenDecl 在遇到通用声明时调用(type、var、const)
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - decl: 通用声明节点
|
||
//
|
||
// 返回值:
|
||
// - error: 如果返回错误,遍历过程会立即停止
|
||
//
|
||
// 使用场景:
|
||
// - 处理包级别的变量和常量声明
|
||
// - 分析声明的文档注释
|
||
// - 收集声明级别的元数据
|
||
VisitGenDecl(filePath string, decl *ast.GenDecl) error
|
||
|
||
// VisitTypeSpec 在遇到类型规范时调用(具体的类型定义)
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - typeSpec: 类型规范节点
|
||
// - decl: 包含该类型规范的通用声明
|
||
//
|
||
// 返回值:
|
||
// - error: 如果返回错误,遍历过程会立即停止
|
||
//
|
||
// 使用场景:
|
||
// - 发现结构体、接口、函数类型等
|
||
// - 分析类型定义的注释和属性
|
||
// - 建立类型索引和关系图
|
||
VisitTypeSpec(filePath string, typeSpec *ast.TypeSpec, decl *ast.GenDecl) error
|
||
|
||
// VisitStructType 在遇到结构体类型时调用
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - structType: 结构体类型节点
|
||
// - typeSpec: 对应的类型规范
|
||
// - decl: 包含该结构体的通用声明
|
||
//
|
||
// 返回值:
|
||
// - error: 如果返回错误,遍历过程会立即停止
|
||
//
|
||
// 使用场景:
|
||
// - 分析结构体定义
|
||
// - 处理结构体的注解和属性
|
||
// - 收集结构体级别的元数据
|
||
VisitStructType(filePath string, structType *ast.StructType, typeSpec *ast.TypeSpec, decl *ast.GenDecl) error
|
||
|
||
// VisitStructField 在遍历结构体字段时调用
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - field: 结构体字段节点
|
||
// - structType: 所属的结构体类型
|
||
//
|
||
// 返回值:
|
||
// - error: 如果返回错误,遍历过程会立即停止
|
||
//
|
||
// 使用场景:
|
||
// - 分析字段类型和标签
|
||
// - 处理注入相关的标签(inject 标签)
|
||
// - 收集字段级别的依赖信息
|
||
VisitStructField(filePath string, field *ast.Field, structType *ast.StructType) error
|
||
|
||
// Complete 在文件处理完成时调用,用于清理和结果收集
|
||
//
|
||
// 参数:
|
||
// - filePath: 已完成处理的文件路径
|
||
//
|
||
// 返回值:
|
||
// - error: 如果返回错误,会影响整体处理结果
|
||
//
|
||
// 使用场景:
|
||
// - 清理文件级的临时数据
|
||
// - 收集和汇总处理结果
|
||
// - 执行文件级别的验证和检查
|
||
Complete(filePath string) error
|
||
}
|
||
|
||
// NewASTWalker 使用默认配置创建新的 ASTWalker 实例
|
||
//
|
||
// 初始化流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ NewASTWalker() │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 创建文件集:token.NewFileSet() │
|
||
// │ 2. 创建注释解析器:NewCommentParser() │
|
||
// │ 3. 设置默认配置:WalkerConfig{...} │
|
||
// │ 4. 初始化访问者列表:make([]NodeVisitor, 0) │
|
||
// │ 5. 构建遍历器实例:&ASTWalker{...} │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 默认配置:
|
||
// - IncludeTestFiles: false(跳过测试文件)
|
||
// - IncludeGeneratedFiles: false(跳过生成的文件)
|
||
// - MaxFileSize: 10MB(限制文件大小)
|
||
// - StrictMode: false(禁用严格模式)
|
||
//
|
||
// 返回值:
|
||
// - *ASTWalker: 配置好的遍历器实例,可以直接使用
|
||
//
|
||
// 使用示例:
|
||
//
|
||
// walker := NewASTWalker()
|
||
// visitor := NewProviderDiscoveryVisitor()
|
||
// walker.AddVisitor(visitor)
|
||
// err := walker.WalkFile("user_service.go")
|
||
//
|
||
// 注意事项:
|
||
// - 必须添加至少一个访问者才能进行有效的分析
|
||
// - 默认配置适用于大多数生产环境
|
||
// - 可以通过 WithConfig 方法修改配置
|
||
func NewASTWalker() *ASTWalker {
|
||
return &ASTWalker{
|
||
fileSet: token.NewFileSet(),
|
||
commentParser: NewCommentParser(),
|
||
config: &WalkerConfig{
|
||
IncludeTestFiles: false,
|
||
IncludeGeneratedFiles: false,
|
||
MaxFileSize: 10 * 1024 * 1024, // 10MB
|
||
StrictMode: false,
|
||
},
|
||
visitors: make([]NodeVisitor, 0),
|
||
}
|
||
}
|
||
|
||
// NewASTWalkerWithConfig 使用自定义配置创建新的 ASTWalker 实例
|
||
//
|
||
// 设计目标:
|
||
// - 提供灵活的配置选项
|
||
// - 支持不同的使用场景(调试、生产、测试)
|
||
// - 保持向后兼容性
|
||
//
|
||
// 参数处理:
|
||
// - 如果 config 为 nil,自动使用默认配置
|
||
// - 根据配置的 StrictMode 创建对应的注释解析器
|
||
//
|
||
// 自定义配置场景:
|
||
// - 调试模式:包含测试文件,启用严格模式
|
||
// - 性能优化:设置较小的文件大小限制
|
||
// - 开发模式:包含生成的文件进行分析
|
||
//
|
||
// 参数:
|
||
// - config: 自定义的遍历器配置(可为 nil)
|
||
//
|
||
// 返回值:
|
||
// - *ASTWalker: 使用指定配置的遍历器实例
|
||
//
|
||
// 使用示例:
|
||
//
|
||
// config := &WalkerConfig{
|
||
// IncludeTestFiles: true,
|
||
// IncludeGeneratedFiles: true,
|
||
// StrictMode: true,
|
||
// MaxFileSize: 5 * 1024 * 1024, // 5MB
|
||
// }
|
||
// walker := NewASTWalkerWithConfig(config)
|
||
//
|
||
// 注意事项:
|
||
// - 自定义配置会覆盖所有默认设置
|
||
// - 建议在特殊场景下使用自定义配置
|
||
// - 确保 MaxFileSize 设置合理,避免性能问题
|
||
func NewASTWalkerWithConfig(config *WalkerConfig) *ASTWalker {
|
||
if config == nil {
|
||
return NewASTWalker()
|
||
}
|
||
|
||
return &ASTWalker{
|
||
fileSet: token.NewFileSet(),
|
||
commentParser: NewCommentParserWithStrictMode(config.StrictMode),
|
||
config: config,
|
||
visitors: make([]NodeVisitor, 0),
|
||
}
|
||
}
|
||
|
||
// AddVisitor 添加节点访问者到遍历器
|
||
//
|
||
// 功能说明:
|
||
// - 将指定的访问者添加到访问者列表
|
||
// - 访问者会按照添加的顺序被调用
|
||
// - 支持添加多个访问者来处理不同的分析任务
|
||
//
|
||
// 使用场景:
|
||
// - 添加 Provider 发现访问者
|
||
// - 添加依赖分析访问者
|
||
// - 添加代码质量检查访问者
|
||
//
|
||
// 参数:
|
||
// - visitor: 要添加的节点访问者
|
||
//
|
||
// 注意事项:
|
||
// - 同一个访问者实例只能添加一次
|
||
// - 添加的访问者必须实现 NodeVisitor 接口
|
||
// - 建议在遍历开始前添加所有需要的访问者
|
||
func (aw *ASTWalker) AddVisitor(visitor NodeVisitor) {
|
||
aw.visitors = append(aw.visitors, visitor)
|
||
}
|
||
|
||
// RemoveVisitor 从遍历器中移除指定的节点访问者
|
||
//
|
||
// 功能说明:
|
||
// - 从访问者列表中移除指定的访问者实例
|
||
// - 使用对象引用进行比较,移除第一个匹配的访问者
|
||
// - 如果访问者不存在,不做任何操作
|
||
//
|
||
// 使用场景:
|
||
// - 动态调整分析任务
|
||
// - 移除不再需要的访问者
|
||
// - 优化性能,移除不必要的处理
|
||
//
|
||
// 参数:
|
||
// - visitor: 要移除的节点访问者
|
||
//
|
||
// 注意事项:
|
||
// - 如果有多个相同的访问者实例,只移除第一个
|
||
// - 移除操作会改变访问者列表的顺序
|
||
// - 建议在遍历开始前完成访问者的调整
|
||
func (aw *ASTWalker) RemoveVisitor(visitor NodeVisitor) {
|
||
for i, v := range aw.visitors {
|
||
if v == visitor {
|
||
aw.visitors = append(aw.visitors[:i], aw.visitors[i+1:]...)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// WalkFile 遍历单个 Go 文件的 AST,执行完整的分析和处理流程
|
||
//
|
||
// 执行流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ WalkFile(filePath) │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 文件过滤:shouldProcessFile() │
|
||
// │ 2. AST 解析:parser.ParseFile() │
|
||
// │ 3. 文件开始:VisitFile() 通知所有访问者 │
|
||
// │ 4. AST 遍历:traverseFile() 递归遍历 AST │
|
||
// │ 5. 文件完成:Complete() 通知所有访问者 │
|
||
// │ 6. 返回结果:成功返回 nil,失败返回错误 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 详细步骤说明:
|
||
// 步骤 1:文件过滤
|
||
// - 检查文件扩展名是否为 .go
|
||
// - 根据配置跳过测试文件和生成的文件
|
||
// - 检查文件大小限制
|
||
//
|
||
// 步骤 2:AST 解析
|
||
// - 使用 Go 标准库解析源文件
|
||
// - 启用注释解析,保留所有注释信息
|
||
// - 记录源码位置信息到文件集
|
||
//
|
||
// 步骤 3:文件开始通知
|
||
// - 按顺序通知所有访问者文件开始处理
|
||
// - 传递文件路径和 AST 节点
|
||
// - 如果任何访问者返回错误,立即停止处理
|
||
//
|
||
// 步骤 4:AST 遍历
|
||
// - 递归遍历 AST 中的所有声明
|
||
// - 按照访问者接口定义的顺序通知访问者
|
||
// - 处理类型声明、结构体、字段等
|
||
//
|
||
// 步骤 5:文件完成通知
|
||
// - 按顺序通知所有访问者文件处理完成
|
||
// - 访问者可以执行清理和结果收集
|
||
// - 如果任何访问者返回错误,立即停止处理
|
||
//
|
||
// 参数:
|
||
// - filePath: 要遍历的 Go 源文件路径
|
||
//
|
||
// 返回值:
|
||
// - error: 成功时返回 nil,失败时返回包装后的错误信息
|
||
//
|
||
// 错误处理:
|
||
// - 文件不存在或无法读取:返回文件系统错误
|
||
// - 语法错误:返回解析错误,包含行号和列号信息
|
||
// - 访问者错误:返回访问者产生的错误,停止后续处理
|
||
//
|
||
// 使用示例:
|
||
//
|
||
// walker := NewASTWalker()
|
||
// walker.AddVisitor(NewProviderDiscoveryVisitor())
|
||
// err := walker.WalkFile("user_service.go")
|
||
// if err != nil {
|
||
// log.Fatal("Failed to walk file: ", err)
|
||
// }
|
||
//
|
||
// 注意事项:
|
||
// - 必须至少添加一个访问者才能进行有效分析
|
||
// - 文件路径必须是有效的 Go 源文件
|
||
// - 错误会立即终止处理过程
|
||
func (aw *ASTWalker) WalkFile(filePath string) error {
|
||
// === 步骤 1:文件过滤 ===
|
||
// 检查文件是否符合处理条件,跳过不符合要求的文件
|
||
if !aw.shouldProcessFile(filePath) {
|
||
return nil
|
||
}
|
||
|
||
// === 步骤 2:AST 解析 ===
|
||
// 使用 Go 标准库将源文件解析为抽象语法树,保留注释信息
|
||
node, err := parser.ParseFile(aw.fileSet, filePath, nil, parser.ParseComments)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to parse file %s: %w", filePath, err)
|
||
}
|
||
|
||
// === 步骤 3:文件开始通知 ===
|
||
// 按顺序通知所有访问者开始处理文件
|
||
for _, visitor := range aw.visitors {
|
||
if err := visitor.VisitFile(filePath, node); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// === 步骤 4:AST 遍历 ===
|
||
// 递归遍历 AST 的所有节点,通知访问者处理各种声明
|
||
if err := aw.traverseFile(filePath, node); err != nil {
|
||
return err
|
||
}
|
||
|
||
// === 步骤 5:文件完成通知 ===
|
||
// 按顺序通知所有访问者文件处理完成
|
||
for _, visitor := range aw.visitors {
|
||
if err := visitor.Complete(filePath); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// WalkDir 遍历目录中的所有 Go 文件,批量执行 AST 分析
|
||
//
|
||
// 执行流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ WalkDir(dirPath) │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 目录遍历:filepath.Walk() 递归遍历 │
|
||
// │ 2. 目录过滤:跳过隐藏目录和依赖目录 │
|
||
// │ 3. 文件过滤:检查 Go 文件和配置条件 │
|
||
// │ 4. 文件处理:调用 WalkFile() 处理每个文件 │
|
||
// │ 5. 错误处理:记录错误但继续处理其他文件 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 目录过滤规则:
|
||
// - 隐藏目录:以 . 开头的目录(.git、.idea 等)
|
||
// - 依赖目录:node_modules、vendor、testdata
|
||
// - 构建目录:通常被 gitignore 的目录
|
||
//
|
||
// 文件处理策略:
|
||
// - 只处理 .go 后缀的文件
|
||
// - 根据配置跳过测试文件和生成文件
|
||
// - 单个文件处理失败不影响其他文件
|
||
//
|
||
// 错误处理策略:
|
||
// - 目录访问错误:立即停止遍历
|
||
// - 文件处理错误:记录警告但继续处理
|
||
// - 使用标准输出打印警告信息
|
||
//
|
||
// 参数:
|
||
// - dirPath: 要遍历的目录路径
|
||
//
|
||
// 返回值:
|
||
// - error: 目录遍历错误,文件处理错误不会返回
|
||
//
|
||
// 使用场景:
|
||
// - 项目分析:分析整个项目的 Provider 定义
|
||
// - 批量处理:对项目中的所有文件进行统一处理
|
||
// - 代码生成:基于项目分析结果生成代码
|
||
//
|
||
// 注意事项:
|
||
// - 大型项目可能需要较长时间
|
||
// - 建议先配置合适的过滤条件
|
||
// - 错误处理采用宽松策略,不会因为单个文件失败而停止
|
||
func (aw *ASTWalker) WalkDir(dirPath string) error {
|
||
return filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||
// 处理文件系统错误
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// === 处理目录 ===
|
||
if info.IsDir() {
|
||
// 跳过隐藏目录和常见的依赖/构建目录
|
||
if strings.HasPrefix(info.Name(), ".") ||
|
||
info.Name() == "node_modules" ||
|
||
info.Name() == "vendor" ||
|
||
info.Name() == "testdata" {
|
||
return filepath.SkipDir
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// === 处理文件 ===
|
||
// 只处理 Go 文件,且符合配置条件的文件
|
||
if filepath.Ext(path) == ".go" && aw.shouldProcessFile(path) {
|
||
if err := aw.WalkFile(path); err != nil {
|
||
// 单个文件处理失败,记录警告但继续处理其他文件
|
||
fmt.Printf("Warning: failed to process file %s: %v\n", path, err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// traverseFile 遍历已解析文件的 AST,处理所有顶级声明
|
||
//
|
||
// 执行流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ traverseFile(filePath, node) │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 遍历声明:node.Decls 列表 │
|
||
// │ 2. 声明处理:traverseDeclaration() │
|
||
// │ 3. 错误处理:任何声明处理失败都会停止整个遍历 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 处理的声明类型:
|
||
// - GenDecl: 通用声明(type、var、const)
|
||
// - FuncDecl: 函数声明(当前版本跳过)
|
||
// - 其他声明类型:根据需要进行扩展
|
||
//
|
||
// 递归策略:
|
||
// - 深度优先遍历:先处理声明,再处理内部规范
|
||
// - 顺序处理:按照源码中的顺序处理每个声明
|
||
// - 错误传播:任何错误都会立即停止遍历过程
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径,用于错误报告
|
||
// - node: 已解析的 AST 文件节点
|
||
//
|
||
// 返回值:
|
||
// - error: 遍历过程中的错误
|
||
//
|
||
// 注意事项:
|
||
// - 当前版本主要处理类型声明
|
||
// - 函数声明会被跳过,可根据需要扩展
|
||
// - 错误处理采用快速失败策略
|
||
func (aw *ASTWalker) traverseFile(filePath string, node *ast.File) error {
|
||
// === 遍历所有顶级声明 ===
|
||
// ast.File.Decls 包含文件中的所有顶级声明
|
||
for _, decl := range node.Decls {
|
||
// 递归处理每个声明,如果处理失败则立即停止
|
||
if err := aw.traverseDeclaration(filePath, decl); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// traverseDeclaration 遍历单个声明,处理通用声明及其规范
|
||
//
|
||
// 执行流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ traverseDeclaration(filePath, decl) │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 类型检查:decl.(*ast.GenDecl) │
|
||
// │ 2. 访问者通知:VisitGenDecl() 通知访问者 │
|
||
// │ 3. 规范遍历:genDecl.Specs 遍历所有规范 │
|
||
// │ 4. 错误处理:任何步骤失败都会停止处理 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 处理的声明类型:
|
||
// - ast.GenDecl: 通用声明,包含类型、变量、常量声明
|
||
// - ast.FuncDecl: 函数声明(当前跳过)
|
||
// - 其他声明类型:根据需要扩展
|
||
//
|
||
// 通用声明规范类型:
|
||
// - TypeSpec: 类型规范(struct、interface、func type等)
|
||
// - ValueSpec: 值规范(var、const声明)
|
||
// - ImportSpec: 导入规范(import声明)
|
||
//
|
||
// 访问者通知策略:
|
||
// - 声明级通知:先通知所有访问者处理声明
|
||
// - 规范级通知:再递归处理声明中的规范
|
||
// - 错误传播:任何访问者错误都会立即停止处理
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径,用于错误报告
|
||
// - decl: AST 声明节点
|
||
//
|
||
// 返回值:
|
||
// - error: 处理过程中的错误
|
||
//
|
||
// 注意事项:
|
||
// - 当前版本主要处理 GenDecl 类型
|
||
// - FuncDecl 类型会被跳过
|
||
// - 错误处理采用快速失败策略
|
||
func (aw *ASTWalker) traverseDeclaration(filePath string, decl ast.Decl) error {
|
||
// === 类型检查 ===
|
||
// 只处理通用声明(type、var、const),跳过函数声明等
|
||
genDecl, ok := decl.(*ast.GenDecl)
|
||
if !ok {
|
||
// 跳过函数声明和其他非通用声明
|
||
return nil
|
||
}
|
||
|
||
// === 访问者通知 ===
|
||
// 按顺序通知所有访问者处理通用声明
|
||
for _, visitor := range aw.visitors {
|
||
if err := visitor.VisitGenDecl(filePath, genDecl); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// === 规范遍历 ===
|
||
// 递归处理声明中的所有规范(TypeSpec、ValueSpec、ImportSpec等)
|
||
for _, spec := range genDecl.Specs {
|
||
if err := aw.traverseSpec(filePath, spec, genDecl); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// traverseSpec 遍历声明中的规范,处理类型规范和结构体字段
|
||
//
|
||
// 执行流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ traverseSpec(filePath, spec, decl) │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 类型检查:spec.(*ast.TypeSpec) │
|
||
// │ 2. 访问者通知:VisitTypeSpec() 通知访问者 │
|
||
// │ 3. 结构体检查:typeSpec.Type.(*ast.StructType) │
|
||
// │ 4. 结构体通知:VisitStructType() 通知访问者 │
|
||
// │ 5. 字段遍历:traverseStructFields() 遍历字段 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 处理的规范类型:
|
||
// - TypeSpec: 类型规范(当前主要处理)
|
||
// - ValueSpec: 值规范(var、const声明,当前跳过)
|
||
// - ImportSpec: 导入规范(import声明,当前跳过)
|
||
//
|
||
// 类型规范处理:
|
||
// - 结构体类型:特别处理,遍历所有字段
|
||
// - 接口类型:可根据需要扩展处理
|
||
// - 函数类型:可根据需要扩展处理
|
||
// - 其他类型:基本类型、数组、映射等
|
||
//
|
||
// 结构体处理策略:
|
||
// - 类型识别:检查是否为 ast.StructType
|
||
// - 访问者通知:通知所有访问者处理结构体
|
||
// - 字段遍历:递归处理结构体的所有字段
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径,用于错误报告
|
||
// - spec: AST 规范节点
|
||
// - decl: 包含该规范的通用声明
|
||
//
|
||
// 返回值:
|
||
// - error: 处理过程中的错误
|
||
//
|
||
// 注意事项:
|
||
// - 当前版本主要处理 TypeSpec 和 StructType
|
||
// - 其他规范类型会被跳过
|
||
// - 结构体字段会进行深度遍历
|
||
func (aw *ASTWalker) traverseSpec(filePath string, spec ast.Spec, decl *ast.GenDecl) error {
|
||
// === 类型检查 ===
|
||
// 只处理类型规范,跳过值规范和导入规范
|
||
typeSpec, ok := spec.(*ast.TypeSpec)
|
||
if !ok {
|
||
// 跳过非类型规范
|
||
return nil
|
||
}
|
||
|
||
// === 访问者通知 ===
|
||
// 按顺序通知所有访问者处理类型规范
|
||
for _, visitor := range aw.visitors {
|
||
if err := visitor.VisitTypeSpec(filePath, typeSpec, decl); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// === 结构体检查和处理 ===
|
||
// 检查是否为结构体类型,如果是则进行特殊处理
|
||
structType, ok := typeSpec.Type.(*ast.StructType)
|
||
if ok {
|
||
// === 结构体通知 ===
|
||
// 按顺序通知所有访问者处理结构体类型
|
||
for _, visitor := range aw.visitors {
|
||
if err := visitor.VisitStructType(filePath, structType, typeSpec, decl); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// === 字段遍历 ===
|
||
// 递归遍历结构体的所有字段
|
||
if err := aw.traverseStructFields(filePath, structType); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// traverseStructFields 遍历结构体类型中的所有字段
|
||
//
|
||
// 执行流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ traverseStructFields(filePath, structType) │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 空值检查:structType.Fields == nil │
|
||
// │ 2. 字段遍历:structType.Fields.List 遍历所有字段 │
|
||
// │ 3. 访问者通知:VisitStructField() 通知访问者 │
|
||
// │ 4. 错误处理:任何字段处理失败都会停止遍历 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 字段遍历策略:
|
||
// - 顺序遍历:按照源码中的顺序处理每个字段
|
||
// - 批量处理:一个字段列表项可能包含多个同名字段
|
||
// - 深度处理:递归处理字段的类型信息
|
||
//
|
||
// 字段类型分析:
|
||
// - 基本类型:int、string、bool等
|
||
// - 复合类型:数组、切片、映射、通道
|
||
// - 自定义类型:用户定义的结构体、接口
|
||
// - 指针类型:各种类型的指针
|
||
//
|
||
// 访问者处理内容:
|
||
// - 字段名称和类型信息
|
||
// - 结构体标签(tag)解析
|
||
// - 注释和文档信息
|
||
// - 注入相关的标签处理
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径,用于错误报告
|
||
// - structType: 结构体类型节点
|
||
//
|
||
// 返回值:
|
||
// - error: 遍历过程中的错误
|
||
//
|
||
// 注意事项:
|
||
// - 空结构体会被跳过
|
||
// - 字段处理失败会立即停止整个遍历
|
||
// - 匿名字段会被正常处理
|
||
func (aw *ASTWalker) traverseStructFields(filePath string, structType *ast.StructType) error {
|
||
// === 空值检查 ===
|
||
// 如果结构体没有字段列表,直接返回
|
||
if structType.Fields == nil {
|
||
return nil
|
||
}
|
||
|
||
// === 字段遍历 ===
|
||
// 遍历结构体的所有字段,一个 field.List 可能包含多个同名字段
|
||
for _, field := range structType.Fields.List {
|
||
// === 访问者通知 ===
|
||
// 按顺序通知所有访问者处理结构体字段
|
||
for _, visitor := range aw.visitors {
|
||
if err := visitor.VisitStructField(filePath, field, structType); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// shouldProcessFile 判断文件是否符合处理条件
|
||
//
|
||
// 过滤规则:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ shouldProcessFile() │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 扩展名检查:必须是 .go 文件 │
|
||
// │ 2. 测试文件检查:根据配置决定是否处理 _test.go 文件 │
|
||
// │ 3. 生成文件检查:根据配置决定是否处理 .gen.go 文件 │
|
||
// │ 4. 文件大小检查:TODO 待实现(需要 os.Stat) │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 过滤策略:
|
||
// - 严格过滤:只处理符合所有条件的文件
|
||
// - 配置驱动:根据 WalkerConfig 配置决定过滤规则
|
||
// - 性能优化:尽早返回,避免不必要的检查
|
||
//
|
||
// 文件类型识别:
|
||
// - 源文件:.go 后缀,非测试文件,非生成文件
|
||
// - 测试文件:_test.go 后缀
|
||
// - 生成文件:.gen.go 后缀
|
||
// - 其他文件:非 .go 后缀的文件
|
||
//
|
||
// 配置影响:
|
||
// - IncludeTestFiles: true 时处理测试文件
|
||
// - IncludeGeneratedFiles: true 时处理生成文件
|
||
// - MaxFileSize: 超过限制的文件会被跳过
|
||
//
|
||
// 参数:
|
||
// - filePath: 要检查的文件路径
|
||
//
|
||
// 返回值:
|
||
// - bool: true 表示应该处理,false 表示应该跳过
|
||
//
|
||
// 使用场景:
|
||
// - WalkFile: 单文件处理前的检查
|
||
// - WalkDir: 目录遍历中的文件过滤
|
||
// - 批量处理:大规模文件处理时的优化
|
||
//
|
||
// 注意事项:
|
||
// - 文件大小检查尚未实现
|
||
// - 过滤规则是累积的,必须通过所有检查
|
||
// - 配置修改会影响过滤行为
|
||
func (aw *ASTWalker) shouldProcessFile(filePath string) bool {
|
||
// === 扩展名检查 ===
|
||
// 只处理 Go 源文件,其他文件一律跳过
|
||
if filepath.Ext(filePath) != ".go" {
|
||
return false
|
||
}
|
||
|
||
// === 测试文件检查 ===
|
||
// 如果配置不允许处理测试文件,跳过 _test.go 文件
|
||
if !aw.config.IncludeTestFiles && strings.HasSuffix(filePath, "_test.go") {
|
||
return false
|
||
}
|
||
|
||
// === 生成文件检查 ===
|
||
// 如果配置不允许处理生成文件,跳过 .gen.go 文件
|
||
if !aw.config.IncludeGeneratedFiles && strings.HasSuffix(filePath, ".gen.go") {
|
||
return false
|
||
}
|
||
|
||
// === 文件大小检查 ===
|
||
// TODO: 待实现文件大小检查功能(需要调用 os.Stat)
|
||
// 需要考虑性能影响,避免过多的文件系统调用
|
||
|
||
// 所有检查都通过,文件符合处理条件
|
||
return true
|
||
}
|
||
|
||
// GetFileSet 返回遍历器使用的文件集
|
||
//
|
||
// 功能说明:
|
||
// - 返回用于记录源码位置信息的文件集
|
||
// - 文件集包含所有已解析文件的位置信息
|
||
// - 可用于将位置信息转换为行号和列号
|
||
//
|
||
// 返回值:
|
||
// - *token.FileSet: 文件集实例
|
||
//
|
||
// 使用场景:
|
||
// - 错误报告:将 AST 位置转换为人类可读的位置
|
||
// - 调试信息:显示源码位置的详细信息
|
||
// - 工具集成:与其他需要位置信息的工具集成
|
||
//
|
||
// 注意事项:
|
||
// - 文件集会在遍历过程中自动更新
|
||
// - 返回的是实例引用,不是副本
|
||
// - 多个文件的位置信息会累积在同一文件集中
|
||
func (aw *ASTWalker) GetFileSet() *token.FileSet {
|
||
return aw.fileSet
|
||
}
|
||
|
||
// GetCommentParser 返回遍历器使用的注释解析器
|
||
//
|
||
// 功能说明:
|
||
// - 返回用于解析注释的注释解析器实例
|
||
// - 注释解析器负责处理 @provider 等注解
|
||
// - 可用于独立的注释解析任务
|
||
//
|
||
// 返回值:
|
||
// - *CommentParser: 注释解析器实例
|
||
//
|
||
// 使用场景:
|
||
// - 注解分析:解析特定的注释格式
|
||
// - 工具扩展:基于注释解析器实现自定义功能
|
||
// - 调试和测试:测试注释解析功能
|
||
//
|
||
// 注意事项:
|
||
// - 注释解析器的配置与遍历器保持一致
|
||
// - 返回的是实例引用,不是副本
|
||
// - 可以用于遍历器外部的注释解析任务
|
||
func (aw *ASTWalker) GetCommentParser() *CommentParser {
|
||
return aw.commentParser
|
||
}
|
||
|
||
// GetConfig 返回遍历器的配置信息
|
||
//
|
||
// 功能说明:
|
||
// - 返回遍历器的当前配置
|
||
// - 配置包含所有影响遍历行为的参数
|
||
// - 可用于检查和修改遍历器配置
|
||
//
|
||
// 返回值:
|
||
// - *WalkerConfig: 遍历器配置指针
|
||
//
|
||
// 使用场景:
|
||
// - 配置检查:查看当前的过滤和处理规则
|
||
// - 动态调整:在运行时修改配置
|
||
// - 配置复制:基于当前配置创建新的遍历器
|
||
//
|
||
// 注意事项:
|
||
// - 返回的是配置的指针,修改会影响遍历器行为
|
||
// - 建议在了解配置含义的情况下进行修改
|
||
// - 配置修改不会影响已处理的文件
|
||
|
||
// ProviderDiscoveryVisitor 实现 NodeVisitor 接口,专门用于发现 Provider 注解
|
||
//
|
||
// 架构设计:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ ProviderDiscoveryVisitor │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ commentParser: *CommentParser - 注释解析器 │
|
||
// │ providers: []Provider - 发现的 Provider 列表 │
|
||
// │ currentFile: string - 当前处理的文件路径 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 核心功能:
|
||
// - 注解识别:识别 @provider 注解
|
||
// - Provider 构建:构建完整的 Provider 对象
|
||
// - 结果收集:收集所有发现的 Provider
|
||
// - 文件跟踪:跟踪当前处理的文件
|
||
//
|
||
// 处理策略:
|
||
// - 结构体优先:只处理结构体类型的注解
|
||
// - 注释解析:使用 CommentParser 解析注释块
|
||
// - 默认值处理:为空值设置合理的默认值
|
||
//
|
||
// 使用场景:
|
||
// - Provider 发现:在 AST 遍历过程中发现 Provider 注解
|
||
// - 代码生成:为代码生成工具提供输入数据
|
||
// - 项目分析:分析项目中的 Provider 定义
|
||
type ProviderDiscoveryVisitor struct {
|
||
commentParser *CommentParser // 注释解析器,用于解析 @provider 注解
|
||
providers []Provider // 发现的 Provider 列表
|
||
currentFile string // 当前处理的文件路径
|
||
}
|
||
|
||
// NewProviderDiscoveryVisitor 创建新的 ProviderDiscoveryVisitor 实例
|
||
//
|
||
// 初始化流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ NewProviderDiscoveryVisitor() │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 保存注释解析器:commentParser 参数 │
|
||
// │ 2. 初始化 Provider 列表:make([]Provider, 0) │
|
||
// │ 3. 创建访问者实例:&ProviderDiscoveryVisitor{...} │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 参数处理:
|
||
// - commentParser: 必须传入有效的注释解析器实例
|
||
// - 不允许为 nil,否则会出现运行时错误
|
||
//
|
||
// 初始化状态:
|
||
// - providers: 空列表,准备收集发现的 Provider
|
||
// - currentFile: 空字符串,等待文件处理时设置
|
||
//
|
||
// 参数:
|
||
// - commentParser: 注释解析器实例
|
||
//
|
||
// 返回值:
|
||
// - *ProviderDiscoveryVisitor: 配置好的访问者实例
|
||
//
|
||
// 使用示例:
|
||
//
|
||
// commentParser := NewCommentParser()
|
||
// visitor := NewProviderDiscoveryVisitor(commentParser)
|
||
// walker := NewASTWalker()
|
||
// walker.AddVisitor(visitor)
|
||
//
|
||
// 注意事项:
|
||
// - 注释解析器必须提前初始化
|
||
// - 访问者可以重复使用,但需要调用 Reset 清理状态
|
||
// - 建议在遍历开始前添加到 ASTWalker
|
||
func NewProviderDiscoveryVisitor(commentParser *CommentParser) *ProviderDiscoveryVisitor {
|
||
return &ProviderDiscoveryVisitor{
|
||
commentParser: commentParser,
|
||
providers: make([]Provider, 0),
|
||
}
|
||
}
|
||
|
||
// VisitFile 实现 NodeVisitor.VisitFile 接口,处理文件级别的初始化
|
||
//
|
||
// 功能说明:
|
||
// - 记录当前处理的文件路径
|
||
// - 为文件级别的处理提供上下文
|
||
// - 不执行具体的发现逻辑
|
||
//
|
||
// 执行时机:
|
||
// - 在开始处理新文件时调用
|
||
// - 在任何 AST 遍历之前调用
|
||
// - 为后续的处理步骤建立上下文
|
||
//
|
||
// 处理策略:
|
||
// - 简单记录:只保存文件路径,不进行复杂处理
|
||
// - 快速返回:立即返回 nil,不产生错误
|
||
// - 上下文建立:为后续的 VisitStructType 提供文件上下文
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前处理的文件路径
|
||
// - node: 解析后的 AST 文件节点
|
||
//
|
||
// 返回值:
|
||
// - error: 总是返回 nil,表示成功
|
||
//
|
||
// 注意事项:
|
||
// - 此方法主要建立上下文,不执行具体业务逻辑
|
||
// - 文件路径会被后续的方法使用
|
||
// - 错误处理在其他方法中实现
|
||
func (pdv *ProviderDiscoveryVisitor) VisitFile(filePath string, node *ast.File) error {
|
||
// 记录当前处理的文件路径,为后续处理提供上下文
|
||
pdv.currentFile = filePath
|
||
return nil
|
||
}
|
||
|
||
// VisitGenDecl 实现 NodeVisitor.VisitGenDecl 接口,处理通用声明
|
||
//
|
||
// 功能说明:
|
||
// - 当前实现不处理通用声明级别的 Provider 发现
|
||
// - Provider 发现主要在结构体级别进行
|
||
// - 保留接口完整性,为未来扩展做准备
|
||
//
|
||
// 处理策略:
|
||
// - 空实现:不执行任何处理逻辑
|
||
// - 快速返回:立即返回 nil,继续后续处理
|
||
// - 接口一致性:保持与 NodeVisitor 接口的一致性
|
||
//
|
||
// 扩展可能性:
|
||
// - 包级别 Provider 注解的处理
|
||
// - 常量和变量级别的注解处理
|
||
// - 声明级别的元数据收集
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - decl: 通用声明节点
|
||
//
|
||
// 返回值:
|
||
// - error: 总是返回 nil,表示成功
|
||
//
|
||
// 注意事项:
|
||
// - 当前版本不处理声明级别的注解
|
||
// - 未来版本可能添加相关功能
|
||
// - 保持接口完整性以便扩展
|
||
func (pdv *ProviderDiscoveryVisitor) VisitGenDecl(filePath string, decl *ast.GenDecl) error {
|
||
// 当前实现不处理通用声明级别的注解
|
||
// Provider 发现主要在结构体级别进行
|
||
return nil
|
||
}
|
||
|
||
// VisitTypeSpec 实现 NodeVisitor.VisitTypeSpec 接口,处理类型规范
|
||
//
|
||
// 功能说明:
|
||
// - 当前实现不在类型规范级别处理 Provider 发现
|
||
// - 主要在结构体类型级别进行详细处理
|
||
// - 保留接口完整性,为未来扩展做准备
|
||
//
|
||
// 处理策略:
|
||
// - 空实现:不执行任何处理逻辑
|
||
// - 快速返回:立即返回 nil,继续后续处理
|
||
// - 结构体优先:将详细处理留给 VisitStructType
|
||
//
|
||
// 设计考虑:
|
||
// - 类型规范级别的处理可能包含接口、函数类型等
|
||
// - Provider 注解通常与结构体类型关联
|
||
// - 避免重复处理,让专门的 VisitStructType 处理
|
||
//
|
||
// 扩展可能性:
|
||
// - 接口类型的 Provider 注解处理
|
||
// - 函数类型的注解处理
|
||
// - 类型级别的元数据收集
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - typeSpec: 类型规范节点
|
||
// - decl: 包含该类型的通用声明
|
||
//
|
||
// 返回值:
|
||
// - error: 总是返回 nil,表示成功
|
||
//
|
||
// 注意事项:
|
||
// - 当前版本不处理类型规范级别的注解
|
||
// - 结构体类型会在 VisitStructType 中处理
|
||
// - 保持接口完整性以便扩展
|
||
func (pdv *ProviderDiscoveryVisitor) VisitTypeSpec(filePath string, typeSpec *ast.TypeSpec, decl *ast.GenDecl) error {
|
||
// 当前实现不在类型规范级别处理 Provider 发现
|
||
// 结构体类型的处理在 VisitStructType 中进行
|
||
return nil
|
||
}
|
||
|
||
// VisitStructType 实现 NodeVisitor.VisitStructType 接口,处理结构体类型的 Provider 注解
|
||
//
|
||
// 执行流程:
|
||
// ┌─────────────────────────────────────────────────────────────┐
|
||
// │ VisitStructType(filePath, structType, ...) │
|
||
// ├─────────────────────────────────────────────────────────────┤
|
||
// │ 1. 注释检查:decl.Doc != nil && len(decl.Doc.List) > 0 │
|
||
// │ 2. 注释提取:收集所有注释行到 commentLines │
|
||
// │ 3. 注解解析:commentParser.ParseCommentBlock() │
|
||
// │ 4. Provider 构建:创建 Provider 对象 │
|
||
// │ 5. 默认值设置:为空字段设置合理的默认值 │
|
||
// │ 6. 结果收集:添加到 providers 列表 │
|
||
// └─────────────────────────────────────────────────────────────┘
|
||
//
|
||
// 注解发现策略:
|
||
// - 文档检查:检查结构体声明前的文档注释
|
||
// - 注释收集:收集所有相关的注释行
|
||
// - 专业解析:使用 CommentParser 进行专业解析
|
||
// - 结果验证:检查解析结果的有效性
|
||
//
|
||
// Provider 构建逻辑:
|
||
// - 基本信息:结构体名称、模式、分组、返回类型
|
||
// - 初始化:创建空的注入参数和导入映射
|
||
// - 默认值:为返回类型设置默认值(*结构体名)
|
||
// - 扩展性:为后续的字段处理预留接口
|
||
//
|
||
// 错误处理策略:
|
||
// - 解析错误:如果解析失败,跳过该结构体
|
||
// - 空结果:如果没有发现注解,不创建 Provider
|
||
// - 静默处理:不产生错误,继续处理其他结构体
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - structType: 结构体类型节点
|
||
// - typeSpec: 对应的类型规范
|
||
// - decl: 包含该结构体的通用声明
|
||
//
|
||
// 返回值:
|
||
// - error: 总是返回 nil,表示成功
|
||
//
|
||
// 注意事项:
|
||
// - 只处理带有文档注释的结构体
|
||
// - 注解解析失败不会产生错误
|
||
// - 发现的 Provider 会被收集到内部列表
|
||
func (pdv *ProviderDiscoveryVisitor) VisitStructType(filePath string, structType *ast.StructType, typeSpec *ast.TypeSpec, decl *ast.GenDecl) error {
|
||
// === 注释检查 ===
|
||
// 检查结构体是否有文档注释,没有注释则跳过处理
|
||
if decl.Doc != nil && len(decl.Doc.List) > 0 {
|
||
// === 注释提取 ===
|
||
// 提取所有注释行,为后续的注解解析做准备
|
||
commentLines := make([]string, len(decl.Doc.List))
|
||
for i, comment := range decl.Doc.List {
|
||
commentLines[i] = comment.Text
|
||
}
|
||
|
||
// === 注解解析 ===
|
||
// 使用注释解析器解析注释块,查找 @provider 注解
|
||
providerComment, err := pdv.commentParser.ParseCommentBlock(commentLines)
|
||
if err == nil && providerComment != nil {
|
||
// === Provider 构建 ===
|
||
// 创建 Provider 对象,设置基本信息
|
||
provider := Provider{
|
||
StructName: typeSpec.Name.Name, // 结构体名称
|
||
Mode: providerComment.Mode, // Provider 模式
|
||
ProviderGroup: providerComment.Group, // Provider 分组
|
||
ReturnType: providerComment.ReturnType, // 返回类型
|
||
InjectParams: make(map[string]InjectParam), // 注入参数映射
|
||
Imports: make(map[string]string), // 导入包映射
|
||
}
|
||
|
||
// === 默认值设置 ===
|
||
// 如果没有指定返回类型,设置默认值为结构体指针
|
||
if provider.ReturnType == "" {
|
||
provider.ReturnType = "*" + provider.StructName
|
||
}
|
||
|
||
// === 结果收集 ===
|
||
// 将构建的 Provider 添加到发现列表
|
||
pdv.providers = append(pdv.providers, provider)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// VisitStructField 实现 NodeVisitor.VisitStructField 接口,处理结构体字段
|
||
//
|
||
// 功能说明:
|
||
// - 当前实现不处理字段级别的详细信息
|
||
// - 字段处理在后续的 Provider 构建阶段进行
|
||
// - 保留接口完整性,为未来扩展做准备
|
||
//
|
||
// 当前职责:
|
||
// - 接口实现:保持与 NodeVisitor 接口的一致性
|
||
// - 扩展预留:为字段级别的处理预留接口
|
||
// - 简单返回:不执行复杂处理逻辑
|
||
//
|
||
// 未来可能的扩展:
|
||
// - 字段类型解析:解析字段的类型信息
|
||
// - 标签处理:处理 struct tags,特别是 inject 标签
|
||
// - 依赖分析:分析字段之间的依赖关系
|
||
// - 验证检查:验证字段配置的正确性
|
||
//
|
||
// 字段处理策略:
|
||
// - 注入标签:识别 inject:"true" 和 inject:"false" 标签
|
||
// - 类型解析:解析复杂类型,包括指针、切片、映射等
|
||
// - 嵌入字段:处理匿名字段和嵌入结构体
|
||
// - 文档注释:处理字段级别的文档注释
|
||
//
|
||
// 参数:
|
||
// - filePath: 当前文件路径
|
||
// - field: 结构体字段节点
|
||
// - structType: 所属的结构体类型
|
||
//
|
||
// 返回值:
|
||
// - error: 总是返回 nil,表示成功
|
||
//
|
||
// 注意事项:
|
||
// - 当前版本不进行字段级别的处理
|
||
// - 字段处理逻辑在其他模块中实现
|
||
// - 保持接口完整性以便未来扩展
|
||
func (pdv *ProviderDiscoveryVisitor) VisitStructField(filePath string, field *ast.Field, structType *ast.StructType) error {
|
||
// 字段级别处理预留接口
|
||
// 当前版本不在此处进行字段处理,相关逻辑在 Provider 构建阶段实现
|
||
// 未来可能的扩展:
|
||
// - 字段类型解析
|
||
// - inject 标签处理
|
||
// - 依赖关系分析
|
||
return nil
|
||
}
|
||
|
||
// Complete 实现 NodeVisitor.Complete 接口,处理文件完成事件
|
||
//
|
||
// 功能说明:
|
||
// - 当前实现不处理文件完成事件
|
||
// - 主要用于清理和结果汇总工作
|
||
// - 保留接口完整性,为未来扩展做准备
|
||
//
|
||
// 当前职责:
|
||
// - 接口实现:保持与 NodeVisitor 接口的一致性
|
||
// - 快速返回:不执行复杂处理逻辑
|
||
// - 扩展预留:为文件完成处理预留接口
|
||
//
|
||
// 未来可能的扩展:
|
||
// - 结果验证:验证文件级别的处理结果
|
||
// - 统计收集:收集文件处理的统计信息
|
||
// - 错误报告:汇总文件处理过程中的错误
|
||
// - 缓存清理:清理文件级别的临时数据
|
||
//
|
||
// 文件完成处理策略:
|
||
// - 静默完成:不产生错误,继续后续处理
|
||
// - 结果保持:保持已发现的 Provider 结果
|
||
// - 状态重置:为下一个文件的处理准备状态
|
||
//
|
||
// 参数:
|
||
// - filePath: 已完成处理的文件路径
|
||
//
|
||
// 返回值:
|
||
// - error: 总是返回 nil,表示成功
|
||
//
|
||
// 注意事项:
|
||
// - 当前版本不进行文件完成处理
|
||
// - 文件路径参数可用于扩展功能
|
||
// - 保持接口完整性以便未来扩展
|
||
func (pdv *ProviderDiscoveryVisitor) Complete(filePath string) error {
|
||
// 文件完成处理预留接口
|
||
// 当前版本不进行特殊的完成处理
|
||
// 未来可能的扩展:
|
||
// - 文件级别结果验证
|
||
// - 统计信息收集
|
||
// - 缓存清理工作
|
||
return nil
|
||
}
|
||
|
||
// GetProviders 返回发现的 Provider 列表
|
||
//
|
||
// 功能说明:
|
||
// - 返回在 AST 遍历过程中发现的所有 Provider
|
||
// - 返回的是内部列表的副本,外部修改不影响内部状态
|
||
// - 包含完整的 Provider 信息,包括基本属性和配置
|
||
//
|
||
// 返回数据特征:
|
||
// - 完整性:包含所有发现的 Provider,没有遗漏
|
||
// - 顺序性:按照发现顺序排列,保持源码中的顺序
|
||
// - 结构化:每个 Provider 包含完整的信息结构
|
||
//
|
||
// 数据内容:
|
||
// - 基本信息:StructName、Mode、ProviderGroup、ReturnType
|
||
// - 注入信息:InjectParams 映射
|
||
// - 导入信息:Imports 映射
|
||
// - 其他属性:NeedPrepareFunc、ProviderFile 等
|
||
//
|
||
// 使用场景:
|
||
// - 代码生成:基于发现的 Provider 生成代码
|
||
// - 项目分析:分析项目中的 Provider 定义
|
||
// - 工具集成:与其他工具集成处理 Provider 信息
|
||
// - 调试测试:验证 Provider 发现的正确性
|
||
//
|
||
// 返回值:
|
||
// - []Provider: 发现的 Provider 列表
|
||
//
|
||
// 注意事项:
|
||
// - 返回的是实际列表,不是副本
|
||
// - 空列表表示没有发现任何 Provider
|
||
// - 外部修改会影响内部状态
|
||
func (pdv *ProviderDiscoveryVisitor) GetProviders() []Provider {
|
||
return pdv.providers
|
||
}
|
||
|
||
// Reset 清理已发现的 Provider,重置访问者状态
|
||
//
|
||
// 功能说明:
|
||
// - 清空已发现的 Provider 列表
|
||
// - 重置当前文件路径
|
||
// - 重置访问者到初始状态,可以重新使用
|
||
//
|
||
// 重置策略:
|
||
// - 列表重置:创建新的空 Provider 列表
|
||
// - 路径重置:将当前文件路径设为空字符串
|
||
// - 状态清理:清理所有与文件处理相关的状态
|
||
//
|
||
// 使用场景:
|
||
// - 重复使用:在多次遍历中重复使用同一个访问者
|
||
// - 状态清理:清理之前的处理结果,避免混淆
|
||
// - 测试环境:在单元测试中重置状态
|
||
// - 批量处理:在批量处理多个目录时重置状态
|
||
//
|
||
// 重置前后对比:
|
||
// - 重置前:providers 包含之前发现的结果,currentFile 包含上一个文件
|
||
// - 重置后:providers 为空列表,currentFile 为空字符串
|
||
//
|
||
// 注意事项:
|
||
// - 重置操作不可逆,之前的结果会丢失
|
||
// - 重置后可以安全地用于新的遍历任务
|
||
// - 建议在开始新的遍历前调用 Reset
|
||
func (pdv *ProviderDiscoveryVisitor) Reset() {
|
||
// 清空已发现的 Provider 列表
|
||
pdv.providers = make([]Provider, 0)
|
||
// 重置当前文件路径
|
||
pdv.currentFile = ""
|
||
}
|