Skip to content

Commit

Permalink
Implement debug pragma to control diagnostic severity for extended sy…
Browse files Browse the repository at this point in the history
…ntax errors
  • Loading branch information
kralicky committed Jan 5, 2024
1 parent 2f06157 commit 29be66f
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 32 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/bufbuild/protovalidate-go v0.3.4
github.com/google/cel-go v0.18.2
github.com/kralicky/gpkg v0.0.0-20231114180450-2f4bff8c5588
github.com/kralicky/protocompile v0.0.0-20240103002913-5d61ee5b6d9c
github.com/kralicky/protocompile v0.0.0-20240105180210-126534396e55
github.com/kralicky/tools-lite v0.0.0-20240104191314-c259ddd5a342
github.com/mattn/go-tty v0.0.5
github.com/spf13/cobra v1.8.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kralicky/gpkg v0.0.0-20231114180450-2f4bff8c5588 h1:chw4znRXk7AA+AlKcrUZzH1Vupl54KcS4W6wkXCX3lU=
github.com/kralicky/gpkg v0.0.0-20231114180450-2f4bff8c5588/go.mod h1:vOkwMjs49XmP/7Xfo9ZL6eg2ei51lmtD/4U/Az5GTq8=
github.com/kralicky/protocompile v0.0.0-20240103002913-5d61ee5b6d9c h1:isISCD75zl9NUt5eSziYtS71coiFZi3alP7bA/QIAbo=
github.com/kralicky/protocompile v0.0.0-20240103002913-5d61ee5b6d9c/go.mod h1:QKlDXp/yojhlpqgJfUHWhqzvD9gCD/baEPFvq89cpgE=
github.com/kralicky/protocompile v0.0.0-20240105180210-126534396e55 h1:tkCmw3X/58q7BwntYCM4+M7iMaXZw0toufzRghEpBFI=
github.com/kralicky/protocompile v0.0.0-20240105180210-126534396e55/go.mod h1:QKlDXp/yojhlpqgJfUHWhqzvD9gCD/baEPFvq89cpgE=
github.com/kralicky/tools-lite v0.0.0-20240104191314-c259ddd5a342 h1:lZLWHXKHmOhTrs3oSZoCRtb8Y9a0mqUwCsaKut+Y1eU=
github.com/kralicky/tools-lite v0.0.0-20240104191314-c259ddd5a342/go.mod h1:NKsdxFI6awifvNvxDwtCU1YCaKRoSSPpbHXkKOMuq24=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
Expand Down
61 changes: 45 additions & 16 deletions pkg/lsp/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Cache struct {

inflightTasksInvalidate gsync.Map[protocompile.ResolvedPath, time.Time]
inflightTasksCompile gsync.Map[protocompile.ResolvedPath, time.Time]
pragmas gsync.Map[protocompile.ResolvedPath, *pragmaMap]
}

// FindDescriptorByName implements linker.Resolver.
Expand Down Expand Up @@ -294,17 +295,17 @@ func (c *Cache) preInvalidateHook(path protocompile.ResolvedPath, reason string)
func (c *Cache) postInvalidateHook(path protocompile.ResolvedPath, prevResult linker.File, willRecompile bool) {
startTime, _ := c.inflightTasksInvalidate.LoadAndDelete(path)
slog.Debug("file invalidated", "path", path, "took", time.Since(startTime))
// if !willRecompile {
// c.resultsMu.Lock()
// defer c.resultsMu.Unlock()
// slog.Debug("file deleted, clearing linker result", "path", path)
// for i, f := range c.results {
// if f.Path() == path {
// c.results = append(c.results[:i], c.results[i+1:]...)
// break
// }
// }
// }
if !willRecompile {
c.resultsMu.Lock()
defer c.resultsMu.Unlock()
slog.Debug("file deleted, clearing linker result", "path", path)
for i, f := range c.results {
if protocompile.ResolvedPath(f.Path()) == path {
c.results = append(c.results[:i], c.results[i+1:]...)
break
}
}
}
}

func (c *Cache) preCompile(path protocompile.ResolvedPath) {
Expand All @@ -325,8 +326,6 @@ func (c *Cache) postCompile(path protocompile.ResolvedPath) {
}

func (c *Cache) Compile(protos ...string) {
// c.compileLock.Lock()
// defer c.compileLock.Unlock()
c.resultsMu.Lock()
defer c.resultsMu.Unlock()
c.compileLocked(protos...)
Expand All @@ -346,25 +345,28 @@ func (c *Cache) compileLocked(protos ...string) {
return
}
}
// important to lock resultsMu here so that it can be modified in compile hooks
// c.resultsMu.Lock()
slog.Debug("done compiling", "protos", len(protos))
for _, r := range res.Files {
path := r.Path()
found := false
// delete(c.partialResults, path)
pragmas := r.(linker.Result).AST().Pragmas

for i, f := range c.results {
// todo: this is big slow
if f.Path() == path {
found = true
slog.With("path", path).Debug("updating existing linker result")
c.results[i] = r
if p, ok := c.pragmas.Load(protocompile.ResolvedPath(path)); ok {
p.update(pragmas)
}
break
}
}
if !found {
slog.With("path", path).Debug("adding new linker result")
c.results = append(c.results, r)
c.pragmas.Store(protocompile.ResolvedPath(path), &pragmaMap{m: pragmas})
}
}
c.unlinkedResultsMu.Lock()
Expand Down Expand Up @@ -486,6 +488,25 @@ func (c *Cache) toProtocolDiagnostics(rawReports []*ProtoDiagnostic) []protocol.
rawReport.RelatedInformation[i].Location.URI = u
}
}
if rawReport.Severity == protocol.SeverityWarning && rawReport.WerrorCategory != "" {
// look up Werror debug pragma for this file
shouldElevate := true
if p, ok := c.FindPragmasByPath(protocompile.ResolvedPath(rawReport.Pos.Start().Filename)); ok {
if dbg, ok := p.Lookup(PragmaDebug); ok {
for _, v := range strings.Fields(dbg) {
if k, v, ok := strings.Cut(v, "="); ok {
if k == PragmaDebugWnoerror && (v == rawReport.WerrorCategory || v == WnoerrorAll) {
shouldElevate = false
break
}
}
}
}
}
if shouldElevate {
rawReport.Severity = protocol.SeverityError
}
}
report := protocol.Diagnostic{
Range: toRange(rawReport.Pos),
Severity: rawReport.Severity,
Expand All @@ -494,6 +515,9 @@ func (c *Cache) toProtocolDiagnostics(rawReports []*ProtoDiagnostic) []protocol.
RelatedInformation: rawReport.RelatedInformation,
Source: "protols",
}
if rawReport.WerrorCategory != "" {
report.Code = rawReport.WerrorCategory
}
if len(rawReport.CodeActions) > 0 {
jsonData, err := json.Marshal(CodeActions{Items: rawReport.CodeActions})
if err != nil {
Expand Down Expand Up @@ -1300,3 +1324,8 @@ func (c *Cache) AllMessages() []protoreflect.MessageDescriptor {
}
return all
}

func (c *Cache) FindPragmasByPath(path protocompile.ResolvedPath) (Pragmas, bool) {
p, ok := c.pragmas.Load(path)
return p, ok
}
31 changes: 20 additions & 11 deletions pkg/lsp/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type ProtoDiagnostic struct {
Tags []protocol.DiagnosticTag
RelatedInformation []protocol.DiagnosticRelatedInformation
CodeActions []CodeAction

// If this is a warning being treated as an error, WerrorCategory will be set to
// a category that can be named in a debug pragma to disable it.
WerrorCategory string
}

type CodeActions struct {
Expand Down Expand Up @@ -221,6 +225,14 @@ func codeActionsForError(errWithPos reporter.ErrorWithPos) []CodeAction {
return []CodeAction{}
}

func werrorCategoryForError(err error) string {
var xse parser.ExtendedSyntaxError
if errors.As(err, &xse) {
return xse.Category()
}
return ""
}

func relatedInformationForError(err error) []protocol.DiagnosticRelatedInformation {
var alreadyDefined reporter.AlreadyDefinedError
if ok := errors.As(err, &alreadyDefined); ok {
Expand Down Expand Up @@ -268,6 +280,7 @@ func (dr *DiagnosticHandler) HandleError(err reporter.ErrorWithPos) error {
RelatedInformation: relatedInformationForError(err),
CodeActions: codeActionsForError(err),
}

dl.Add(newDiagnostic)

dr.listenerMu.RLock()
Expand All @@ -284,12 +297,6 @@ func (dr *DiagnosticHandler) HandleWarning(err reporter.ErrorWithPos) {
return
}

var xse parser.ExtendedSyntaxError
if errors.As(err, &xse) {
dr.HandleError(err)
return
}

slog.Debug(fmt.Sprintf("[diagnostic] warning: %s\n", err.Error()))

pos := err.GetPosition()
Expand All @@ -300,11 +307,12 @@ func (dr *DiagnosticHandler) HandleWarning(err reporter.ErrorWithPos) {
dr.diagnosticsMu.Unlock()

newDiagnostic := &ProtoDiagnostic{
Pos: pos,
Severity: severityForError(protocol.SeverityWarning, err),
Error: err.Unwrap(),
Tags: tagsForError(err),
CodeActions: codeActionsForError(err),
Pos: pos,
Severity: severityForError(protocol.SeverityWarning, err),
Error: err.Unwrap(),
Tags: tagsForError(err),
CodeActions: codeActionsForError(err),
WerrorCategory: werrorCategoryForError(err),
}
dl.Add(newDiagnostic)

Expand Down Expand Up @@ -358,6 +366,7 @@ func (dr *DiagnosticHandler) FullDiagnosticSnapshot() map[string][]*ProtoDiagnos
Tags: d.Tags,
RelatedInformation: d.RelatedInformation,
CodeActions: d.CodeActions,
WerrorCategory: d.WerrorCategory,
})
}
res[path] = list
Expand Down
37 changes: 35 additions & 2 deletions pkg/lsp/pragmas.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
package lsp

import "github.com/kralicky/protocompile/ast"
import (
"sync"

"github.com/kralicky/protocompile/ast"
)

func init() {
ast.PragmaKey = "protols"
}

var PragmaNoFormat = "nofmt"
const (
PragmaNoFormat = "nofmt"
PragmaDebug = "debug"

PragmaDebugWnoerror = "Wnoerror"
WnoerrorAll = "all"
)

type Pragmas interface {
Lookup(key string) (value string, ok bool)
}

type pragmaMap struct {
mu sync.RWMutex
m map[string]string
}

// Lookup implements Pragmas.
func (p *pragmaMap) Lookup(key string) (string, bool) {
p.mu.RLock()
defer p.mu.RUnlock()
v, ok := p.m[key]
return v, ok
}

func (p *pragmaMap) update(m map[string]string) {
p.mu.Lock()
defer p.mu.Unlock()
p.m = m
}

0 comments on commit 29be66f

Please sign in to comment.