Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@xushiwei
Copy link
Member

@xushiwei xushiwei commented Oct 6, 2025

@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 enhances the xgo compiler by adding support for keyword arguments (kwargs) in function calls. It modifies the compileCallExpr function to preprocess kwargs, converting them into a form compatible with existing argument processing logic. This transformation involves creating composite literals for struct or map types based on the expected parameter type, thereby expanding the flexibility of function call syntax within xgo.

Highlights

  • Keyword Argument Support: Introduced the core logic to recognize and process keyword arguments (kwargs) in function calls within the xgo/cl compiler.
  • Argument Transformation: Implemented a mechanism to transform keyword arguments into standard positional arguments, specifically as composite literals for structs or string maps, before the call expression is compiled.
  • Type-Specific Merging: Added functions (mergeKwargs, mergeStringMapKwargs, mergeStructKwargs) to handle the merging of keyword arguments based on the target parameter's type, supporting structs, pointers to structs, and map[string]T.
  • Struct Field Name Resolution: Included a helper function (getFldName) to correctly resolve struct field names from keyword argument identifiers, including handling capitalization for unexported fields.
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.

@xushiwei
Copy link
Member Author

xushiwei commented Oct 6, 2025

/review

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 introduces support for keyword arguments in function calls, a significant enhancement. The implementation correctly handles merging keyword arguments into struct and map types for both variadic and non-variadic functions. However, there are a few areas that need improvement. The code currently uses panic with "TODO" messages for unhandled cases, which should be replaced with proper compile-time error reporting to avoid crashing the compiler and to provide better feedback to users. Additionally, there's a potential issue in how the closing brace position of generated composite literals is calculated, which could affect source mapping and debugging tools. My review includes suggestions to address these points.

cl/expr.go Outdated
Comment on lines 721 to 723
if idx < 0 || len(v.Args) < idx {
panic("TODO: no kwargs or arguments not enough")
}
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 use of panic with a "TODO" message is not suitable for production code. This should be replaced with proper error handling that provides a clear compile-time error to the user. The current logic doesn't handle variadic functions without non-variadic parameters (e.g., func(...T)), leading to a panic. This case should be handled gracefully, for instance, by disallowing keyword arguments for such functions.

Suggested change
if idx < 0 || len(v.Args) < idx {
panic("TODO: no kwargs or arguments not enough")
}
if idx < 0 {
panic(ctx.newCodeErrorf(v.Pos(), v.End(), "keyword arguments are not supported for a function with only variadic parameters"))
}
if len(v.Args) < idx {
panic(ctx.newCodeErrorf(v.Pos(), v.End(), "not enough arguments for function call with keyword arguments"))
}

cl/expr.go Outdated
return mergeStringMapKwargs(kwargs) // map[string]T
}
}
panic("TODO: unexpected kwargs type")
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Using panic with a "TODO" message for unhandled cases is not robust. It should be replaced with proper error handling that reports a clear compile-time error, indicating which types are supported for keyword argument merging.

Suggested change
panic("TODO: unexpected kwargs type")
panic(ctx.newCodeErrorf(kwargs[0].Pos(), kwargs[len(kwargs)-1].End(), "keyword arguments can only be used for struct, pointer to struct, or map[string]T types, but got %v", t))

cl/expr.go Outdated
return &ast.CompositeLit{
Lbrace: kwargs[0].Pos(),
Elts: elts,
Rbrace: kwargs[n-1].End() - 1,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The calculation of the Rbrace position for the generated CompositeLit as kwargs[n-1].End() - 1 is likely incorrect. The End() method on an AST node typically returns the position after the node, so subtracting 1 would point to the last character of the last keyword argument's value. A more correct position for the closing brace would be kwargs[n-1].End(), representing the position immediately after the keyword arguments. This ensures better source mapping for tools that consume this AST.

Suggested change
Rbrace: kwargs[n-1].End() - 1,
Rbrace: kwargs[n-1].End(),

cl/expr.go Outdated
return &ast.CompositeLit{
Lbrace: kwargs[0].Pos(),
Elts: elts,
Rbrace: kwargs[n-1].End() - 1,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The calculation of the Rbrace position for the generated CompositeLit as kwargs[n-1].End() - 1 is likely incorrect. The End() method on an AST node typically returns the position after the node, so subtracting 1 would point to the last character of the last keyword argument's value. A more correct position for the closing brace would be kwargs[n-1].End(), representing the position immediately after the keyword arguments. This ensures better source mapping for tools that consume this AST.

Suggested change
Rbrace: kwargs[n-1].End() - 1,
Rbrace: kwargs[n-1].End(),

@niupilot
Copy link
Contributor

niupilot bot commented Oct 6, 2025

Code Review Summary

Great work on implementing kwargs support! The implementation is functionally sound with zero runtime overhead. Here are the key findings across code quality, performance, documentation, and security:

🔴 Critical Issues

Error handling needs improvement - Generic panic messages should be replaced with context-rich errors for better debugging.

Security concern - Potential out-of-bounds access without validation in helper functions.

📝 Key Recommendations

  1. Replace TODO panics with descriptive error messages including position info
  2. Add validation for edge cases (empty kwargs, nil return from fn.arg())
  3. Document the kwargs transformation algorithm, especially for variadic functions
  4. Add defensive bounds checking in merge functions

✅ Strengths

  • Clean separation of concerns
  • Zero runtime overhead (compile-time only transformation)
  • Smart auto-capitalization for field names
  • Efficient O(k) complexity for typical use cases

Detailed inline comments follow with specific line references.

cl/expr.go Outdated
if fn.variadic { // has variadic parameter
idx := fn.size - 1
if idx < 0 || len(v.Args) < idx {
panic("TODO: no kwargs or arguments not enough")
Copy link
Contributor

Choose a reason for hiding this comment

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

Critical: Inadequate error handling

Replace this TODO panic with a descriptive error message:

Suggested change
panic("TODO: no kwargs or arguments not enough")
panic(ctx.newCodeErrorf(v.Pos(), v.End(), "keyword arguments cannot be used when insufficient positional arguments are provided before variadic parameter"))

This provides better context for debugging and matches the codebase's error reporting pattern.

cl/expr.go Outdated
copy(args[idx+1:], v.Args[idx:])
} else {
copy(args, v.Args)
args[n] = mergeKwargs(v.Kwargs, fn.arg(n, false))
Copy link
Contributor

Choose a reason for hiding this comment

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

Critical: Missing validation before merge

fn.arg(n, false) can return nil when n exceeds the valid parameter range (see line 611). This will cause mergeKwargs to panic with a generic error. Add validation:

Suggested change
args[n] = mergeKwargs(v.Kwargs, fn.arg(n, false))
argType := fn.arg(n, false)
if argType == nil {
panic(ctx.newCodeErrorf(v.Kwargs[0].Pos(), v.End(), "too many arguments including keyword arguments"))
}
args[n] = mergeKwargs(v.Kwargs, argType)

cl/expr.go Outdated
return mergeStringMapKwargs(kwargs) // map[string]T
}
}
panic("TODO: unexpected kwargs type")
Copy link
Contributor

Choose a reason for hiding this comment

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

High: Improve error message with type information

Include the actual type encountered to help debugging:

Suggested change
panic("TODO: unexpected kwargs type")
panic(fmt.Sprintf("keyword arguments not supported for parameter type %v", t))

Also consider using ctx.newCodeErrorf if position information is available from the kwargs.

}

func mergeStringMapKwargs(kwargs []*ast.KwargExpr) ast.Expr {
n := len(kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Security: Add bounds checking

Accessing kwargs[0] and kwargs[n-1] without validating the slice is non-empty could cause a panic. While the caller currently checks len(v.Kwargs) > 0, add defensive validation:

Suggested change
n := len(kwargs)
func mergeStringMapKwargs(kwargs []*ast.KwargExpr) ast.Expr {
n := len(kwargs)
if n == 0 {
panic("internal error: empty kwargs slice")
}
elts := make([]ast.Expr, n)

Same issue exists in mergeStructKwargs at line 785.

cl/expr.go Outdated
panic(err)
}

func mergeKwargs(kwargs []*ast.KwargExpr, t types.Type) ast.Expr {
Copy link
Contributor

Choose a reason for hiding this comment

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

Documentation: Add function documentation

This is a key function for the kwargs feature. Please add a doc comment:

Suggested change
func mergeKwargs(kwargs []*ast.KwargExpr, t types.Type) ast.Expr {
// mergeKwargs converts keyword arguments into an appropriate composite literal
// based on the target parameter type:
// - *struct or struct: creates a struct literal with field names
// - map[string]T: creates a map literal with string keys
// - other types: panics with error message
//
// The function handles automatic field name capitalization for unexported struct fields.
func mergeKwargs(kwargs []*ast.KwargExpr, t types.Type) ast.Expr {

fnt := pfn.Type
fn := &fnType{}
fn.load(fnt)
if len(v.Kwargs) > 0 { // https://github.com/goplus/xgo/issues/2443
Copy link
Contributor

Choose a reason for hiding this comment

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

Documentation: Add algorithm overview

The kwargs transformation logic is complex, especially for variadic functions. Add a comment explaining the overall approach:

Suggested change
if len(v.Kwargs) > 0 { // https://github.com/goplus/xgo/issues/2443
if len(v.Kwargs) > 0 { // https://github.com/goplus/xgo/issues/2443
// Process keyword arguments by converting them into a composite literal argument.
// For variadic functions: kwargs are inserted at the last required parameter position
// For non-variadic functions: kwargs are appended as an additional argument
// The mergeKwargs function transforms kwargs based on target parameter type.
n := len(v.Args)

cl/expr.go Outdated
}
}

func getFldName(name *ast.Ident, t *types.Struct) *ast.Ident {
Copy link
Contributor

Choose a reason for hiding this comment

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

Documentation: Explain field resolution behavior

Add documentation for this important function that handles Python-style naming conventions:

Suggested change
func getFldName(name *ast.Ident, t *types.Struct) *ast.Ident {
// getFldName resolves a kwarg name to the actual struct field name.
// It handles three cases:
// 1. Exact match: kwarg name matches struct field name exactly
// 2. Capitalization match: unexported kwarg name matches capitalized exported field
// (e.g., "cache" matches "Cache")
// 3. Fallback: returns original name if no match found (type checker will report error)
//
// This allows Python-style lowercase kwargs to map to Go's exported field conventions.
func getFldName(name *ast.Ident, t *types.Struct) *ast.Ident {

cl/expr.go Outdated
if fn.variadic { // has variadic parameter
idx := fn.size - 1
if idx < 0 || len(v.Args) < idx {
panic("TODO: no kwargs or arguments not enough")
Copy link
Contributor

Choose a reason for hiding this comment

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

Replace panic with descriptive error message:

return ctx.newCodeErrorf(v.Pos(), v.End(), 
    "keyword arguments require at least %d positional arguments before variadic parameter, got %d", 
    idx, len(v.Args))

Issue: The current generic panic "TODO: no kwargs or arguments not enough" makes debugging extremely difficult. Users need to know:

  1. Which function call failed
  2. How many arguments were expected
  3. How many were provided

Using ctx.newCodeErrorf provides position information and clear guidance.

cl/expr.go Outdated
return mergeStringMapKwargs(kwargs) // map[string]T
}
}
panic("TODO: unexpected kwargs type")
Copy link
Contributor

Choose a reason for hiding this comment

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

Replace panic with specific error message:

return ctx.newCodeErrorf(kwargs[0].Pos(), kwargs[len(kwargs)-1].End(),
    "keyword arguments require parameter type to be struct, *struct, or map[string]T, got %v", t)

Issue: The current "TODO: unexpected kwargs type" doesn't tell users:

  • What type was encountered
  • What types work with kwargs

This improved message clearly states the supported types and shows what was actually provided.

Comment on lines 716 to 734
if len(v.Kwargs) > 0 { // https://github.com/goplus/xgo/issues/2443
n := len(v.Args)
args := make([]ast.Expr, n+1)
if fn.variadic { // has variadic parameter
idx := fn.size - 1
if idx < 0 || len(v.Args) < idx {
panic("TODO: no kwargs or arguments not enough")
}
copy(args, v.Args[:idx])
args[idx] = mergeKwargs(v.Kwargs, fn.params.At(idx).Type())
copy(args[idx+1:], v.Args[idx:])
} else {
copy(args, v.Args)
args[n] = mergeKwargs(v.Kwargs, fn.arg(n, false))
}
ne := *v
ne.Args, ne.Kwargs = args, nil
v = &ne
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Add comprehensive comment explaining the kwargs transformation algorithm:

// Transform kwargs into composite literal arguments based on parameter type.
// For variadic functions: kwargs are inserted before the variadic parameter position.
// For non-variadic functions: kwargs are appended as an additional argument.
// The mergeKwargs function handles type-specific transformations (struct/map).
// https://github.com/goplus/xgo/issues/2443
if len(v.Kwargs) > 0 {
    // ... existing code
}

Issue: The current code only has a GitHub issue reference but doesn't explain:

  1. Why variadic and non-variadic functions are handled differently
  2. What the insertion strategy is
  3. What mergeKwargs does

This documentation is critical for future maintainers to understand the algorithm and edge cases.

cl/expr.go Outdated
Comment on lines 751 to 765
func mergeKwargs(kwargs []*ast.KwargExpr, t types.Type) ast.Expr {
switch t := t.Underlying().(type) {
case *types.Pointer:
if t, ok := t.Elem().Underlying().(*types.Struct); ok {
return mergeStructKwargs(kwargs, t)
}
case *types.Struct:
return mergeStructKwargs(kwargs, t)
case *types.Map:
if t, ok := t.Key().Underlying().(*types.Basic); ok && t.Kind() == types.String {
return mergeStringMapKwargs(kwargs) // map[string]T
}
}
panic("TODO: unexpected kwargs type")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Add function documentation:

// mergeKwargs converts keyword arguments into an appropriate composite literal
// based on the target parameter type:
//   - struct or *struct: creates a struct literal with field names (via mergeStructKwargs)
//   - map[string]T: creates a map literal with string keys (via mergeStringMapKwargs)
// Returns a CompositeLit expression that can be used as a regular function argument.
func mergeKwargs(kwargs []*ast.KwargExpr, t types.Type) ast.Expr {

Issue: The function lacks documentation explaining what it does and how it decides which merge strategy to use. This is a key function in the kwargs implementation and needs clear documentation for maintainability.

Comment on lines 767 to 781
func mergeStringMapKwargs(kwargs []*ast.KwargExpr) ast.Expr {
n := len(kwargs)
elts := make([]ast.Expr, n)
for i, arg := range kwargs {
elts[i] = &ast.KeyValueExpr{
Key: toBasicLit(arg.Name),
Value: arg.Value,
}
}
return &ast.CompositeLit{
Lbrace: kwargs[0].Pos(),
Elts: elts,
Rbrace: kwargs[n-1].End() - 1,
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Add function documentation with example:

// mergeStringMapKwargs converts keyword arguments into a map[string]T composite literal.
// Each kwarg name becomes a string key in the resulting map literal.
// Example: foo(a: 1, b: 2) becomes foo(map[string]int{"a": 1, "b": 2})
func mergeStringMapKwargs(kwargs []*ast.KwargExpr) ast.Expr {

Issue: This function has no documentation explaining the transformation. An example helps clarify the behavior.

cl/expr.go Outdated
Comment on lines 799 to 815
func getFldName(name *ast.Ident, t *types.Struct) *ast.Ident {
var capName string
exported := name.IsExported()
if !exported {
capName = stringutil.Capitalize(name.Name)
}
for i, n := 0, t.NumFields(); i < n; i++ {
fld := t.Field(i)
if fld.Name() == name.Name {
return name
}
if !exported && fld.Exported() && fld.Name() == capName {
return &ast.Ident{NamePos: name.NamePos, Name: capName}
}
}
return name // fallback to origin name
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Add comprehensive documentation for field name resolution:

// getFldName resolves a kwarg identifier to the actual struct field name.
// It handles three cases:
//   1. Exact match: kwarg name matches struct field name exactly
//   2. Capitalization match: unexported kwarg name matches exported field when capitalized
//      (e.g., "cache" matches "Cache", allowing Python-style lowercase kwargs)
//   3. Fallback: returns original name if no match (type checker will report error later)
func getFldName(name *ast.Ident, t *types.Struct) *ast.Ident {

Issue: This function implements important field name resolution logic that allows Python-style lowercase kwargs to map to Go's exported field conventions. Without documentation, the capitalization behavior is mysterious and could cause confusion.

The documentation explains all three cases and the fallback strategy, making the code maintainable and the behavior predictable.

@codecov
Copy link

codecov bot commented Oct 7, 2025

Codecov Report

❌ Patch coverage is 92.85714% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.23%. Comparing base (378ee7b) to head (7f48cb1).
⚠️ Report is 14 commits behind head on main.

Files with missing lines Patch % Lines
cl/expr.go 92.85% 4 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2448      +/-   ##
==========================================
- Coverage   93.23%   93.23%   -0.01%     
==========================================
  Files          34       34              
  Lines       11515    11599      +84     
==========================================
+ Hits        10736    10814      +78     
- Misses        616      620       +4     
- Partials      163      165       +2     

☔ 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.

@xushiwei
Copy link
Member Author

xushiwei commented Oct 7, 2025

/review

xushiwei and others added 2 commits October 7, 2025 09:00
Co-authored-by: niupilot[bot] <230321281+niupilot[bot]@users.noreply.github.com>
@xushiwei
Copy link
Member Author

xushiwei commented Oct 7, 2025

/review

@niupilot
Copy link
Contributor

niupilot bot commented Oct 7, 2025

Code Review Complete

Critical Issues Found

1. Missing nil check (cl/expr.go:732)

High Severity: fn.arg(n, false) can return nil, causing panic in mergeKwargs.

Fix: Add validation before the call:

argType := fn.arg(n, false)
if argType == nil {
    panic(ctx.newCodeErrorf(v.Pos(), v.End(), "too many arguments including keyword arguments"))
}
args[n] = mergeKwargs(ctx, v, argType)

2. Unsafe array access (cl/expr.go:785, 801)

Medium Severity: Functions mergeStringMapKwargs and mergeStructKwargs access kwargs[0] and kwargs[n-1] without checking if slice is empty.

Fix: Add defensive check at start of both functions:

if len(kwargs) == 0 {
    panic("internal error: empty kwargs slice")
}

Design Concerns

  • Type fallback: Silent fallback to map[string]T when type is nil/unsupported could mask errors
  • Documentation: Missing docs for new functions explaining transformation behavior
  • Test coverage: 92.85% patch coverage, may miss error path edge cases

Strengths

  • Zero runtime overhead (compile-time transformation)
  • Smart field name resolution supporting Python-style kwargs
  • Clean separation of concerns

Recommendation

Address the critical nil check issue before merging.


👉 Want me to re-run this review? Type /review! CodeAgent Repository

@xushiwei xushiwei merged commit 42bcd93 into goplus:main Oct 7, 2025
28 of 29 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