feat(ssh_config): Check if IgnoreUnknown contains unused options

This commit is contained in:
Myzel394 2024-10-02 14:24:22 +02:00
parent cd23ffcb7f
commit 594ceeb5d1
No known key found for this signature in database
GPG Key ID: ED20A1D1D423AF3F
9 changed files with 101 additions and 6 deletions

View File

@ -36,6 +36,7 @@ func Analyze(
d.Indexes = i d.Indexes = i
analyzeValuesAreValid(ctx) analyzeValuesAreValid(ctx)
analyzeIgnoreUnknownHasNoUnnecessary(ctx)
analyzeDependents(ctx) analyzeDependents(ctx)
analyzeBlocks(ctx) analyzeBlocks(ctx)
analyzeMatchBlocks(ctx) analyzeMatchBlocks(ctx)

View File

@ -10,6 +10,10 @@ func analyzeBlocks(
ctx *analyzerContext, ctx *analyzerContext,
) { ) {
for _, block := range ctx.document.GetAllBlocks() { for _, block := range ctx.document.GetAllBlocks() {
if block == nil {
continue
}
if block.GetOptions().Size() == 0 { if block.GetOptions().Size() == 0 {
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: block.GetEntryOption().LocationRange.ToLSPRange(), Range: block.GetEntryOption().LocationRange.ToLSPRange(),

View File

@ -0,0 +1,39 @@
package analyzer
import (
"config-lsp/common"
"config-lsp/handlers/ssh_config/fields"
"fmt"
protocol "github.com/tliron/glsp/protocol_3_16"
)
var ignoreUnknownOption = fields.CreateNormalizedName("IgnoreUnknown")
func analyzeIgnoreUnknownHasNoUnnecessary(
ctx *analyzerContext,
) {
for _, block := range ctx.document.GetAllBlocks() {
ignoreUnknown, found := ctx.document.Indexes.IgnoredOptions[block]
if !found {
// No `IgnoreUnknown` option specified
continue
}
for optionName, ignoreInfo := range ignoreUnknown.IgnoredOptions {
info := ctx.document.FindOptionByNameAndBlock(optionName, block)
if info == nil {
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: ignoreInfo.ToLSPRange(),
Message: fmt.Sprintf("Option %s is not present", optionName),
Tags: []protocol.DiagnosticTag{
protocol.DiagnosticTagUnnecessary,
},
Severity: &common.SeverityHint,
})
}
}
}
}

View File

@ -0,0 +1,26 @@
package analyzer
import (
testutils_test "config-lsp/handlers/ssh_config/test_utils"
"testing"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TestIgnoreUnknownUnnecessary(
t *testing.T,
) {
d := testutils_test.DocumentFromInput(t, `
IgnoreUnknown helloWorld
PermitRootLogin 'yes'
`)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeIgnoreUnknownHasNoUnnecessary(ctx)
if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
}
}

View File

@ -72,8 +72,10 @@ func (d SSHDocument) GetAllHostBlocks() []*ast.SSHHostBlock {
// GetAllBlocks returns all blocks in the document // GetAllBlocks returns all blocks in the document
// Note: The blocks are **not** sorted // Note: The blocks are **not** sorted
// Note: This also returns `nil` (as the global block)
func (d SSHDocument) GetAllBlocks() []ast.SSHBlock { func (d SSHDocument) GetAllBlocks() []ast.SSHBlock {
blocks := make([]ast.SSHBlock, 0) blocks := make([]ast.SSHBlock, 0)
blocks = append(blocks, nil)
for _, block := range d.GetAllHostBlocks() { for _, block := range d.GetAllHostBlocks() {
blocks = append(blocks, block) blocks = append(blocks, block)

View File

@ -26,9 +26,13 @@ type SSHIndexIncludeLine struct {
Block ast.SSHBlock Block ast.SSHBlock
} }
type SSHIndexIgnoredUnknownInfo struct {
common.LocationRange
}
type SSHIndexIgnoredUnknowns struct { type SSHIndexIgnoredUnknowns struct {
OptionValue *ast.SSHOption OptionValue *ast.SSHOption
IgnoredOptions map[fields.NormalizedOptionName]struct{} IgnoredOptions map[fields.NormalizedOptionName]SSHIndexIgnoredUnknownInfo
} }
type SSHIndexes struct { type SSHIndexes struct {

View File

@ -5,7 +5,7 @@ import (
"config-lsp/handlers/ssh_config/fields" "config-lsp/handlers/ssh_config/fields"
) )
func (u SSHIndexIgnoredUnknowns) GetIgnoredForLine(line uint32) map[fields.NormalizedOptionName]struct{} { func (u SSHIndexIgnoredUnknowns) GetIgnoredForLine(line uint32) map[fields.NormalizedOptionName]SSHIndexIgnoredUnknownInfo {
if line >= u.OptionValue.Start.Line { if line >= u.OptionValue.Start.Line {
return u.IgnoredOptions return u.IgnoredOptions
} }

View File

@ -151,11 +151,26 @@ func addIgnoredOption(
block ast.SSHBlock, block ast.SSHBlock,
) { ) {
rawIgnored := option.OptionValue.Value.Value rawIgnored := option.OptionValue.Value.Value
ignoredAsSlice := ignoredValuesPattern.FindAllString(rawIgnored, -1) ignoredAsSlice := ignoredValuesPattern.FindAllStringIndex(rawIgnored, -1)
ignored := make(map[fields.NormalizedOptionName]struct{}, 0) ignored := make(map[fields.NormalizedOptionName]SSHIndexIgnoredUnknownInfo, 0)
for _, ig := range ignoredAsSlice { for _, ignoreInfo := range ignoredAsSlice {
ignored[fields.CreateNormalizedName(ig)] = struct{}{} start := ignoreInfo[0]
end := ignoreInfo[1]
name := rawIgnored[start:end]
ignored[fields.CreateNormalizedName(name)] = SSHIndexIgnoredUnknownInfo{
LocationRange: common.LocationRange{
Start: common.Location{
Line: option.OptionValue.Start.Line,
Character: option.OptionValue.Start.Character + uint32(start),
},
End: common.Location{
Line: option.OptionValue.End.Line,
Character: option.OptionValue.Start.Character + uint32(end),
},
},
}
} }
i.IgnoredOptions[block] = SSHIndexIgnoredUnknowns{ i.IgnoredOptions[block] = SSHIndexIgnoredUnknowns{

View File

@ -112,4 +112,8 @@ UseKeychain yes
if !(len(indexes.IgnoredOptions[nil].IgnoredOptions) == 1 && utils.KeyExists(indexes.IgnoredOptions[nil].IgnoredOptions, "usekeychain")) { if !(len(indexes.IgnoredOptions[nil].IgnoredOptions) == 1 && utils.KeyExists(indexes.IgnoredOptions[nil].IgnoredOptions, "usekeychain")) {
t.Errorf("Expected IgnoreOptions to contain 'UseKeychain', but got: %v", indexes.IgnoredOptions[nil].IgnoredOptions) t.Errorf("Expected IgnoreOptions to contain 'UseKeychain', but got: %v", indexes.IgnoredOptions[nil].IgnoredOptions)
} }
if !(indexes.IgnoredOptions[nil].IgnoredOptions["usekeychain"].Start.Line == 0 && indexes.IgnoredOptions[nil].IgnoredOptions["usekeychain"].Start.Character == 14 && indexes.IgnoredOptions[nil].IgnoredOptions["usekeychain"].End.Character == 25) {
t.Errorf("Expected IgnoreOptions to contain 'UseKeychain' on line 0 and from position 14-24, but got: %v", indexes.IgnoredOptions[nil].IgnoredOptions)
}
} }