Skip to content

Commit fcf51f7

Browse files
committed
Tighten suggestion-algorithm comments
1 parent 8ceaf73 commit fcf51f7

4 files changed

Lines changed: 15 additions & 36 deletions

File tree

src/runtime/Types/ClassBase.cs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -838,17 +838,13 @@ private static Dictionary<string, SuggestionKind> GetCandidateMemberNames(Type t
838838
// string when no member is similar enough to suggest. The result is cached in
839839
// _suggestionCache, so this runs at most once per (type, missing-name).
840840
//
841-
// Similarity is Jaro-Winkler rather than a Levenshtein threshold: the prefix-favoring
842-
// measure keeps suffix-extended real targets that an edit-distance cutoff rejects
843-
// (BrokerageName.InteractiveBrokers -> INTERACTIVE_BROKERS_BROKERAGE is 11 edits away
844-
// but 0.92 similar), while naturally rejecting the short-name noise edit distance
845-
// admits ('cash' is within 2 edits of 'ASI'). Substring containment is kept as a
846-
// fallback signal for fragment lookups Jaro-Winkler cannot see (its match window
847-
// rules out 'cash' vs 'set_cash'), but only for fragments long enough to be
848-
// meaningful, so 1-2 letter members no longer qualify for every long missed name.
841+
// Jaro-Winkler (prefix-favoring) keeps suffix-extended targets that an edit-distance
842+
// cutoff rejects (InteractiveBrokers -> INTERACTIVE_BROKERS_BROKERAGE); gated
843+
// containment covers fragment lookups outside its match window ('cash' -> 'set_cash').
849844
private static string ComputeSimilarMemberNames(Type type, string name)
850845
{
851846
const int MaxSuggestions = 5;
847+
// In evaluation over real member sets, intended targets scored >= 0.90 and noise <= 0.85.
852848
const double SimilarityThreshold = 0.87;
853849

854850
var scored = new List<(string Name, double Score, SuggestionKind Kind)>();
@@ -857,8 +853,7 @@ private static string ComputeSimilarMemberNames(Type type, string name)
857853
var score = JaroWinklerSimilarity(name, candidate.Key);
858854
if (score < SimilarityThreshold)
859855
{
860-
// Containment matches score by how much of the longer name the fragment
861-
// covers, so they always rank below any Jaro-Winkler match.
856+
// Coverage scoring ranks containment matches below any similarity match.
862857
score = IsMeaningfulContainment(name, candidate.Key)
863858
? (double)Math.Min(name.Length, candidate.Key.Length) / Math.Max(name.Length, candidate.Key.Length)
864859
: 0;
@@ -910,11 +905,8 @@ private static (string Name, SuggestionKind Kind) ToSnakeCaseMemberName(MemberIn
910905
};
911906
}
912907

913-
// A containment signal is only trustworthy when the contained fragment carries real
914-
// information: at least 3 characters, and a candidate contained in the missed name
915-
// must additionally cover at least half of it. Without the length gates every 1-2
916-
// letter member (single-letter methods, greek-letter properties) is a substring of
917-
// any long missed name and floods the suggestion list.
908+
// Without the length gates every 1-2 letter member is a substring of any long
909+
// missed name and floods the suggestion list.
918910
private static bool IsMeaningfulContainment(string name, string candidate)
919911
{
920912
const int MinFragmentLength = 3;
@@ -931,10 +923,7 @@ private static bool IsMeaningfulContainment(string name, string candidate)
931923
}
932924

933925
/// <summary>
934-
/// Case-insensitive Jaro-Winkler similarity in [0, 1]: the Jaro similarity (matching
935-
/// characters within a sliding window, penalizing transpositions) boosted by up to
936-
/// 0.1 per shared prefix character (capped at 4), so names that agree on their
937-
/// leading characters rank higher than names with the same edit distance elsewhere.
926+
/// Case-insensitive Jaro-Winkler similarity in [0, 1] (Jaro boosted by shared prefix).
938927
/// </summary>
939928
private static double JaroWinklerSimilarity(string a, string b)
940929
{

src/testing/classtest.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,8 @@ public static int[] CalculationResults()
8787

8888
public static int CalculationResult { get; set; }
8989

90-
// Short members: every one of these is a substring of a longer missed name like
91-
// 'set_account_type', so suggestion tests can assert they are not offered as
92-
// suggestions for it while a similarly-named longer member is.
90+
// Short members, all substrings of a longer miss like 'set_account_type', which
91+
// must not suggest them.
9392
public static int T()
9493
{
9594
return 0;
@@ -106,8 +105,7 @@ public static void SetAccountCurrency(string currency)
106105
}
107106

108107
/// <summary>
109-
/// Supports missing-attribute suggestion tests for enum values whose real name extends
110-
/// the guessed name with an extra suffix (a common miss on enum-like constant sets).
108+
/// Supports suggestion tests for enum members that extend the guessed name with a suffix.
111109
/// </summary>
112110
public enum SuggestionEnum
113111
{

tests/test_class.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,8 @@ def _suggestions(message):
220220
def test_missing_attribute_does_not_suggest_short_members():
221221
"""A long missed name must not collect 1-2 letter members via substring containment.
222222
223-
Types like QCAlgorithm expose many 1-2 letter members (indicator shortcuts, greek
224-
letters); every one of them is a substring of a long missed name, so they used to
225-
flood the hint (e.g. 'set_account_type' -> "Did you mean: 'cc', 'co', 'a', 'c',
226-
't'?") while the member the user most likely meant was not within the edit-distance
227-
threshold and did not appear at all.
223+
Every short member is a substring of a long miss, so hints used to read
224+
"Did you mean: 'cc', 'co', 'a', 'c', 't'?" while the intended member was absent.
228225
"""
229226
from Python.Test import SuggestionTest
230227

tests/test_enum.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,8 @@ def test_missing_enum_member_hasattr_still_false():
6969

7070

7171
def test_missing_enum_member_suffix_extended_name_suggested():
72-
"""A guessed name that the real member extends with a suffix must be suggested.
73-
74-
Enum-like constant sets often have members that extend the natural guess (e.g.
75-
BrokerageName.INTERACTIVE_BROKERS_BROKERAGE for a guessed INTERACTIVE_BROKERS);
76-
such members are many edits away, so a pure edit-distance threshold missed them.
77-
Both the PascalCase and the UPPER_SNAKE guess must surface every extension.
78-
"""
72+
"""A guessed name that the real member extends with a suffix must be suggested,
73+
from both the PascalCase and the UPPER_SNAKE spelling of the guess."""
7974
import re
8075
from Python.Test import SuggestionEnum
8176

0 commit comments

Comments
 (0)