Skip to content

ClassDefs: support embed#563

Merged
xushiwei merged 5 commits intogoplus:mainfrom
xushiwei:q
Jan 19, 2026
Merged

ClassDefs: support embed#563
xushiwei merged 5 commits intogoplus:mainfrom
xushiwei:q

Conversation

@xushiwei
Copy link
Member

No description provided.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @xushiwei, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the internal ClassDefs mechanism to introduce robust support for embedded fields in Go structs. By transitioning from direct field management to a callback-based approach for adding fields, the system can now accurately represent and initialize embedded types. This change improves the flexibility and correctness of struct definition generation, particularly for complex type compositions.

Highlights

  • Refactored ClassDefs Structure: The ClassDefs struct has been refactored to remove direct storage of Fields and Tags. Instead, it now utilizes an addFld callback function, providing a more flexible mechanism for handling field definitions.
  • Support for Embedded Fields: The NewAndInit method within ClassDefs has been updated to correctly identify and process embedded fields. This includes logic to determine the appropriate name for embedded types and to use the new addFld callback for their inclusion.
  • New embedName Utility Function: A new embedName function has been introduced in ast.go. This utility is responsible for extracting the correct name of an embedded type, handling various type representations such as pointers, basic types, named types, and aliases.
  • Enhanced Test Coverage: Existing test cases have been updated to align with the new ClassDefs structure and addFld callback. Additionally, new tests (TestClassDefsEmbedNoInit and TestClassDefsEmbedInit) have been added to specifically validate the functionality of embedded fields, both with and without initialization.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors ClassDefs to support embedded fields. The Fields and Tags slices have been removed from the ClassDefs struct and replaced with an addFld callback function, which decouples field management from ClassDefs. This is a good design improvement. The NewAndInit method is updated to handle embedded fields, and new tests have been added to cover this functionality. My main feedback is to add a check in NewAndInit to enforce the documented contract that fn and typ cannot both be nil.

func (p *ClassDefs) NewAndInit(fn F, tag string, pos token.Pos, typ types.Type, names ...string) {
// initializes them by `fn` (can be nil). `typ` and `fn` can not be both nil.
func (p *ClassDefs) NewAndInit(fn F, pos token.Pos, typ types.Type, names ...string) {
pkg := p.pkg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The function comment on line 596 states that typ and fn cannot both be nil. However, the function body does not enforce this, which could lead to addFld being called with a nil type, potentially causing issues later. It's good practice to enforce this contract at the beginning of the function.

	if fn == nil && typ == nil {
		panic("gogen.NewAndInit: fn and typ cannot both be nil")
	}
	pkg := p.pkg

@codecov
Copy link

codecov bot commented Jan 18, 2026

Codecov Report

❌ Patch coverage is 95.83333% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.03%. Comparing base (c6835d6) to head (d7a6163).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
ast.go 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #563      +/-   ##
==========================================
- Coverage   96.03%   96.03%   -0.01%     
==========================================
  Files          24       24              
  Lines        6717     6727      +10     
==========================================
+ Hits         6451     6460       +9     
- Misses        198      199       +1     
  Partials       68       68              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

return t.Obj().Name()
}
return ""
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing nil validation and interface type handling

The embedName function should validate the input and handle interface types explicitly:

  1. Nil check: If typ is nil, this will panic on type assertion
  2. Interface types: Named interfaces are handled by *types.Named, but unnamed interface types (e.g., interface{ Read() }) will silently return "", which could cause issues
  3. Nil Obj() check: t.Obj() could potentially return nil for Named/Alias types

Recommendation:

func embedName(typ types.Type) string {
	if typ == nil {
		return ""
	}
	if t, ok := typ.(*types.Pointer); ok {
		typ = t.Elem()
	}
	switch typ := typ.(type) {
	case *types.Basic:
		return typ.Name()
	case *types.Named:
		if obj := typ.Obj(); obj != nil {
			return obj.Name()
		}
	case *typesalias.Alias:
		if obj := typ.Obj(); obj != nil {
			return obj.Name()
		}
	}
	// Return empty or panic for unsupported types
	return ""
}

// NewAndInit creates variables with specified `typ` (can be nil) and `names`, and
// initializes them by `fn` (can be nil).
func (p *ClassDefs) NewAndInit(fn F, tag string, pos token.Pos, typ types.Type, names ...string) {
// initializes them by `fn` (can be nil). `typ` and `fn` can not be both nil.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear documentation for parameter constraints

The current documentation is ambiguous. Based on the implementation:

  • When fn == nil: typ must NOT be nil
  • When fn != nil: typ CAN be nil (inferred from initialization)

Recommendation:

// NewAndInit creates variables with specified `typ` (can be nil) and `names`, and
// initializes them by `fn` (can be nil). When `fn` is nil (no initialization),
// `typ` must not be nil. When names is empty, creates an embedded field.

Also consider enforcing this programmatically:

if typ == nil && fn == nil {
	panic("NewAndInit: typ and fn cannot both be nil")
}

p.Fields = append(p.Fields, fld)
p.Tags = append(p.Tags, tag)
if embed {
p.addFld(0, embedName(typ), typ, true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential nil function pointer dereference

The code calls p.addFld() without validating that p.addFld is non-nil. If ClassDefsStart is called with addFld = nil, this will cause a panic.

Recommendation: Add validation in ClassDefsStart:

func (p *Package) ClassDefsStart(
	recv *types.Var,
	addFld func(idx int, name string, typ types.Type, embed bool),
) *ClassDefs {
	if addFld == nil {
		panic("ClassDefsStart: addFld callback must not be nil")
	}
	// ...
}

scope := cb.current.scope
recvName := ident(recv.Name())
if embed {
names = []string{embedName(typ)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate embedName result

When embedName(typ) returns an empty string (for unsupported types), creating a field with an empty name could violate Go's struct field naming requirements.

Recommendation:

if embed {
	name := embedName(typ)
	if name == "" {
		panic("invalid embedded type: cannot determine field name")
	}
	names = []string{name}
}

) *ClassDefs {
if debugInstr {
log.Println("ClassDefsStart")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing parameter documentation

The addFld callback parameter needs documentation explaining its purpose and parameters:

Recommendation:

// ClassDefsStart starts a classfile fields declaration block.
// The addFld callback is invoked for each field added via NewAndInit:
//   - idx: field index within the current NewAndInit call
//   - name: field name (or type name for embedded fields)
//   - typ: field type
//   - embed: true if this is an embedded field
// recv is the receiver variable for the generated XGo_Init method.
// Should call (*ClassDefs).End() to end it.

@xgopilot
Copy link
Contributor

xgopilot bot commented Jan 18, 2026

Code Review Summary

This PR successfully adds embedded field support to ClassDefs through a clean callback-based refactoring. The implementation is solid with good test coverage, and the architectural change from direct field storage to callbacks improves flexibility.

Key strengths:

  • Clean separation of concerns with the callback pattern
  • Comprehensive test coverage for both initialized and uninitialized embedded fields
  • Reduced struct size (~40% smaller memory footprint)
  • Eliminated redundant scope.Lookup() operations

Main concerns to address:

  • Missing nil validation in embedName() and callback invocation
  • Documentation needs clarification for parameter constraints
  • Empty string return from embedName() should be validated before field creation

All specific issues have been commented inline. Overall, this is a well-executed refactoring that needs minor hardening around edge cases.

@xushiwei xushiwei merged commit 04985cb into goplus:main Jan 19, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant