Skip to content

Commit 65a6a00

Browse files
authored
Merge pull request authzed#1881 from josephschorr/lsp-warnings
Add warnings to the LSP
2 parents 1c24c8c + 0185e09 commit 65a6a00

File tree

5 files changed

+78
-22
lines changed

5 files changed

+78
-22
lines changed

internal/lsp/handlers.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,15 @@ func (s *Server) computeDiagnostics(ctx context.Context, uri lsp.DocumentURI) ([
5757
return &jsonrpc2.Error{Code: jsonrpc2.CodeInternalError, Message: "file not found"}
5858
}
5959

60-
_, devErrs, err := development.NewDevContext(ctx, &developerv1.RequestContext{
60+
devCtx, devErrs, err := development.NewDevContext(ctx, &developerv1.RequestContext{
6161
Schema: file.contents,
6262
Relationships: nil,
6363
})
6464
if err != nil {
6565
return err
6666
}
6767

68+
// Get errors.
6869
for _, devErr := range devErrs.GetInputErrors() {
6970
diagnostics = append(diagnostics, lsp.Diagnostic{
7071
Severity: lsp.Error,
@@ -76,11 +77,31 @@ func (s *Server) computeDiagnostics(ctx context.Context, uri lsp.DocumentURI) ([
7677
})
7778
}
7879

80+
// If there are no errors, we can also check for warnings.
81+
if len(diagnostics) == 0 {
82+
warnings, err := development.GetWarnings(ctx, devCtx)
83+
if err != nil {
84+
return err
85+
}
86+
87+
for _, devWarning := range warnings {
88+
diagnostics = append(diagnostics, lsp.Diagnostic{
89+
Severity: lsp.Warning,
90+
Range: lsp.Range{
91+
Start: lsp.Position{Line: int(devWarning.Line) - 1, Character: int(devWarning.Column) - 1},
92+
End: lsp.Position{Line: int(devWarning.Line) - 1, Character: int(devWarning.Column) - 1},
93+
},
94+
Message: devWarning.Message,
95+
})
96+
}
97+
}
98+
7999
return nil
80100
}); err != nil {
81101
return nil, err
82102
}
83103

104+
log.Info().Int("diagnostics", len(diagnostics)).Str("uri", string(uri)).Msg("computed diagnostics")
84105
return diagnostics, nil
85106
}
86107

internal/lsp/lsp_test.go

+28-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestDocumentNoDiagnostics(t *testing.T) {
5151
require.Len(t, resp.Items, 0)
5252
}
5353

54-
func TestDocumentDiagnostics(t *testing.T) {
54+
func TestDocumentErrorDiagnostics(t *testing.T) {
5555
tester := newLSPTester(t)
5656
tester.initialize()
5757

@@ -80,6 +80,33 @@ func TestDocumentDiagnostics(t *testing.T) {
8080
require.Len(t, resp.Items, 0)
8181
}
8282

83+
func TestDocumentWarningDiagnostics(t *testing.T) {
84+
tester := newLSPTester(t)
85+
tester.initialize()
86+
87+
tester.setFileContents("file:///test", `
88+
definition user {}
89+
90+
definition resource {
91+
relation viewer: user
92+
permission view_resource = viewer
93+
}
94+
`)
95+
96+
resp, _ := sendAndReceive[FullDocumentDiagnosticReport](tester, "textDocument/diagnostic",
97+
TextDocumentDiagnosticParams{
98+
TextDocument: TextDocument{URI: "file:///test"},
99+
})
100+
require.Equal(t, "full", resp.Kind)
101+
require.Len(t, resp.Items, 1)
102+
require.Equal(t, lsp.DiagnosticSeverity(lsp.Warning), resp.Items[0].Severity)
103+
require.Equal(t, `Permission "view_resource" references parent type "resource" in its name; it is recommended to drop the suffix`, resp.Items[0].Message)
104+
require.Equal(t, lsp.Range{
105+
Start: lsp.Position{Line: 5, Character: 3},
106+
End: lsp.Position{Line: 5, Character: 3},
107+
}, resp.Items[0].Range)
108+
}
109+
83110
func TestDocumentDiagnosticsForTypeError(t *testing.T) {
84111
tester := newLSPTester(t)
85112
tester.initialize()

pkg/development/warningdefs.go

+12-8
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ var lintRelationReferencesParentType = func(
3737
var lintPermissionReferencingItself = func(
3838
ctx context.Context,
3939
computedUserset *corev1.ComputedUserset,
40+
sourcePosition *corev1.SourcePosition,
4041
ts *typesystem.TypeSystem,
4142
) (*devinterface.DeveloperWarning, error) {
4243
parentRelation := ctx.Value(relationKey).(*corev1.Relation)
4344
permName := parentRelation.Name
4445
if computedUserset.Relation == permName {
45-
return warningForMetadata(
46+
return warningForPosition(
4647
fmt.Sprintf("Permission %q references itself, which will cause an error to be raised due to infinite recursion", permName),
47-
parentRelation,
48+
sourcePosition,
4849
), nil
4950
}
5051

@@ -54,6 +55,7 @@ var lintPermissionReferencingItself = func(
5455
var lintArrowReferencingUnreachable = func(
5556
ctx context.Context,
5657
ttu *corev1.TupleToUserset,
58+
sourcePosition *corev1.SourcePosition,
5759
ts *typesystem.TypeSystem,
5860
) (*devinterface.DeveloperWarning, error) {
5961
parentRelation := ctx.Value(relationKey).(*corev1.Relation)
@@ -82,7 +84,7 @@ var lintArrowReferencingUnreachable = func(
8284
}
8385

8486
if !wasFound {
85-
return warningForMetadata(
87+
return warningForPosition(
8688
fmt.Sprintf(
8789
"Arrow `%s->%s` under permission %q references relation/permission %q that does not exist on any subject types of relation %q",
8890
ttu.Tupleset.Relation,
@@ -91,7 +93,7 @@ var lintArrowReferencingUnreachable = func(
9193
ttu.ComputedUserset.Relation,
9294
ttu.Tupleset.Relation,
9395
),
94-
parentRelation,
96+
sourcePosition,
9597
), nil
9698
}
9799

@@ -101,6 +103,7 @@ var lintArrowReferencingUnreachable = func(
101103
var lintArrowOverSubRelation = func(
102104
ctx context.Context,
103105
ttu *corev1.TupleToUserset,
106+
sourcePosition *corev1.SourcePosition,
104107
ts *typesystem.TypeSystem,
105108
) (*devinterface.DeveloperWarning, error) {
106109
parentRelation := ctx.Value(relationKey).(*corev1.Relation)
@@ -117,7 +120,7 @@ var lintArrowOverSubRelation = func(
117120

118121
for _, subjectType := range allowedSubjectTypes {
119122
if subjectType.Relation != tuple.Ellipsis {
120-
return warningForMetadata(
123+
return warningForPosition(
121124
fmt.Sprintf(
122125
"Arrow `%s->%s` under permission %q references relation %q that has relation %q on subject %q: *the subject relation will be ignored for the arrow*",
123126
ttu.Tupleset.Relation,
@@ -127,7 +130,7 @@ var lintArrowOverSubRelation = func(
127130
subjectType.Relation,
128131
subjectType.Namespace,
129132
),
130-
parentRelation,
133+
sourcePosition,
131134
), nil
132135
}
133136
}
@@ -138,6 +141,7 @@ var lintArrowOverSubRelation = func(
138141
var lintArrowReferencingRelation = func(
139142
ctx context.Context,
140143
ttu *corev1.TupleToUserset,
144+
sourcePosition *corev1.SourcePosition,
141145
ts *typesystem.TypeSystem,
142146
) (*devinterface.DeveloperWarning, error) {
143147
parentRelation := ctx.Value(relationKey).(*corev1.Relation)
@@ -166,7 +170,7 @@ var lintArrowReferencingRelation = func(
166170
}
167171

168172
if !nts.IsPermission(targetRelation.Name) {
169-
return warningForMetadata(
173+
return warningForPosition(
170174
fmt.Sprintf(
171175
"Arrow `%s->%s` under permission %q references relation %q on definition %q; it is recommended to point to a permission",
172176
ttu.Tupleset.Relation,
@@ -175,7 +179,7 @@ var lintArrowReferencingRelation = func(
175179
targetRelation.Name,
176180
subjectType.Namespace,
177181
),
178-
parentRelation,
182+
sourcePosition,
179183
), nil
180184
}
181185
}

pkg/development/warnings.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@ var allChecks = checkers{
2525
}
2626

2727
func warningForMetadata(message string, metadata namespace.WithSourcePosition) *devinterface.DeveloperWarning {
28-
if metadata.GetSourcePosition() == nil {
28+
return warningForPosition(message, metadata.GetSourcePosition())
29+
}
30+
31+
func warningForPosition(message string, sourcePosition *corev1.SourcePosition) *devinterface.DeveloperWarning {
32+
if sourcePosition == nil {
2933
return &devinterface.DeveloperWarning{
3034
Message: message,
3135
}
3236
}
3337

34-
lineNumber := metadata.GetSourcePosition().ZeroIndexedLineNumber + 1
35-
columnNumber := metadata.GetSourcePosition().ZeroIndexedColumnPosition + 1
38+
lineNumber := sourcePosition.ZeroIndexedLineNumber + 1
39+
columnNumber := sourcePosition.ZeroIndexedColumnPosition + 1
3640

3741
return &devinterface.DeveloperWarning{
3842
Message: message,
@@ -96,8 +100,8 @@ func addDefinitionWarnings(ctx context.Context, def *corev1.NamespaceDefinition,
96100

97101
type (
98102
relationChecker func(ctx context.Context, relation *corev1.Relation, vts *typesystem.TypeSystem) (*devinterface.DeveloperWarning, error)
99-
computedUsersetChecker func(ctx context.Context, computedUserset *corev1.ComputedUserset, vts *typesystem.TypeSystem) (*devinterface.DeveloperWarning, error)
100-
ttuChecker func(ctx context.Context, ttu *corev1.TupleToUserset, vts *typesystem.TypeSystem) (*devinterface.DeveloperWarning, error)
103+
computedUsersetChecker func(ctx context.Context, computedUserset *corev1.ComputedUserset, sourcePosition *corev1.SourcePosition, vts *typesystem.TypeSystem) (*devinterface.DeveloperWarning, error)
104+
ttuChecker func(ctx context.Context, ttu *corev1.TupleToUserset, sourcePosition *corev1.SourcePosition, vts *typesystem.TypeSystem) (*devinterface.DeveloperWarning, error)
101105
)
102106

103107
type checkers struct {
@@ -135,7 +139,7 @@ func walkUsersetOperations(ctx context.Context, ops []*corev1.SetOperation_Child
135139

136140
case *corev1.SetOperation_Child_ComputedUserset:
137141
for _, checker := range checkers.computedUsersetCheckers {
138-
checkerWarning, err := checker(ctx, t.ComputedUserset, ts)
142+
checkerWarning, err := checker(ctx, t.ComputedUserset, op.SourcePosition, ts)
139143
if err != nil {
140144
return nil, err
141145
}
@@ -155,7 +159,7 @@ func walkUsersetOperations(ctx context.Context, ops []*corev1.SetOperation_Child
155159

156160
case *corev1.SetOperation_Child_TupleToUserset:
157161
for _, checker := range checkers.ttuCheckers {
158-
checkerWarning, err := checker(ctx, t.TupleToUserset, ts)
162+
checkerWarning, err := checker(ctx, t.TupleToUserset, op.SourcePosition, ts)
159163
if err != nil {
160164
return nil, err
161165
}

pkg/development/warnings_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestWarnings(t *testing.T) {
3838
expectedWarning: &developerv1.DeveloperWarning{
3939
Message: "Permission \"view\" references itself, which will cause an error to be raised due to infinite recursion",
4040
Line: 2,
41-
Column: 5,
41+
Column: 23,
4242
},
4343
},
4444
{
@@ -51,7 +51,7 @@ func TestWarnings(t *testing.T) {
5151
expectedWarning: &developerv1.DeveloperWarning{
5252
Message: "Permission \"view\" references itself, which will cause an error to be raised due to infinite recursion",
5353
Line: 4,
54-
Column: 5,
54+
Column: 42,
5555
},
5656
},
5757
{
@@ -70,7 +70,7 @@ func TestWarnings(t *testing.T) {
7070
expectedWarning: &developerv1.DeveloperWarning{
7171
Message: "Arrow `group->member` under permission \"view\" references relation \"member\" on definition \"group\"; it is recommended to point to a permission",
7272
Line: 9,
73-
Column: 5,
73+
Column: 23,
7474
},
7575
},
7676
{
@@ -88,7 +88,7 @@ func TestWarnings(t *testing.T) {
8888
expectedWarning: &developerv1.DeveloperWarning{
8989
Message: "Arrow `group->member` under permission \"view\" references relation/permission \"member\" that does not exist on any subject types of relation \"group\"",
9090
Line: 8,
91-
Column: 5,
91+
Column: 23,
9292
},
9393
},
9494
{
@@ -108,7 +108,7 @@ func TestWarnings(t *testing.T) {
108108
expectedWarning: &developerv1.DeveloperWarning{
109109
Message: "Arrow `parent_group->member` under permission \"view\" references relation \"parent_group\" that has relation \"member\" on subject \"group\": *the subject relation will be ignored for the arrow*",
110110
Line: 10,
111-
Column: 5,
111+
Column: 23,
112112
},
113113
},
114114
{

0 commit comments

Comments
 (0)