@@ -15,6 +15,7 @@ import (
15
15
"github.com/docker/docker-language-server/internal/types"
16
16
"github.com/hashicorp/hcl/v2"
17
17
"github.com/hashicorp/hcl/v2/hclsyntax"
18
+ "github.com/moby/buildkit/frontend/dockerfile/parser"
18
19
"github.com/moby/buildkit/solver/errdefs"
19
20
)
20
21
@@ -112,13 +113,23 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder
112
113
}
113
114
}
114
115
115
- body , ok := doc .(document.BakeHCLDocument ).File ().Body .(* hclsyntax.Body )
116
+ bakeDoc := doc .(document.BakeHCLDocument )
117
+ body , ok := bakeDoc .File ().Body .(* hclsyntax.Body )
116
118
if ! ok {
117
119
return diagnostics
118
120
}
119
121
122
+ targetDockerfiles := map [string ]string {}
123
+ dockerfileContent := map [string ][]* parser.Node {}
124
+ for _ , b := range body .Blocks {
125
+ if b .Type == "target" && len (b .Labels ) == 1 {
126
+ dockerfilePath , _ := bakeDoc .DockerfileForTarget (b )
127
+ targetDockerfiles [b .Labels [0 ]] = dockerfilePath
128
+ }
129
+ }
130
+
120
131
for _ , block := range body .Blocks {
121
- if block .Type == "target" {
132
+ if block .Type == "target" && len ( block . Labels ) == 1 {
122
133
if _ , ok := block .Body .Attributes ["dockerfile-inline" ]; ok {
123
134
if attribute , ok := block .Body .Attributes ["dockerfile" ]; ok {
124
135
diagnostics = append (diagnostics , protocol.Diagnostic {
@@ -206,15 +217,24 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder
206
217
}
207
218
}
208
219
209
- dockerfilePath , err := doc .(document. BakeHCLDocument ) .DockerfileForTarget (block )
220
+ dockerfilePath , err := bakeDoc .DockerfileForTarget (block )
210
221
if dockerfilePath == "" || err != nil {
211
222
continue
212
223
}
213
224
214
225
if attribute , ok := block .Body .Attributes ["target" ]; ok {
215
226
if expr , ok := attribute .Expr .(* hclsyntax.TemplateExpr ); ok && len (expr .Parts ) == 1 {
216
227
if literalValueExpr , ok := expr .Parts [0 ].(* hclsyntax.LiteralValueExpr ); ok {
217
- diagnostic := c .checkTargetTarget (dockerfilePath , expr , literalValueExpr , source )
228
+ dockerfile := targetDockerfiles [block .Labels [0 ]]
229
+ if dockerfile == "" {
230
+ dockerfileContent ["" ] = nil
231
+ }
232
+ nodes , ok := dockerfileContent [dockerfile ]
233
+ if ! ok {
234
+ _ , nodes = document .OpenDockerfile (context .Background (), c .docs , dockerfilePath )
235
+ dockerfileContent [block .Labels [0 ]] = nodes
236
+ }
237
+ diagnostic := c .checkTargetTarget (nodes , expr , literalValueExpr , source )
218
238
if diagnostic != nil {
219
239
diagnostics = append (diagnostics , * diagnostic )
220
240
}
@@ -224,7 +244,31 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder
224
244
225
245
if attribute , ok := block .Body .Attributes ["args" ]; ok {
226
246
if expr , ok := attribute .Expr .(* hclsyntax.ObjectConsExpr ); ok {
227
- argsDiagnostics := c .checkTargetArgs (dockerfilePath , input , expr , source )
247
+ args := make (map [string ]struct {})
248
+ for _ , b := range body .Blocks {
249
+ if b .Type == "target" && len (b .Labels ) == 1 && b .Labels [0 ] != block .Labels [0 ] {
250
+ parents , _ := bakeDoc .ParentTargets (b .Labels [0 ])
251
+ if slices .Contains (parents , block .Labels [0 ]) {
252
+ dockerfile := targetDockerfiles [b .Labels [0 ]]
253
+ if dockerfile == "" {
254
+ dockerfileContent ["" ] = nil
255
+ }
256
+ nodes , ok := dockerfileContent [dockerfile ]
257
+ if ! ok {
258
+ _ , nodes = document .OpenDockerfile (context .Background (), c .docs , dockerfile )
259
+ dockerfileContent [dockerfile ] = nodes
260
+ }
261
+ c .collectARGs (nodes , args )
262
+ }
263
+ }
264
+ }
265
+
266
+ nodes , ok := dockerfileContent [dockerfilePath ]
267
+ if ! ok {
268
+ _ , nodes = document .OpenDockerfile (context .Background (), c .docs , dockerfilePath )
269
+ dockerfileContent [dockerfilePath ] = nodes
270
+ }
271
+ argsDiagnostics := c .checkTargetArgs (nodes , input , expr , source , args )
228
272
diagnostics = append (diagnostics , argsDiagnostics ... )
229
273
}
230
274
}
@@ -233,10 +277,7 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder
233
277
return diagnostics
234
278
}
235
279
236
- // checkTargetArgs examines the args attribute of a target block.
237
- func (c * BakeHCLDiagnosticsCollector ) checkTargetArgs (dockerfilePath string , input []byte , expr * hclsyntax.ObjectConsExpr , source string ) []protocol.Diagnostic {
238
- _ , nodes := document .OpenDockerfile (context .Background (), c .docs , dockerfilePath )
239
- args := []string {}
280
+ func (c * BakeHCLDiagnosticsCollector ) collectARGs (nodes []* parser.Node , args map [string ]struct {}) {
240
281
for _ , child := range nodes {
241
282
if strings .EqualFold (child .Value , "ARG" ) {
242
283
child = child .Next
@@ -246,12 +287,16 @@ func (c *BakeHCLDiagnosticsCollector) checkTargetArgs(dockerfilePath string, inp
246
287
if idx != - 1 {
247
288
value = value [:idx ]
248
289
}
249
- args = append ( args , value )
290
+ args [ value ] = struct {}{}
250
291
child = child .Next
251
292
}
252
293
}
253
294
}
295
+ }
254
296
297
+ // checkTargetArgs examines the args attribute of a target block.
298
+ func (c * BakeHCLDiagnosticsCollector ) checkTargetArgs (nodes []* parser.Node , input []byte , expr * hclsyntax.ObjectConsExpr , source string , args map [string ]struct {}) []protocol.Diagnostic {
299
+ c .collectARGs (nodes , args )
255
300
diagnostics := []protocol.Diagnostic {}
256
301
for _ , item := range expr .Items {
257
302
start := item .KeyExpr .Range ().Start .Byte
@@ -264,27 +309,23 @@ func (c *BakeHCLDiagnosticsCollector) checkTargetArgs(dockerfilePath string, inp
264
309
if slices .Contains (builtinArgs , arg ) {
265
310
continue
266
311
}
267
-
268
- diagnostic := checkStringLiteral (
269
- source ,
270
- arg ,
271
- fmt .Sprintf ("'%v' not defined as an ARG in your Dockerfile" , arg ),
272
- args ,
273
- item .KeyExpr .Range (),
274
- )
275
-
276
- if diagnostic != nil {
312
+ if _ , ok := args [arg ]; ! ok {
313
+ diagnostic := createDiagnostic (
314
+ source ,
315
+ fmt .Sprintf ("'%v' not defined as an ARG in your Dockerfile" , arg ),
316
+ item .KeyExpr .Range (),
317
+ )
277
318
diagnostics = append (diagnostics , * diagnostic )
278
319
}
320
+
279
321
}
280
322
return diagnostics
281
323
}
282
324
283
- func (c * BakeHCLDiagnosticsCollector ) checkTargetTarget (dockerfilePath string , expr * hclsyntax.TemplateExpr , literalValueExpr * hclsyntax.LiteralValueExpr , source string ) * protocol.Diagnostic {
325
+ func (c * BakeHCLDiagnosticsCollector ) checkTargetTarget (nodes [] * parser. Node , expr * hclsyntax.TemplateExpr , literalValueExpr * hclsyntax.LiteralValueExpr , source string ) * protocol.Diagnostic {
284
326
value , _ := literalValueExpr .Value (& hcl.EvalContext {})
285
327
target := value .AsString ()
286
328
287
- _ , nodes := document .OpenDockerfile (context .Background (), c .docs , dockerfilePath )
288
329
found := false
289
330
for _ , child := range nodes {
290
331
if strings .EqualFold (child .Value , "FROM" ) {
@@ -330,7 +371,10 @@ func checkStringLiteral(diagnosticSource, attributeValue, message string, expect
330
371
if slices .Contains (expectedValues , attributeValue ) {
331
372
return nil
332
373
}
374
+ return createDiagnostic (diagnosticSource , message , attributeRange )
375
+ }
333
376
377
+ func createDiagnostic (diagnosticSource , message string , attributeRange hcl.Range ) * protocol.Diagnostic {
334
378
return & protocol.Diagnostic {
335
379
Message : message ,
336
380
Source : types .CreateStringPointer (diagnosticSource ),
0 commit comments