diff --git a/CHANGELOG.md b/CHANGELOG.md index 52fc2b6..4d2fc89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ All notable changes to the Docker Language Server will be documented in this fil - stop volume named references from causing volume attributes to not be suggested ([#309](https://github.com/docker/docker-language-server/issues/309)) - textDocument/documentLink - ensure the image attribute is valid before trying to process it for document links ([#306](https://github.com/docker/docker-language-server/issues/306)) +- Bake + - textDocument/definition + - fix nil pointers when navigating around a top level attribute that is not in any block ([#311](https://github.com/docker/docker-language-server/issues/311)) ## [0.10.2] - 2025-06-06 diff --git a/internal/bake/hcl/definition.go b/internal/bake/hcl/definition.go index af6003f..e6c61b6 100644 --- a/internal/bake/hcl/definition.go +++ b/internal/bake/hcl/definition.go @@ -70,7 +70,7 @@ func ResolveAttributeValue(ctx context.Context, definitionLinkSupport bool, mana if tupleConsExpr, ok := attribute.Expr.(*hclsyntax.TupleConsExpr); ok { for _, e := range tupleConsExpr.Exprs { if isInsideRange(e.Range(), position) { - if templateExpr, ok := e.(*hclsyntax.TemplateExpr); ok { + if templateExpr, ok := e.(*hclsyntax.TemplateExpr); ok && sourceBlock != nil { if templateExpr.IsStringLiteral() { // look up a target reference if it's inside a // target block's inherits attribute, or a @@ -169,7 +169,7 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager if objectConsExpression, ok := expression.(*hclsyntax.ObjectConsExpr); ok { for _, item := range objectConsExpression.Items { - if isInsideRange(item.KeyExpr.Range(), position) { + if isInsideRange(item.KeyExpr.Range(), position) && sourceBlock != nil { if attributeName == "args" && sourceBlock.Type == "target" { dockerfilePath, err := doc.DockerfileForTarget(sourceBlock) if dockerfilePath == "" || err != nil { diff --git a/internal/bake/hcl/definition_test.go b/internal/bake/hcl/definition_test.go index 95e7423..a5ec1aa 100644 --- a/internal/bake/hcl/definition_test.go +++ b/internal/bake/hcl/definition_test.go @@ -982,6 +982,99 @@ func TestDefinition(t *testing.T) { }, }, }, + { + name: "attribute whitespace should resolve to nothing", + content: "a1 = \"value\"", + line: 0, + character: 3, + endCharacter: -1, + locations: nil, + links: nil, + }, + { + name: "attribute array string value", + content: "a1 = [\"value\"]", + line: 0, + character: 10, + endCharacter: -1, + locations: nil, + links: nil, + }, + { + name: "attribute array string has a templated variable", + content: "a1 = \"\"\na2 = [\"${a1}\"]", + line: 1, + character: 10, + endCharacter: -1, + locations: []protocol.Location{ + { + URI: bakeFileURI, + Range: protocol.Range{ + Start: protocol.Position{Line: 0, Character: 0}, + End: protocol.Position{Line: 0, Character: 2}, + }, + }, + }, + links: []protocol.LocationLink{ + { + OriginSelectionRange: &protocol.Range{ + Start: protocol.Position{Line: 1, Character: 9}, + End: protocol.Position{Line: 1, Character: 11}, + }, + TargetURI: bakeFileURI, + TargetRange: protocol.Range{ + Start: protocol.Position{Line: 0, Character: 0}, + End: protocol.Position{Line: 0, Character: 2}, + }, + TargetSelectionRange: protocol.Range{ + Start: protocol.Position{Line: 0, Character: 0}, + End: protocol.Position{Line: 0, Character: 2}, + }, + }, + }, + }, + { + name: "attribute object value's attribute name", + content: "args = { var = value }", + line: 0, + character: 11, + endCharacter: -1, + locations: nil, + links: nil, + }, + { + name: "attribute object value's attribute name", + content: "a1 = \"\"\nargs = { var = a1 }", + line: 1, + character: 16, + endCharacter: -1, + locations: []protocol.Location{ + { + URI: bakeFileURI, + Range: protocol.Range{ + Start: protocol.Position{Line: 0, Character: 0}, + End: protocol.Position{Line: 0, Character: 2}, + }, + }, + }, + links: []protocol.LocationLink{ + { + OriginSelectionRange: &protocol.Range{ + Start: protocol.Position{Line: 1, Character: 15}, + End: protocol.Position{Line: 1, Character: 17}, + }, + TargetURI: bakeFileURI, + TargetRange: protocol.Range{ + Start: protocol.Position{Line: 0, Character: 0}, + End: protocol.Position{Line: 0, Character: 2}, + }, + TargetSelectionRange: protocol.Range{ + Start: protocol.Position{Line: 0, Character: 0}, + End: protocol.Position{Line: 0, Character: 2}, + }, + }, + }, + }, { name: "variable referenced in for loop conditional", content: "variable num { default = 3 }\nvariable varList { default = [\"tag\"] }\ntarget default {\n tags = [for var in varList : upper(var) if num > 2]\n}",