mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(aliases): Add duplicates analyzer
This commit is contained in:
parent
bbe083621e
commit
d7f8b905a4
@ -27,6 +27,7 @@ func Analyze(
|
||||
}
|
||||
|
||||
errors = append(errors, analyzeContainsRequiredKeys(*d)...)
|
||||
errors = append(errors, analyzeContainsNoDoubleValues(*d.Parser)...)
|
||||
|
||||
return utils.Map(
|
||||
errors,
|
||||
|
193
handlers/aliases/analyzer/double_values.go
Normal file
193
handlers/aliases/analyzer/double_values.go
Normal file
@ -0,0 +1,193 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
"config-lsp/handlers/aliases/indexes"
|
||||
"config-lsp/utils"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var valueHandlerMap = map[string]func(
|
||||
rawValue []ast.AliasValueInterface,
|
||||
) []common.LSPError{
|
||||
"AliasValueUser": analyzeValueUser,
|
||||
"AliasValueEmail": analyzeValueEmail,
|
||||
"AliasValueCommand": analyzeValueCommand,
|
||||
"AliasValueFile": analyzeValueFile,
|
||||
"AliasValueInclude": analyzeValueInclude,
|
||||
"AliasValueError": analyzeValueError,
|
||||
}
|
||||
|
||||
func analyzeContainsNoDoubleValues(
|
||||
p ast.AliasesParser,
|
||||
) []common.LSPError {
|
||||
errors := make([]common.LSPError, 0)
|
||||
|
||||
it := p.Aliases.Iterator()
|
||||
|
||||
for it.Next() {
|
||||
entry := it.Value().(*ast.AliasEntry)
|
||||
|
||||
valuesPerType := utils.Group(
|
||||
entry.Values.Values,
|
||||
func(entry ast.AliasValueInterface) string {
|
||||
return entry.GetStructName()
|
||||
},
|
||||
)
|
||||
|
||||
for valueType, values := range valuesPerType {
|
||||
handler := valueHandlerMap[valueType]
|
||||
|
||||
newErrors := handler(values)
|
||||
errors = append(errors, newErrors...)
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func analyzeValueUser(
|
||||
rawValues []ast.AliasValueInterface,
|
||||
) []common.LSPError {
|
||||
users := make(map[string]struct{})
|
||||
errs := make([]common.LSPError, 0)
|
||||
|
||||
// Simple double value check
|
||||
for _, rawValue := range rawValues {
|
||||
value := rawValue.(ast.AliasValueUser)
|
||||
key := indexes.NormalizeKey(value.Value)
|
||||
|
||||
if _, found := users[key]; found {
|
||||
errs = append(errs, common.LSPError{
|
||||
Range: value.Location,
|
||||
Err: errors.New(fmt.Sprintf("User '%s' is defined multiple times", key)),
|
||||
})
|
||||
} else {
|
||||
users[key] = struct{}{}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func analyzeValueEmail(
|
||||
rawValues []ast.AliasValueInterface,
|
||||
) []common.LSPError {
|
||||
emails := make(map[string]struct{})
|
||||
errs := make([]common.LSPError, 0)
|
||||
|
||||
for _, rawValue := range rawValues {
|
||||
value := rawValue.(ast.AliasValueEmail)
|
||||
|
||||
// Simple double value check
|
||||
if _, found := emails[value.Value]; found {
|
||||
errs = append(errs, common.LSPError{
|
||||
Range: value.Location,
|
||||
Err: errors.New(fmt.Sprintf("Email '%s' is defined multiple times", value.Value)),
|
||||
})
|
||||
} else {
|
||||
emails[value.Value] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func analyzeValueCommand(
|
||||
rawValues []ast.AliasValueInterface,
|
||||
) []common.LSPError {
|
||||
commands := make(map[string]struct{})
|
||||
errs := make([]common.LSPError, 0)
|
||||
|
||||
for _, rawValue := range rawValues {
|
||||
value := rawValue.(ast.AliasValueCommand)
|
||||
command := value.Command
|
||||
|
||||
// Simple double value check
|
||||
if _, found := commands[command]; found {
|
||||
errs = append(errs, common.LSPError{
|
||||
Range: value.Location,
|
||||
Err: errors.New(fmt.Sprintf("Command '%s' is defined multiple times", command)),
|
||||
})
|
||||
} else {
|
||||
commands[command] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func analyzeValueFile(
|
||||
rawValues []ast.AliasValueInterface,
|
||||
) []common.LSPError {
|
||||
files := make(map[string]struct{})
|
||||
errs := make([]common.LSPError, 0)
|
||||
|
||||
for _, rawValue := range rawValues {
|
||||
value := rawValue.(ast.AliasValueFile)
|
||||
path := string(value.Path)
|
||||
|
||||
// Simple double value check
|
||||
if _, found := files[path]; found {
|
||||
errs = append(errs, common.LSPError{
|
||||
Range: value.Location,
|
||||
Err: errors.New(fmt.Sprintf("File '%s' is defined multiple times", path)),
|
||||
})
|
||||
} else {
|
||||
files[path] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func analyzeValueInclude(
|
||||
rawValues []ast.AliasValueInterface,
|
||||
) []common.LSPError {
|
||||
files := make(map[string]struct{})
|
||||
errs := make([]common.LSPError, 0)
|
||||
|
||||
for _, rawValue := range rawValues {
|
||||
value := rawValue.(ast.AliasValueInclude)
|
||||
path := string(value.Path.Path)
|
||||
|
||||
// Simple double value check
|
||||
if _, found := files[path]; found {
|
||||
errs = append(errs, common.LSPError{
|
||||
Range: value.Location,
|
||||
Err: errors.New(fmt.Sprintf("Inclusion '%s' is included multiple times", path)),
|
||||
})
|
||||
} else {
|
||||
files[path] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func analyzeValueError(
|
||||
rawValues []ast.AliasValueInterface,
|
||||
) []common.LSPError {
|
||||
codes := make(map[uint16]struct{})
|
||||
errs := make([]common.LSPError, 0)
|
||||
|
||||
for _, rawValue := range rawValues {
|
||||
value := rawValue.(ast.AliasValueError)
|
||||
code := value.Code.ErrorCodeAsInt()
|
||||
|
||||
// Simple double value check
|
||||
if _, found := codes[code]; found {
|
||||
errs = append(errs, common.LSPError{
|
||||
Range: value.Location,
|
||||
Err: errors.New(fmt.Sprintf("Error code '%d' is defined multiple times", code)),
|
||||
})
|
||||
} else {
|
||||
codes[code] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
119
handlers/aliases/analyzer/double_values_test.go
Normal file
119
handlers/aliases/analyzer/double_values_test.go
Normal file
@ -0,0 +1,119 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
"config-lsp/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestContainsDoubleUsers(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
alice: root
|
||||
master: alice, alice
|
||||
`)
|
||||
p := ast.NewAliasesParser()
|
||||
errors := p.Parse(input)
|
||||
|
||||
// d := aliases.AliasesDocument{
|
||||
// Parser: &p,
|
||||
// }
|
||||
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
errors = analyzeContainsNoDoubleValues(p)
|
||||
|
||||
if !(len(errors) == 1) {
|
||||
t.Errorf("Expected errors, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsDoubleEmails(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
alice: root@localhost, some, noise, here, root@localhost
|
||||
`)
|
||||
p := ast.NewAliasesParser()
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
errors = analyzeContainsNoDoubleValues(p)
|
||||
|
||||
if !(len(errors) == 1) {
|
||||
t.Errorf("Expected errors, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsDoubleCommands(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
alice: |echo, |test, |echo
|
||||
`)
|
||||
p := ast.NewAliasesParser()
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
errors = analyzeContainsNoDoubleValues(p)
|
||||
|
||||
if !(len(errors) == 1) {
|
||||
t.Errorf("Expected errors, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsDoubleErrors(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
alice: error:450 Nonono, error:450 Some other message
|
||||
root: error:450 Nonon, error:451 This is not okay
|
||||
`)
|
||||
p := ast.NewAliasesParser()
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
errors = analyzeContainsNoDoubleValues(p)
|
||||
|
||||
if !(len(errors) == 1) {
|
||||
t.Errorf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
if !(errors[0].Range.Start.Line == 0 && errors[0].Range.End.Line == 0) {
|
||||
t.Errorf("Expected error to be on line 0, got %v-%v", errors[0].Range.Start.Line, errors[0].Range.End.Line)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComplexExampleContainsNoDoubleValues(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
alice: root@localhost, user@localhost
|
||||
master: alice, root
|
||||
noreply: error:450 Nonono
|
||||
`)
|
||||
p := ast.NewAliasesParser()
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
errors = analyzeContainsNoDoubleValues(p)
|
||||
|
||||
if !(len(errors) == 0) {
|
||||
t.Errorf("Expected no errors, got %v", errors)
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
type AliasValueInterface interface {
|
||||
GetAliasValue() AliasValue
|
||||
GetStructName() string
|
||||
}
|
||||
|
||||
func (a AliasValue) String() string {
|
||||
@ -21,6 +22,10 @@ func (a AliasValue) GetAliasValue() AliasValue {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a AliasValue) GetStructName() string {
|
||||
return "AliasValue"
|
||||
}
|
||||
|
||||
type AliasValue struct {
|
||||
Location common.LocationRange
|
||||
Value string
|
||||
@ -30,6 +35,10 @@ type AliasValueUser struct {
|
||||
AliasValue
|
||||
}
|
||||
|
||||
func (a AliasValueUser) GetStructName() string {
|
||||
return "AliasValueUser"
|
||||
}
|
||||
|
||||
type path string
|
||||
|
||||
type AliasValueFile struct {
|
||||
@ -37,13 +46,8 @@ type AliasValueFile struct {
|
||||
Path path
|
||||
}
|
||||
|
||||
func (a AliasValueFile) CheckIsValid() []common.LSPError {
|
||||
return utils.Map(
|
||||
fields.PathField.CheckIsValid(string(a.Path)),
|
||||
func(invalidValue *docvalues.InvalidValue) common.LSPError {
|
||||
return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue)
|
||||
},
|
||||
)
|
||||
func (a AliasValueFile) GetStructName() string {
|
||||
return "AliasValueFile"
|
||||
}
|
||||
|
||||
type AliasValueCommand struct {
|
||||
@ -51,13 +55,8 @@ type AliasValueCommand struct {
|
||||
Command string
|
||||
}
|
||||
|
||||
func (a AliasValueCommand) CheckIsValid() []common.LSPError {
|
||||
return utils.Map(
|
||||
fields.CommandField.CheckIsValid(a.Command),
|
||||
func(invalidValue *docvalues.InvalidValue) common.LSPError {
|
||||
return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue)
|
||||
},
|
||||
)
|
||||
func (a AliasValueCommand) GetStructName() string {
|
||||
return "AliasValueCommand"
|
||||
}
|
||||
|
||||
type AliasValueIncludePath struct {
|
||||
@ -79,6 +78,10 @@ func (a AliasValueInclude) CheckIsValid() []common.LSPError {
|
||||
)
|
||||
}
|
||||
|
||||
func (a AliasValueInclude) GetStructName() string {
|
||||
return "AliasValueInclude"
|
||||
}
|
||||
|
||||
type AliasValueEmail struct {
|
||||
AliasValue
|
||||
}
|
||||
@ -92,6 +95,10 @@ func (a AliasValueEmail) CheckIsValid() []common.LSPError {
|
||||
)
|
||||
}
|
||||
|
||||
func (a AliasValueEmail) GetStructName() string {
|
||||
return "AliasValueEmail"
|
||||
}
|
||||
|
||||
type AliasValueError struct {
|
||||
AliasValue
|
||||
|
||||
@ -103,6 +110,10 @@ type AliasValueErrorCode struct {
|
||||
AliasValue
|
||||
}
|
||||
|
||||
func (a AliasValueError) GetStructName() string {
|
||||
return "AliasValueError"
|
||||
}
|
||||
|
||||
func (a AliasValueErrorCode) ErrorCodeAsInt() uint16 {
|
||||
code, err := strconv.Atoi(a.Value)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user