From 45940aef5ce3b131dac35a057c2772bbd21167ce Mon Sep 17 00:00:00 2001 From: i4k Date: Tue, 3 Sep 2024 14:15:57 +0100 Subject: [PATCH] refactor: remove name suggestion in evaluation. Signed-off-by: i4k --- didyoumean.go | 27 ------------------ hclsyntax/didyoumean.go | 27 ------------------ hclsyntax/didyoumean_test.go | 54 ------------------------------------ hclsyntax/expression.go | 19 ++----------- hclsyntax/parser_template.go | 8 +----- hclsyntax/structure.go | 34 ++--------------------- json/didyoumean.go | 36 ------------------------ json/didyoumean_test.go | 52 ---------------------------------- json/parser.go | 7 +---- json/structure.go | 7 +---- traversal.go | 15 +--------- 11 files changed, 8 insertions(+), 278 deletions(-) delete mode 100644 didyoumean.go delete mode 100644 hclsyntax/didyoumean.go delete mode 100644 hclsyntax/didyoumean_test.go delete mode 100644 json/didyoumean.go delete mode 100644 json/didyoumean_test.go diff --git a/didyoumean.go b/didyoumean.go deleted file mode 100644 index fd00ca6f..00000000 --- a/didyoumean.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package hcl - -import ( - "github.com/agext/levenshtein" -) - -// nameSuggestion tries to find a name from the given slice of suggested names -// that is close to the given name and returns it if found. If no suggestion -// is close enough, returns the empty string. -// -// The suggestions are tried in order, so earlier suggestions take precedence -// if the given string is similar to two or more suggestions. -// -// This function is intended to be used with a relatively-small number of -// suggestions. It's not optimized for hundreds or thousands of them. -func nameSuggestion(given string, suggestions []string) string { - for _, suggestion := range suggestions { - dist := levenshtein.Distance(given, suggestion, nil) - if dist < 3 { // threshold determined experimentally - return suggestion - } - } - return "" -} diff --git a/hclsyntax/didyoumean.go b/hclsyntax/didyoumean.go deleted file mode 100644 index 5b0e4681..00000000 --- a/hclsyntax/didyoumean.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package hclsyntax - -import ( - "github.com/agext/levenshtein" -) - -// nameSuggestion tries to find a name from the given slice of suggested names -// that is close to the given name and returns it if found. If no suggestion -// is close enough, returns the empty string. -// -// The suggestions are tried in order, so earlier suggestions take precedence -// if the given string is similar to two or more suggestions. -// -// This function is intended to be used with a relatively-small number of -// suggestions. It's not optimized for hundreds or thousands of them. -func nameSuggestion(given string, suggestions []string) string { - for _, suggestion := range suggestions { - dist := levenshtein.Distance(given, suggestion, nil) - if dist < 3 { // threshold determined experimentally - return suggestion - } - } - return "" -} diff --git a/hclsyntax/didyoumean_test.go b/hclsyntax/didyoumean_test.go deleted file mode 100644 index 84cfed6d..00000000 --- a/hclsyntax/didyoumean_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package hclsyntax - -import "testing" - -func TestNameSuggestion(t *testing.T) { - var keywords = []string{"false", "true", "null"} - - tests := []struct { - Input, Want string - }{ - {"true", "true"}, - {"false", "false"}, - {"null", "null"}, - {"bananas", ""}, - {"NaN", ""}, - {"Inf", ""}, - {"Infinity", ""}, - {"void", ""}, - {"undefined", ""}, - - {"ture", "true"}, - {"tru", "true"}, - {"tre", "true"}, - {"treu", "true"}, - {"rtue", "true"}, - - {"flase", "false"}, - {"fales", "false"}, - {"flse", "false"}, - {"fasle", "false"}, - {"fasel", "false"}, - {"flue", "false"}, - - {"nil", "null"}, - {"nul", "null"}, - {"unll", "null"}, - {"nll", "null"}, - } - - for _, test := range tests { - t.Run(test.Input, func(t *testing.T) { - got := nameSuggestion(test.Input, keywords) - if got != test.Want { - t.Errorf( - "wrong result\ninput: %q\ngot: %q\nwant: %q", - test.Input, got, test.Want, - ) - } - }) - } -} diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 6a4def0d..171df6ee 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -302,16 +302,11 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti }, } } else { - suggestion := nameSuggestion(name, avail) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %s%s?", namespace, suggestion) - } - return cty.DynamicVal, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Call to unknown function", - Detail: fmt.Sprintf("There is no function named %q in namespace %s.%s", name, namespace, suggestion), + Detail: fmt.Sprintf("There is no function named %q in namespace %s", name, namespace), Subject: &e.NameRange, Context: e.Range().Ptr(), Expression: e, @@ -321,21 +316,11 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti } } } - - avail := make([]string, 0, len(ctx.Functions)) - for name := range ctx.Functions { - avail = append(avail, name) - } - suggestion := nameSuggestion(e.Name, avail) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - return cty.DynamicVal, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Call to unknown function", - Detail: fmt.Sprintf("There is no function named %q.%s", e.Name, suggestion), + Detail: fmt.Sprintf("There is no function named %q", e.Name), Subject: &e.NameRange, Context: e.Range().Ptr(), Expression: e, diff --git a/hclsyntax/parser_template.go b/hclsyntax/parser_template.go index f5b8b6a6..53af13e9 100644 --- a/hclsyntax/parser_template.go +++ b/hclsyntax/parser_template.go @@ -597,17 +597,11 @@ Token: default: if !p.recovery { - suggestions := []string{"if", "for", "else", "endif", "endfor"} given := string(kw.Bytes) - suggestion := nameSuggestion(given, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid template control keyword", - Detail: fmt.Sprintf("%q is not a valid template control keyword.%s", given, suggestion), + Detail: fmt.Sprintf("%q is not a valid template control keyword", given), Subject: &kw.Range, Context: hcl.RangeBetween(next.Range, kw.Range).Ptr(), }) diff --git a/hclsyntax/structure.go b/hclsyntax/structure.go index 6274cc21..dc37f83f 100644 --- a/hclsyntax/structure.go +++ b/hclsyntax/structure.go @@ -69,23 +69,10 @@ func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostic } suggestions = append(suggestions, attrS.Name) } - suggestion := nameSuggestion(name, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } else { - // Is there a block of the same name? - for _, blockS := range schema.Blocks { - if blockS.Type == name { - suggestion = fmt.Sprintf(" Did you mean to define a block of type %q?", name) - break - } - } - } - diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported argument", - Detail: fmt.Sprintf("An argument named %q is not expected here.%s", name, suggestion), + Detail: fmt.Sprintf("An argument named %q is not expected here", name), Subject: &attr.NameRange, }) } @@ -94,27 +81,10 @@ func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostic for _, block := range b.Blocks { blockTy := block.Type if _, hidden := remain.hiddenBlocks[blockTy]; !hidden { - var suggestions []string - for _, blockS := range schema.Blocks { - suggestions = append(suggestions, blockS.Type) - } - suggestion := nameSuggestion(blockTy, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } else { - // Is there an attribute of the same name? - for _, attrS := range schema.Attributes { - if attrS.Name == blockTy { - suggestion = fmt.Sprintf(" Did you mean to define argument %q? If so, use the equals sign to assign it a value.", blockTy) - break - } - } - } - diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported block type", - Detail: fmt.Sprintf("Blocks of type %q are not expected here.%s", blockTy, suggestion), + Detail: fmt.Sprintf("Blocks of type %q are not expected here", blockTy), Subject: &block.TypeRange, }) } diff --git a/json/didyoumean.go b/json/didyoumean.go deleted file mode 100644 index dc004c4b..00000000 --- a/json/didyoumean.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package json - -import ( - "github.com/agext/levenshtein" -) - -var keywords = []string{"false", "true", "null"} - -// keywordSuggestion tries to find a valid JSON keyword that is close to the -// given string and returns it if found. If no keyword is close enough, returns -// the empty string. -func keywordSuggestion(given string) string { - return nameSuggestion(given, keywords) -} - -// nameSuggestion tries to find a name from the given slice of suggested names -// that is close to the given name and returns it if found. If no suggestion -// is close enough, returns the empty string. -// -// The suggestions are tried in order, so earlier suggestions take precedence -// if the given string is similar to two or more suggestions. -// -// This function is intended to be used with a relatively-small number of -// suggestions. It's not optimized for hundreds or thousands of them. -func nameSuggestion(given string, suggestions []string) string { - for _, suggestion := range suggestions { - dist := levenshtein.Distance(given, suggestion, nil) - if dist < 3 { // threshold determined experimentally - return suggestion - } - } - return "" -} diff --git a/json/didyoumean_test.go b/json/didyoumean_test.go deleted file mode 100644 index f189790b..00000000 --- a/json/didyoumean_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package json - -import "testing" - -func TestKeywordSuggestion(t *testing.T) { - tests := []struct { - Input, Want string - }{ - {"true", "true"}, - {"false", "false"}, - {"null", "null"}, - {"bananas", ""}, - {"NaN", ""}, - {"Inf", ""}, - {"Infinity", ""}, - {"void", ""}, - {"undefined", ""}, - - {"ture", "true"}, - {"tru", "true"}, - {"tre", "true"}, - {"treu", "true"}, - {"rtue", "true"}, - - {"flase", "false"}, - {"fales", "false"}, - {"flse", "false"}, - {"fasle", "false"}, - {"fasel", "false"}, - {"flue", "false"}, - - {"nil", "null"}, - {"nul", "null"}, - {"unll", "null"}, - {"nll", "null"}, - } - - for _, test := range tests { - t.Run(test.Input, func(t *testing.T) { - got := keywordSuggestion(test.Input) - if got != test.Want { - t.Errorf( - "wrong result\ninput: %q\ngot: %q\nwant: %q", - test.Input, got, test.Want, - ) - } - }) - } -} diff --git a/json/parser.go b/json/parser.go index 539e1fa5..33279517 100644 --- a/json/parser.go +++ b/json/parser.go @@ -490,16 +490,11 @@ func parseKeyword(p *peeker) (node, hcl.Diagnostics) { }, } default: - var dym string - if suggest := keywordSuggestion(s); suggest != "" { - dym = fmt.Sprintf(" Did you mean %q?", suggest) - } - return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid JSON keyword", - Detail: fmt.Sprintf("%q is not a valid JSON keyword.%s", s, dym), + Detail: fmt.Sprintf("%q is not a valid JSON keyword", s), Subject: &tok.Range, }, } diff --git a/json/structure.go b/json/structure.go index fccab54c..aae84e55 100644 --- a/json/structure.go +++ b/json/structure.go @@ -59,15 +59,10 @@ func (b *body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostic } if _, ok := hiddenAttrs[k]; !ok { - suggestion := nameSuggestion(k, nameSuggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extraneous JSON object property", - Detail: fmt.Sprintf("No argument or block type is named %q.%s", k, suggestion), + Detail: fmt.Sprintf("No argument or block type is named %q", k), Subject: &attr.NameRange, Context: attr.Range().Ptr(), }) diff --git a/traversal.go b/traversal.go index 540dde7e..6d5dc7ac 100644 --- a/traversal.go +++ b/traversal.go @@ -97,24 +97,11 @@ func (t Traversal) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) { } } - suggestions := make([]string, 0, len(ctx.Variables)) - thisCtx = ctx - for thisCtx != nil { - for k := range thisCtx.Variables { - suggestions = append(suggestions, k) - } - thisCtx = thisCtx.parent - } - suggestion := nameSuggestion(name, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: "Unknown variable", - Detail: fmt.Sprintf("There is no variable named %q.%s", name, suggestion), + Detail: fmt.Sprintf("There is no variable named %q", name), Subject: &root.SrcRange, }, }