Skip to content

LSP documentSymbol marks every symbol with SymbolTag.Deprecated, regardless of whether it is deprecated #4530

@garrettr

Description

@garrettr

GitHub repository with your minimal reproducible example (do not leave this field blank or fill out this field with "github.com/bufbuild/buf" or we will automatically close your issue, see the instructions above!)

https://github.com/garrettr/buf-lsp-symboltag-deprecated-repro

Commands

python3 lsp_probe.py


(`lsp_probe.py` is a dependency-free Python LSP client that opens
`proto/example/v1/example.proto`, requests `textDocument/documentSymbol`,
and prints each returned symbol's `tags` field. It just shells out to
`buf lsp serve`.)

Output

$ python3 lsp_probe.py
buf version: 1.67.0
symbols returned: 9
symbols with SymbolTag.Deprecated (tags=[1]): 9

  name=example.v1.Greeting                      tags=[1]
  name=example.v1.Greeting.text                 tags=[1]
  name=example.v1.Greeting.count                tags=[1]
  name=example.v1.GreeterService                tags=[1]
  name=example.v1.GreeterService.Greet          tags=[1]
  name=example.v1.GreetRequest                  tags=[1]
  name=example.v1.GreetRequest.name             tags=[1]
  name=example.v1.GreetResponse                 tags=[1]
  name=example.v1.GreetResponse.greeting        tags=[1]

Expected Output

example.proto contains no option deprecated = true; or [deprecated = true]
anywhere, so no symbol should carry SymbolTag.Deprecated. Expected:

buf version: 1.67.0
symbols returned: 9
symbols with SymbolTag.Deprecated (tags=[1]): 0

  name=example.v1.Greeting                      tags=None
  name=example.v1.Greeting.text                 tags=None
  ... (etc.)

Anything else?

The user-visible effect is that every symbol in every .proto file is rendered
with a strikethrough in editors that honor SymbolTag.Deprecated from
textDocument/documentSymbol — in VS Code that's the Outline panel, breadcrumbs,
sticky scroll, and Go to Symbol (Cmd+Shift+O). Editor body text is unaffected
because the semantic-token codepath flags deprecated tokens correctly.

Cause. Introduced in #4311 (Add tags to LSP diagnostics, merged
2026-02-03), first released in v1.65.0, still present on main as of this
writing. private/buf/buflsp/symbol.go populates Tags unconditionally:

var isDeprecated bool
if _, ok := s.ir.Deprecated().AsBool(); ok {
    isDeprecated = true
}
return protocol.SymbolInformation{
    ...
    Deprecated:    isDeprecated,
    Tags: []protocol.SymbolTag{          // <-- always set
        protocol.SymbolTagDeprecated,
    },
}

Per the LSP spec,
clients prefer tags over the deprecated deprecated field, so the
unconditional tag wins. Tags should only include SymbolTagDeprecated when
isDeprecated is true, e.g.:

var tags []protocol.SymbolTag
if isDeprecated {
    tags = []protocol.SymbolTag{protocol.SymbolTagDeprecated}
}

A second, smaller bug: the if discards the bool returned by AsBool() and
only checks the second ok return, so isDeprecated (and the legacy
Deprecated field) would also be wrong for an explicit
option deprecated = false;. It should be:

isDeprecated, _ := s.ir.Deprecated().AsBool()

Affected: v1.65.0v1.69.0 (and main). Last unaffected release: v1.64.0.
Both buf lsp serve and buf beta lsp are affected (they share symbol.go).
Reproduced on Linux x86_64.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions