Skip to content

Conversation

phoban01
Copy link

This change enables support for exporting aliased package names from source cue to go code.

The special case of the "_" import alias is explicitly ignored.

Fixes #3974

This change enables support for exporting aliased package names from
source cue to go code.

The special case of the "_" import alias is explicitly ignored.

Fixes cue-lang#3974

Signed-off-by: Piaras Hoban <phoban01@gmail.com>
@phoban01 phoban01 requested a review from cueckoo as a code owner August 14, 2025 10:58
Copy link
Member

@mvdan mvdan left a comment

Choose a reason for hiding this comment

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

Thank you for the patch! This is roughly in line with what I was thinking, and thank you for writing tests. I think a couple of edge cases still need to be ironed out, though.


#ObjectMeta: {}

-- cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue --
Copy link
Member

Choose a reason for hiding this comment

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

could you rewrite this test so it doesn't use cue.mod/gen? e.g. make these sub-packages inside the same module.

if ok {
quotedPath = fmt.Sprintf("%s %s", alias, quotedPath)
}
printf("\t%s\n", quotedPath)
Copy link
Member

Choose a reason for hiding this comment

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

I'd avoid the two-layered formatting, like:

if alias, ok := g.importAliases[path]; ok {
    printf("\t%s %q\n", alias, path)
} else {
    printf("\t%q\n", path)
}

@@ -323,6 +340,7 @@ func (g *generator) emitType(val cue.Value, optionalStg optionalStrategy) (typeF
if err != nil {
return facts, fmt.Errorf("cannot parse @go type expression: %w", err)
}

Copy link
Member

Choose a reason for hiding this comment

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

please avoid whitespace changes elsewhere, as they are more likely to cause git conflicts down the line

@@ -679,11 +697,15 @@ func goValueAttr(val cue.Value) cue.Attribute {

// goPkgNameForInstance determines what to name a Go package generated from a CUE instance.
// By default this is the CUE package name, but it can be overriden by a @go() package attribute.
func goPkgNameForInstance(inst *build.Instance, instVal cue.Value) string {
// Import aliases will also checked at this point and inserted if found.
func goPkgNameForInstance(inst *build.Instance, instVal cue.Value, aliasLookup map[string]string) string {
Copy link
Member

Choose a reason for hiding this comment

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

name the parameter importAliases for consistency.

@@ -679,11 +697,15 @@ func goValueAttr(val cue.Value) cue.Attribute {

// goPkgNameForInstance determines what to name a Go package generated from a CUE instance.
// By default this is the CUE package name, but it can be overriden by a @go() package attribute.
func goPkgNameForInstance(inst *build.Instance, instVal cue.Value) string {
// Import aliases will also checked at this point and inserted if found.
Copy link
Member

Choose a reason for hiding this comment

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

I'd reword this slightly, like:

When supplying importAliases, and if no package attribute is found,
the returned package name reflects the alias name that the package is being imported as.

@@ -100,7 +109,7 @@ func Generate(ctx *cue.Context, insts ...*build.Instance) error {
buf = fmt.Appendf(buf, format, args...)
}
printf("// Code generated by \"cue exp gengotypes\"; DO NOT EDIT.\n\n")
goPkgName := goPkgNameForInstance(inst, instVal)
goPkgName := goPkgNameForInstance(inst, instVal, g.importAliases)
Copy link
Member

Choose a reason for hiding this comment

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

this is rather crucial - you must NOT use importAliases here, so you should use a nil map instead. this tracks which Go packages (by name) we generate per directory, as we can only generate one package per directory. This is entirely unrelated to what aliases a package is imported under.

@@ -217,6 +231,9 @@ type generator struct {

// def tracks the generation state for a single CUE definition.
def *generatedDef

// importAliases maps package names to the alias
importAliases map[string]string
Copy link
Member

Choose a reason for hiding this comment

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

Note that we organize fields on a per-package and per-definition basis. This should be on a per-package basis I assume.

@@ -772,3 +794,32 @@ func emitDocs(printf func(string, ...any), name string, groups []*ast.CommentGro
}
}
}

// gatherImportAliases collects the aliases from imports across the instance.
// We explicitly ignore "_" aliases because CUE does not support.
Copy link
Member

Choose a reason for hiding this comment

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

CUE source code should never be using underscore-aliased imports, so I would delete this line as well as the check for _ below - it just shouldn't happen

if !ok {
continue
}
for _, s := range impDecl.Specs {
Copy link
Member

Choose a reason for hiding this comment

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

Loop directly over f.Imports, which will save you a handful of lines

@@ -217,6 +231,9 @@ type generator struct {

// def tracks the generation state for a single CUE definition.
def *generatedDef

// importAliases maps package names to the alias
Copy link
Member

Choose a reason for hiding this comment

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

this needs clarifying - is it to the first alias that we found for each package? I reckon it should be, but then we should document it as such, and update the code to ignore any second or third alias for the same package path.

but also, what happens if file f1.cue does import same "foo.com/bar/p1", and f2.cue does import same "foo.com/bar/p2"? As far as I can see, you'll generate Go code which imports both packages aliased as same.

I think collecting the original aliases is a good idea, because more often than not it will lead to better results - the user will most likely have chosen reasonable aliases. But you'll need some sort of strategy to deal with conflicts, and add a test for that edge case.

I would personally suggest to track which aliases are already taken, and when you encounter an aliased import where the alias is already taken, you can add a number at the end - e.g. the second same above could become same2.

Copy link
Member

Choose a reason for hiding this comment

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

It's also worth noting that you can have duplicates between non-aliased package imports and aliased imports too, so that must also have a test and be checked for duplicates. For example, import "foo.com/bar/same" in f1.cue, and import same "foo.com/bar/p2" in f2.cue.

@mvdan mvdan self-assigned this Aug 29, 2025
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.

cmd/cue: exp gengotypes fails to handle conflicting imports
2 participants