2025-03-16 14:56:42 +01:00

113 lines
3.2 KiB
Go

package analyzer
import (
"config-lsp/common"
docvalues "config-lsp/doc-values"
"config-lsp/handlers/sshd_config/ast"
"config-lsp/handlers/sshd_config/diagnostics"
"config-lsp/handlers/sshd_config/fields"
"config-lsp/utils"
"fmt"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func analyzeStructureIsValid(
ctx *analyzerContext,
) {
it := ctx.document.Config.Options.Iterator()
for it.Next() {
entry := it.Value().(ast.SSHDEntry)
switch entry.(type) {
case *ast.SSHDOption:
checkOption(ctx, entry.(*ast.SSHDOption), nil)
case *ast.SSHDMatchBlock:
matchBlock := entry.(*ast.SSHDMatchBlock)
checkMatchBlock(ctx, matchBlock)
}
}
}
func checkOption(
ctx *analyzerContext,
option *ast.SSHDOption,
matchBlock *ast.SSHDMatchBlock,
) {
if option.Key == nil {
return
}
///// General checks
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
if option.Separator == nil || option.Separator.Value.Value == "" {
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: option.Key.LocationRange.ToLSPRange(),
Message: "There should be a separator between an option and its value",
Severity: &common.SeverityError,
})
} else {
checkIsUsingDoubleQuotes(ctx, option.Separator.Value, option.Separator.LocationRange)
checkQuotesAreClosed(ctx, option.Separator.Value, option.Separator.LocationRange)
}
///// Check if the key is valid
docOption, optionFound := fields.Options[option.Key.Key]
if !optionFound {
ctx.diagnostics = append(ctx.diagnostics, diagnostics.GenerateUnknownOption(
option.Key.ToLSPRange(),
option.Key.Value.Value,
))
ctx.document.Indexes.UnknownOptions[option.Start.Line] = ast.SSHDOptionInfo{
Option: option,
MatchBlock: matchBlock,
}
// Since we don't know the option, we can't verify the value
return
// Check for values that are not allowed in Match blocks
} else if matchBlock != nil && !utils.KeyExists(fields.MatchAllowedOptions, option.Key.Key) {
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: option.Key.ToLSPRange(),
Message: fmt.Sprintf("Option '%s' is not allowed in Match blocks", option.Key.Key),
Severity: &common.SeverityError,
})
}
///// Check if the value is valid
if option.OptionValue != nil {
checkIsUsingDoubleQuotes(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
checkQuotesAreClosed(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
invalidValues := docOption.DeprecatedCheckIsValid(option.OptionValue.Value.Value)
for _, invalidValue := range invalidValues {
err := docvalues.LSPErrorFromInvalidValue(option.Start.Line, *invalidValue).ShiftCharacter(option.OptionValue.Start.Character)
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: err.Range.ToLSPRange(),
Message: err.Err.Error(),
Severity: &common.SeverityError,
})
}
}
}
func checkMatchBlock(
ctx *analyzerContext,
matchBlock *ast.SSHDMatchBlock,
) {
it := matchBlock.Options.Iterator()
for it.Next() {
option := it.Value().(*ast.SSHDOption)
checkOption(ctx, option, matchBlock)
}
}