diff --git a/dap/adapter.go b/dap/adapter.go index eb579e33150b..7ced02dce1ad 100644 --- a/dap/adapter.go +++ b/dap/adapter.go @@ -589,6 +589,7 @@ func (b *breakpointMap) Set(fname string, sbps []dap.SourceBreakpoint) (breakpoi EndLine: sbp.Line, Column: sbp.Column, EndColumn: sbp.Column, + Reason: "pending", } } breakpoints = append(breakpoints, bp) @@ -608,6 +609,27 @@ func (b *breakpointMap) Intersect(ctx Context, src *pb.Source, ws string) map[di digests[digest.Digest(dgst)] = id } } + + // Mark unverified breakpoints as failed at this point since we couldn't find an area + // in the source where they applied. + for _, info := range src.Infos { + fname := filepath.Join(ws, info.Filename) + + bps := b.byPath[fname] + for _, bp := range bps { + if !bp.Verified && bp.Reason != "failed" { + bp.Reason = "failed" + + ctx.C() <- &dap.BreakpointEvent{ + Event: dap.Event{Event: "breakpoint"}, + Body: dap.BreakpointEventBody{ + Reason: "changed", + Breakpoint: bp, + }, + } + } + } + } return digests } @@ -651,6 +673,7 @@ func (b *breakpointMap) intersect(ctx Context, src *pb.Source, locs *pb.Location bp.Column = int(r.Start.Character) bp.EndColumn = int(r.End.Character) bp.Verified = true + bp.Reason = "" ctx.C() <- &dap.BreakpointEvent{ Event: dap.Event{Event: "breakpoint"}, diff --git a/go.mod b/go.mod index 5712a1c88526..9811d341efcb 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/docker/docker v28.5.2+incompatible github.com/docker/go-units v0.5.0 github.com/gofrs/flock v0.13.0 - github.com/google/go-dap v0.12.0 + github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928 diff --git a/go.sum b/go.sum index 327dda5284ea..57e62b0bcee4 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnL github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-dap v0.12.0 h1:rVcjv3SyMIrpaOoTAdFDyHs99CwVOItIJGKLQFQhNeM= -github.com/google/go-dap v0.12.0/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc= +github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b h1:m+yLjBIoXaMKk6pwd1IJYYgM76+mwcy7J9+cuY7LqmQ= +github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= diff --git a/tests/dap_build.go b/tests/dap_build.go index 7ebc3752feb7..ce7f169b6748 100644 --- a/tests/dap_build.go +++ b/tests/dap_build.go @@ -77,6 +77,7 @@ var dapBuildTests = []func(t *testing.T, sb integration.Sandbox){ testDapBuild, testDapBuildStopOnEntry, testDapBuildSetBreakpoints, + testDapBuildVerifiedBreakpoints, testDapBuildStepIn, testDapBuildStepNext, testDapBuildStepOut, @@ -199,6 +200,56 @@ func testDapBuildSetBreakpoints(t *testing.T, sb integration.Sandbox) { require.NoError(t, done(false)) } +func testDapBuildVerifiedBreakpoints(t *testing.T, sb integration.Sandbox) { + dir := createTestProject(t) + client, done, err := dapBuildCmd(t, sb, withArgs(dir)) + require.NoError(t, err) + + interruptCh := pollInterruptEvents(client) + + var actual []dap.BreakpointEventBody + client.RegisterEvent("breakpoint", func(em dap.EventMessage) { + e := em.(*dap.BreakpointEvent) + actual = append(actual, e.Body) + }) + + doLaunch(t, client, commands.LaunchConfig{ + Dockerfile: path.Join(dir, "Dockerfile"), + ContextPath: dir, + }, + dap.SourceBreakpoint{Line: 2}, + dap.SourceBreakpoint{Line: 10}, + ) + + stopped := waitForInterrupt[*dap.StoppedEvent](t, interruptCh) + require.NotNil(t, stopped) + + assert.Equal(t, []dap.BreakpointEventBody{ + { + Reason: "changed", + Breakpoint: dap.Breakpoint{ + Id: 1, + Line: 2, + EndLine: 2, + Verified: true, + }, + }, + { + Reason: "changed", + Breakpoint: dap.Breakpoint{ + Id: 2, + Line: 10, + EndLine: 10, + Verified: false, + Reason: "failed", + }, + }, + }, actual) + + var exitErr *exec.ExitError + require.ErrorAs(t, done(true), &exitErr) +} + func testDapBuildStepIn(t *testing.T, sb integration.Sandbox) { dir := createTestProject(t) client, done, err := dapBuildCmd(t, sb, withArgs(dir)) diff --git a/vendor/github.com/google/go-dap/schematypes.go b/vendor/github.com/google/go-dap/schematypes.go index 1f360c9a8b6b..4fc2255b6031 100644 --- a/vendor/github.com/google/go-dap/schematypes.go +++ b/vendor/github.com/google/go-dap/schematypes.go @@ -112,7 +112,8 @@ type ErrorResponseBody struct { // CancelRequest: The `cancel` request is used by the client in two situations: // - to indicate that it is no longer interested in the result produced by a specific request issued earlier -// - to cancel a progress sequence. Clients should only call this request if the corresponding capability `supportsCancelRequest` is true. +// - to cancel a progress sequence. +// Clients should only call this request if the corresponding capability `supportsCancelRequest` is true. // This request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honoring this request but there are no guarantees. // The `cancel` request may return an error if it could not cancel an operation but a client should refrain from presenting this error to end users. // The request that got cancelled still needs to send a response back. This can either be a normal result (`success` attribute true) or an error response (`success` attribute false and the `message` set to `cancelled`). @@ -232,6 +233,7 @@ type OutputEventBody struct { Line int `json:"line,omitempty"` Column int `json:"column,omitempty"` Data json.RawMessage `json:"data,omitempty"` + LocationReference int `json:"locationReference,omitempty"` } // BreakpointEvent: The event indicates that some information about a breakpoint has changed. @@ -456,6 +458,7 @@ type InitializeRequestArguments struct { SupportsMemoryEvent bool `json:"supportsMemoryEvent,omitempty"` SupportsArgsCanBeInterpretedByShell bool `json:"supportsArgsCanBeInterpretedByShell,omitempty"` SupportsStartDebuggingRequest bool `json:"supportsStartDebuggingRequest,omitempty"` + SupportsANSIStyling bool `json:"supportsANSIStyling,omitempty"` } // InitializeResponse: Response to `initialize` request. @@ -655,8 +658,7 @@ type SetFunctionBreakpointsResponseBody struct { Breakpoints []Breakpoint `json:"breakpoints"` } -// SetExceptionBreakpointsRequest: The request configures the debugger's response to thrown exceptions. -// If an exception is configured to break, a `stopped` event is fired (with reason `exception`). +// SetExceptionBreakpointsRequest: The request configures the debugger's response to thrown exceptions. Each of the `filters`, `filterOptions`, and `exceptionOptions` in the request are independent configurations to a debug adapter indicating a kind of exception to catch. An exception thrown in a program should result in a `stopped` event from the debug adapter (with reason `exception`) if any of the configured filters match. // Clients should only call this request if the corresponding capability `exceptionBreakpointFilters` returns one or more filters. type SetExceptionBreakpointsRequest struct { Request @@ -673,7 +675,7 @@ type SetExceptionBreakpointsArguments struct { // SetExceptionBreakpointsResponse: Response to `setExceptionBreakpoints` request. // The response contains an array of `Breakpoint` objects with information about each exception breakpoint or filter. The `Breakpoint` objects are in the same order as the elements of the `filters`, `filterOptions`, `exceptionOptions` arrays given as arguments. If both `filters` and `filterOptions` are given, the returned array must start with `filters` information first, followed by `filterOptions` information. -// The `verified` property of a `Breakpoint` object signals whether the exception breakpoint or filter could be successfully created and whether the condition or hit count expressions are valid. In case of an error the `message` property explains the problem. The `id` property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events. +// The `verified` property of a `Breakpoint` object signals whether the exception breakpoint or filter could be successfully created and whether the condition is valid. In case of an error the `message` property explains the problem. The `id` property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events. // For backward compatibility both the `breakpoints` array and the enclosing `body` are optional. If these elements are missing a client is not able to show problems for individual exception breakpoints or filters. type SetExceptionBreakpointsResponse struct { Response @@ -698,6 +700,9 @@ type DataBreakpointInfoArguments struct { VariablesReference int `json:"variablesReference,omitempty"` Name string `json:"name"` FrameId int `json:"frameId,omitempty"` + Bytes int `json:"bytes,omitempty"` + AsAddress bool `json:"asAddress,omitempty"` + Mode string `json:"mode,omitempty"` } // DataBreakpointInfoResponse: Response to `dataBreakpointInfo` request. @@ -1062,11 +1067,13 @@ type SetVariableResponse struct { } type SetVariableResponseBody struct { - Value string `json:"value"` - Type string `json:"type,omitempty"` - VariablesReference int `json:"variablesReference,omitempty"` - NamedVariables int `json:"namedVariables,omitempty"` - IndexedVariables int `json:"indexedVariables,omitempty"` + Value string `json:"value"` + Type string `json:"type,omitempty"` + VariablesReference int `json:"variablesReference,omitempty"` + NamedVariables int `json:"namedVariables,omitempty"` + IndexedVariables int `json:"indexedVariables,omitempty"` + MemoryReference string `json:"memoryReference,omitempty"` + ValueLocationReference int `json:"valueLocationReference,omitempty"` } // SourceRequest: The request retrieves the source code for a given source reference. @@ -1177,7 +1184,7 @@ type LoadedSourcesResponseBody struct { Sources []Source `json:"sources"` } -// EvaluateRequest: Evaluates the given expression in the context of the topmost stack frame. +// EvaluateRequest: Evaluates the given expression in the context of a stack frame. // The expression has access to any variables and arguments that are in scope. type EvaluateRequest struct { Request @@ -1189,6 +1196,9 @@ type EvaluateRequest struct { type EvaluateArguments struct { Expression string `json:"expression"` FrameId int `json:"frameId,omitempty"` + Line int `json:"line,omitempty"` + Column int `json:"column,omitempty"` + Source *Source `json:"source,omitempty"` Context string `json:"context,omitempty"` Format *ValueFormat `json:"format,omitempty"` } @@ -1201,13 +1211,14 @@ type EvaluateResponse struct { } type EvaluateResponseBody struct { - Result string `json:"result"` - Type string `json:"type,omitempty"` - PresentationHint *VariablePresentationHint `json:"presentationHint,omitempty"` - VariablesReference int `json:"variablesReference"` - NamedVariables int `json:"namedVariables,omitempty"` - IndexedVariables int `json:"indexedVariables,omitempty"` - MemoryReference string `json:"memoryReference,omitempty"` + Result string `json:"result"` + Type string `json:"type,omitempty"` + PresentationHint *VariablePresentationHint `json:"presentationHint,omitempty"` + VariablesReference int `json:"variablesReference"` + NamedVariables int `json:"namedVariables,omitempty"` + IndexedVariables int `json:"indexedVariables,omitempty"` + MemoryReference string `json:"memoryReference,omitempty"` + ValueLocationReference int `json:"valueLocationReference,omitempty"` } // SetExpressionRequest: Evaluates the given `value` expression and assigns it to the `expression` which must be a modifiable l-value. @@ -1236,12 +1247,14 @@ type SetExpressionResponse struct { } type SetExpressionResponseBody struct { - Value string `json:"value"` - Type string `json:"type,omitempty"` - PresentationHint *VariablePresentationHint `json:"presentationHint,omitempty"` - VariablesReference int `json:"variablesReference,omitempty"` - NamedVariables int `json:"namedVariables,omitempty"` - IndexedVariables int `json:"indexedVariables,omitempty"` + Value string `json:"value"` + Type string `json:"type,omitempty"` + PresentationHint *VariablePresentationHint `json:"presentationHint,omitempty"` + VariablesReference int `json:"variablesReference,omitempty"` + NamedVariables int `json:"namedVariables,omitempty"` + IndexedVariables int `json:"indexedVariables,omitempty"` + MemoryReference string `json:"memoryReference,omitempty"` + ValueLocationReference int `json:"valueLocationReference,omitempty"` } // StepInTargetsRequest: This request retrieves the possible step-in targets for the specified stack frame. @@ -1434,6 +1447,33 @@ type DisassembleResponseBody struct { Instructions []DisassembledInstruction `json:"instructions"` } +// LocationsRequest: Looks up information about a location reference previously returned by the debug adapter. +type LocationsRequest struct { + Request + + Arguments LocationsArguments `json:"arguments"` +} + +// LocationsArguments: Arguments for `locations` request. +type LocationsArguments struct { + LocationReference int `json:"locationReference"` +} + +// LocationsResponse: Response to `locations` request. +type LocationsResponse struct { + Response + + Body LocationsResponseBody `json:"body,omitempty"` +} + +type LocationsResponseBody struct { + Source Source `json:"source"` + Line int `json:"line"` + Column int `json:"column,omitempty"` + EndLine int `json:"endLine,omitempty"` + EndColumn int `json:"endColumn,omitempty"` +} + // Capabilities: Information about the capabilities of a debug adapter. type Capabilities struct { SupportsConfigurationDoneRequest bool `json:"supportsConfigurationDoneRequest,omitempty"` @@ -1475,6 +1515,9 @@ type Capabilities struct { SupportsInstructionBreakpoints bool `json:"supportsInstructionBreakpoints,omitempty"` SupportsExceptionFilterOptions bool `json:"supportsExceptionFilterOptions,omitempty"` SupportsSingleThreadExecutionRequests bool `json:"supportsSingleThreadExecutionRequests,omitempty"` + SupportsDataBreakpointBytes bool `json:"supportsDataBreakpointBytes,omitempty"` + BreakpointModes []BreakpointMode `json:"breakpointModes,omitempty"` + SupportsANSIStyling bool `json:"supportsANSIStyling,omitempty"` } // ExceptionBreakpointsFilter: An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for configuring how exceptions are dealt with. @@ -1529,12 +1572,6 @@ type ColumnDescriptor struct { Width int `json:"width,omitempty"` } -// ModulesViewDescriptor: The ModulesViewDescriptor is the container for all declarative configuration options of a module view. -// For now it only specifies the columns to be shown in the modules view. -type ModulesViewDescriptor struct { - Columns []ColumnDescriptor `json:"columns"` -} - // Thread: A Thread type Thread struct { Id int `json:"id"` @@ -1591,15 +1628,17 @@ type Scope struct { // If the number of named or indexed children is large, the numbers should be returned via the `namedVariables` and `indexedVariables` attributes. // The client can use this information to present the children in a paged UI and fetch them in chunks. type Variable struct { - Name string `json:"name"` - Value string `json:"value"` - Type string `json:"type,omitempty"` - PresentationHint *VariablePresentationHint `json:"presentationHint,omitempty"` - EvaluateName string `json:"evaluateName,omitempty"` - VariablesReference int `json:"variablesReference"` - NamedVariables int `json:"namedVariables,omitempty"` - IndexedVariables int `json:"indexedVariables,omitempty"` - MemoryReference string `json:"memoryReference,omitempty"` + Name string `json:"name"` + Value string `json:"value"` + Type string `json:"type,omitempty"` + PresentationHint *VariablePresentationHint `json:"presentationHint,omitempty"` + EvaluateName string `json:"evaluateName,omitempty"` + VariablesReference int `json:"variablesReference"` + NamedVariables int `json:"namedVariables,omitempty"` + IndexedVariables int `json:"indexedVariables,omitempty"` + MemoryReference string `json:"memoryReference,omitempty"` + DeclarationLocationReference int `json:"declarationLocationReference,omitempty"` + ValueLocationReference int `json:"valueLocationReference,omitempty"` } // VariablePresentationHint: Properties of a variable that can be used to determine how to render the variable in the UI. @@ -1625,6 +1664,7 @@ type SourceBreakpoint struct { Condition string `json:"condition,omitempty"` HitCondition string `json:"hitCondition,omitempty"` LogMessage string `json:"logMessage,omitempty"` + Mode string `json:"mode,omitempty"` } // FunctionBreakpoint: Properties of a breakpoint passed to the `setFunctionBreakpoints` request. @@ -1651,6 +1691,7 @@ type InstructionBreakpoint struct { Offset int `json:"offset,omitempty"` Condition string `json:"condition,omitempty"` HitCondition string `json:"hitCondition,omitempty"` + Mode string `json:"mode,omitempty"` } // Breakpoint: Information about a breakpoint created in `setBreakpoints`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, or `setDataBreakpoints` requests. @@ -1665,6 +1706,7 @@ type Breakpoint struct { EndColumn int `json:"endColumn,omitempty"` InstructionReference string `json:"instructionReference,omitempty"` Offset int `json:"offset,omitempty"` + Reason string `json:"reason,omitempty"` } // SteppingGranularity: The granularity of one 'step' in the stepping requests `next`, `stepIn`, `stepOut`, and `stepBack`. @@ -1739,6 +1781,7 @@ type StackFrameFormat struct { type ExceptionFilterOptions struct { FilterId string `json:"filterId"` Condition string `json:"condition,omitempty"` + Mode string `json:"mode,omitempty"` } // ExceptionOptions: An `ExceptionOptions` assigns configuration options to a set of exceptions. @@ -1782,11 +1825,23 @@ type DisassembledInstruction struct { Column int `json:"column,omitempty"` EndLine int `json:"endLine,omitempty"` EndColumn int `json:"endColumn,omitempty"` + PresentationHint string `json:"presentationHint,omitempty"` } // InvalidatedAreas: Logical areas that can be invalidated by the `invalidated` event. type InvalidatedAreas string +// BreakpointMode: A `BreakpointMode` is provided as a option when setting breakpoints on sources or instructions. +type BreakpointMode struct { + Mode string `json:"mode"` + Label string `json:"label"` + Description string `json:"description,omitempty"` + AppliesTo []BreakpointModeApplicability `json:"appliesTo"` +} + +// BreakpointModeApplicability: Describes one or more type of breakpoint a `BreakpointMode` applies to. This is a non-exhaustive enumeration and may expand as future breakpoint types are added. +type BreakpointModeApplicability string + // Mapping of request commands and corresponding struct constructors that // can be passed to json.Unmarshal. var requestCtor = map[string]messageCtor{ @@ -1843,6 +1898,7 @@ var requestCtor = map[string]messageCtor{ "readMemory": func() Message { return &ReadMemoryRequest{} }, "writeMemory": func() Message { return &WriteMemoryRequest{} }, "disassemble": func() Message { return &DisassembleRequest{} }, + "locations": func() Message { return &LocationsRequest{} }, } // Mapping of response commands and corresponding struct constructors that @@ -1892,6 +1948,7 @@ var responseCtor = map[string]messageCtor{ "readMemory": func() Message { return &ReadMemoryResponse{} }, "writeMemory": func() Message { return &WriteMemoryResponse{} }, "disassemble": func() Message { return &DisassembleResponse{} }, + "locations": func() Message { return &LocationsResponse{} }, } // Mapping of event ids and corresponding struct constructors that diff --git a/vendor/modules.txt b/vendor/modules.txt index 28f8741bff51..bbee6179b8cc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -333,7 +333,7 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/go-dap v0.12.0 +# github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b ## explicit; go 1.18 github.com/google/go-dap # github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510