Bug Summary

File:build/gcc/spellcheck.h
Warning:line 150, column 32
The left operand of '!=' is a garbage value due to array index out of bounds

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-suse-linux -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name spellcheck.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/buildworker/marxinbox-gcc-clang-static-analyzer/objdir/gcc -resource-dir /usr/lib64/clang/15.0.7 -D IN_GCC -D HAVE_CONFIG_H -I . -I . -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/. -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../include -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libcpp/include -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libcody -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libdecnumber -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libdecnumber/bid -I ../libdecnumber -I /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libbacktrace -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../include/c++/13/x86_64-suse-linux -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../include/c++/13/backward -internal-isystem /usr/lib64/clang/15.0.7/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-suse-linux/13/../../../../x86_64-suse-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-narrowing -Wwrite-strings -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings -fdeprecated-macro -fdebug-compilation-dir=/buildworker/marxinbox-gcc-clang-static-analyzer/objdir/gcc -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=plist-html -analyzer-config silence-checkers=core.NullDereference -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /buildworker/marxinbox-gcc-clang-static-analyzer/objdir/clang-static-analyzer/2023-03-27-141847-20772-1/report-FNZj60.plist -x c++ /buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc

/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc

1/* Find near-matches for strings.
2 Copyright (C) 2015-2023 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License
17along with GCC; see the file COPYING3. If not see
18<http://www.gnu.org/licenses/>. */
19
20#include "config.h"
21#include "system.h"
22#include "coretypes.h"
23#include "tm.h"
24#include "tree.h"
25#include "spellcheck.h"
26#include "selftest.h"
27
28/* Cost of a case transformation. */
29#define CASE_COST1 1
30
31/* Cost of another kind of edit. */
32#define BASE_COST2 2
33
34/* Get the edit distance between the two strings: the minimal
35 number of edits that are needed to change one string into another,
36 where edits can be one-character insertions, removals, or substitutions,
37 or transpositions of two adjacent characters (counting as one "edit").
38
39 This implementation uses a modified variant of the Wagner-Fischer
40 algorithm for the Damerau-Levenshtein distance; specifically, the
41 "optimal string alignment distance" or "restricted edit distance"
42 variant. This implementation has been further modified to take
43 case into account. */
44
45edit_distance_t
46get_edit_distance (const char *s, int len_s,
47 const char *t, int len_t)
48{
49 const bool debug = false;
50
51 if (debug)
52 {
53 printf ("s: \"%s\" (len_s=%i)\n", s, len_s);
54 printf ("t: \"%s\" (len_t=%i)\n", t, len_t);
55 }
56
57 if (len_s == 0)
58 return BASE_COST2 * len_t;
59 if (len_t == 0)
60 return BASE_COST2 * len_s;
61
62 /* We effectively build a matrix where each (i, j) contains the
63 distance between the prefix strings s[0:j] and t[0:i].
64 Rather than actually build an (len_t + 1) * (len_s + 1) matrix,
65 we simply keep track of the last two rows, v_one_ago and v_two_ago,
66 and a new row, v_next, which avoids an (len_t + 1) * (len_s + 1)
67 allocation and memory accesses in favor of three (len_s + 1)
68 allocations. These could potentially be
69 statically-allocated if we impose a maximum length on the
70 strings of interest. */
71 edit_distance_t *v_two_ago = new edit_distance_t[len_s + 1];
72 edit_distance_t *v_one_ago = new edit_distance_t[len_s + 1];
73 edit_distance_t *v_next = new edit_distance_t[len_s + 1];
74
75 /* The first row is for the case of an empty target string, which
76 we can reach by deleting every character in the source string. */
77 for (int i = 0; i < len_s + 1; i++)
78 v_one_ago[i] = i * BASE_COST2;
79
80 /* Build successive rows. */
81 for (int i = 0; i < len_t; i++)
82 {
83 if (debug)
84 {
85 printf ("i:%i v_one_ago = ", i);
86 for (int j = 0; j < len_s + 1; j++)
87 printf ("%i ", v_one_ago[j]);
88 printf ("\n");
89 }
90
91 /* The initial column is for the case of an empty source string; we
92 can reach prefixes of the target string of length i
93 by inserting i characters. */
94 v_next[0] = (i + 1) * BASE_COST2;
95
96 /* Build the rest of the row by considering neighbors to
97 the north, west and northwest. */
98 for (int j = 0; j < len_s; j++)
99 {
100 edit_distance_t cost;
101
102 if (s[j] == t[i])
103 cost = 0;
104 else if (TOLOWER (s[j])_sch_tolower[(s[j]) & 0xff] == TOLOWER (t[i])_sch_tolower[(t[i]) & 0xff])
105 cost = CASE_COST1;
106 else
107 cost = BASE_COST2;
108 edit_distance_t deletion = v_next[j] + BASE_COST2;
109 edit_distance_t insertion = v_one_ago[j + 1] + BASE_COST2;
110 edit_distance_t substitution = v_one_ago[j] + cost;
111 edit_distance_t cheapest = MIN (deletion, insertion)((deletion) < (insertion) ? (deletion) : (insertion));
112 cheapest = MIN (cheapest, substitution)((cheapest) < (substitution) ? (cheapest) : (substitution)
)
;
113 if (i > 0 && j > 0 && s[j] == t[i - 1] && s[j - 1] == t[i])
114 {
115 edit_distance_t transposition = v_two_ago[j - 1] + BASE_COST2;
116 cheapest = MIN (cheapest, transposition)((cheapest) < (transposition) ? (cheapest) : (transposition
))
;
117 }
118 v_next[j + 1] = cheapest;
119 }
120
121 /* Prepare to move on to next row. */
122 for (int j = 0; j < len_s + 1; j++)
123 {
124 v_two_ago[j] = v_one_ago[j];
125 v_one_ago[j] = v_next[j];
126 }
127 }
128
129 if (debug)
130 {
131 printf ("final v_next = ");
132 for (int j = 0; j < len_s + 1; j++)
133 printf ("%i ", v_next[j]);
134 printf ("\n");
135 }
136
137 edit_distance_t result = v_next[len_s];
138 delete[] v_two_ago;
139 delete[] v_one_ago;
140 delete[] v_next;
141 return result;
142}
143
144/* Get the edit distance between two nil-terminated strings. */
145
146edit_distance_t
147get_edit_distance (const char *s, const char *t)
148{
149 return get_edit_distance (s, strlen (s), t, strlen (t));
150}
151
152/* Given TARGET, a non-NULL string, and CANDIDATES, a non-NULL ptr to
153 an autovec of non-NULL strings, determine which element within
154 CANDIDATES has the lowest edit distance to TARGET. If there are
155 multiple elements with the same minimal distance, the first in the
156 vector wins.
157
158 If more than half of the letters were misspelled, the suggestion is
159 likely to be meaningless, so return NULL for this case. */
160
161const char *
162find_closest_string (const char *target,
163 const auto_vec<const char *> *candidates)
164{
165 gcc_assert (target)((void)(!(target) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 165, __FUNCTION__), 0 : 0))
;
3
'?' condition is false
166 gcc_assert (candidates)((void)(!(candidates) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 166, __FUNCTION__), 0 : 0))
;
4
'?' condition is false
167
168 int i;
169 const char *candidate;
170 best_match<const char *, const char *> bm (target);
171 FOR_EACH_VEC_ELT (*candidates, i, candidate)for (i = 0; (*candidates).iterate ((i), &(candidate)); ++
(i))
5
Loop condition is true. Entering loop body
172 {
173 gcc_assert (candidate)((void)(!(candidate) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 173, __FUNCTION__), 0 : 0))
;
6
Assuming 'candidate' is non-null
7
'?' condition is false
174 bm.consider (candidate);
8
Calling 'best_match::consider'
175 }
176
177 return bm.get_best_meaningful_candidate ();
178}
179
180/* Generate the maximum edit distance for which we consider a suggestion
181 to be meaningful, given a goal of length GOAL_LEN and a candidate of
182 length CANDIDATE_LEN.
183
184 This is a third of the length of the candidate or of the goal,
185 whichever is bigger. */
186
187edit_distance_t
188get_edit_distance_cutoff (size_t goal_len, size_t candidate_len)
189{
190 size_t max_length = MAX (goal_len, candidate_len)((goal_len) > (candidate_len) ? (goal_len) : (candidate_len
))
;
191 size_t min_length = MIN (goal_len, candidate_len)((goal_len) < (candidate_len) ? (goal_len) : (candidate_len
))
;
192
193 gcc_assert (max_length >= min_length)((void)(!(max_length >= min_length) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 193, __FUNCTION__), 0 : 0))
;
194
195 /* Special case: don't offer suggestions for a pair of
196 length == 1 strings (or empty strings). */
197 if (max_length <= 1)
198 return 0;
199
200 /* If the lengths are close, then round down. */
201 if (max_length - min_length <= 1)
202 /* ...but allow an edit distance of at least 1. */
203 return BASE_COST2 * MAX (max_length / 3, 1)((max_length / 3) > (1) ? (max_length / 3) : (1));
204
205 /* Otherwise, round up (thus giving a little extra leeway to some cases
206 involving insertions/deletions). */
207 return BASE_COST2 * (max_length + 2) / 3;
208}
209
210#if CHECKING_P1
211
212namespace selftest {
213
214/* Selftests. */
215
216/* Verify that get_edit_distance (A, B) equals the expected value. */
217
218static void
219test_get_edit_distance_one_way (const char *a, const char *b,
220 edit_distance_t expected)
221{
222 edit_distance_t actual = get_edit_distance (a, b);
223 ASSERT_EQ (actual, expected)do { const char *desc_ = "ASSERT_EQ (" "(actual)" ", " "(expected)"
")"; if (((actual)) == ((expected))) ::selftest::pass ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 223, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 223, __FUNCTION__)))), desc_); } while (0)
;
224}
225
226/* Verify that both
227 get_edit_distance (A, B)
228 and
229 get_edit_distance (B, A)
230 equal the expected value, to ensure that the function is symmetric. */
231
232static void
233test_get_edit_distance_both_ways (const char *a, const char *b,
234 edit_distance_t expected)
235{
236 test_get_edit_distance_one_way (a, b, expected);
237 test_get_edit_distance_one_way (b, a, expected);
238}
239
240/* Verify get_edit_distance for a variety of pairs of pre-canned
241 inputs, comparing against known-good values. */
242
243static void
244test_edit_distances ()
245{
246 test_get_edit_distance_both_ways ("", "nonempty",
247 BASE_COST2 * strlen ("nonempty"));
248 test_get_edit_distance_both_ways ("saturday", "sunday",
249 BASE_COST2 * 3);
250 test_get_edit_distance_both_ways ("foo", "m_foo", BASE_COST2 * 2);
251 test_get_edit_distance_both_ways ("hello_world", "HelloWorld", 4);
252 test_get_edit_distance_both_ways
253 ("the quick brown fox jumps over the lazy dog", "dog", BASE_COST2 * 40);
254 test_get_edit_distance_both_ways
255 ("the quick brown fox jumps over the lazy dog",
256 "the quick brown dog jumps over the lazy fox",
257 BASE_COST2 * 4);
258 test_get_edit_distance_both_ways
259 ("Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
260 "All your base are belong to us",
261 BASE_COST2 * 44);
262 test_get_edit_distance_both_ways ("foo", "FOO", 3);
263 test_get_edit_distance_both_ways ("fee", "deed", BASE_COST2 * 2);
264 test_get_edit_distance_both_ways ("coorzd1", "coordx1", BASE_COST2 * 2);
265 test_get_edit_distance_both_ways ("assert", "sqrt", BASE_COST2 * 3);
266 test_get_edit_distance_both_ways ("PATH_MAX", "INT8_MAX", BASE_COST2 * 3);
267 test_get_edit_distance_both_ways ("time", "nice", BASE_COST2 * 2);
268 test_get_edit_distance_both_ways ("bar", "carg", BASE_COST2 * 2);
269 test_get_edit_distance_both_ways ("gtk_widget_show_all",
270 "GtkWidgetShowAll", 10);
271 test_get_edit_distance_both_ways ("m_bar", "bar", BASE_COST2 * 2);
272 test_get_edit_distance_both_ways ("MACRO", "MACRAME", BASE_COST2 * 3);
273 test_get_edit_distance_both_ways ("ab", "ac", BASE_COST2 * 1);
274 test_get_edit_distance_both_ways ("ab", "a", BASE_COST2 * 1);
275 test_get_edit_distance_both_ways ("a", "b", BASE_COST2 * 1);
276 test_get_edit_distance_both_ways ("nanl", "name", BASE_COST2 * 2);
277 test_get_edit_distance_both_ways ("char", "bar", BASE_COST2 * 2);
278 test_get_edit_distance_both_ways ("-optimize", "fsanitize", BASE_COST2 * 5);
279 test_get_edit_distance_both_ways ("__DATE__", "__i386__", BASE_COST2 * 4);
280
281 /* Examples where transposition helps. */
282 test_get_edit_distance_both_ways ("ab", "ba", BASE_COST2 * 1);
283 test_get_edit_distance_both_ways ("ba", "abc", BASE_COST2 * 2);
284 test_get_edit_distance_both_ways ("coorzd1", "coordz1", BASE_COST2 * 1);
285 test_get_edit_distance_both_ways ("abcdefghijklmnopqrstuvwxyz",
286 "bacdefghijklmnopqrstuvwxzy",
287 BASE_COST2 * 2);
288 test_get_edit_distance_both_ways ("saturday", "sundya", BASE_COST2 * 4);
289 test_get_edit_distance_both_ways ("signed", "singed", BASE_COST2 * 1);
290}
291
292/* Subroutine of test_get_edit_distance_cutoff, for emulating the
293 spellchecking cutoff in up to GCC 8. */
294
295static edit_distance_t
296get_old_cutoff (size_t goal_len, size_t candidate_len)
297{
298 return BASE_COST2 * MAX (goal_len, candidate_len)((goal_len) > (candidate_len) ? (goal_len) : (candidate_len
))
/ 2;
299}
300
301/* Verify that the cutoff for "meaningfulness" of suggestions is at least as
302 conservative as in older GCC releases.
303
304 This should ensure that we don't offer additional meaningless
305 suggestions (apart from those for which transposition has helped). */
306
307static void
308test_get_edit_distance_cutoff ()
309{
310 for (size_t goal_len = 0; goal_len < 30; goal_len++)
311 for (size_t candidate_len = 0; candidate_len < 30; candidate_len++)
312 ASSERT_TRUE (get_edit_distance_cutoff (goal_len, candidate_len)do { const char *desc_ = "ASSERT_TRUE (" "(get_edit_distance_cutoff (goal_len, candidate_len) <= get_old_cutoff (goal_len, candidate_len))"
")"; bool actual_ = ((get_edit_distance_cutoff (goal_len, candidate_len
) <= get_old_cutoff (goal_len, candidate_len))); if (actual_
) ::selftest::pass (((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 313, __FUNCTION__))), desc_); else ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 313, __FUNCTION__))), desc_); } while (0)
313 <= get_old_cutoff (goal_len, candidate_len))do { const char *desc_ = "ASSERT_TRUE (" "(get_edit_distance_cutoff (goal_len, candidate_len) <= get_old_cutoff (goal_len, candidate_len))"
")"; bool actual_ = ((get_edit_distance_cutoff (goal_len, candidate_len
) <= get_old_cutoff (goal_len, candidate_len))); if (actual_
) ::selftest::pass (((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 313, __FUNCTION__))), desc_); else ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 313, __FUNCTION__))), desc_); } while (0)
;
314}
315
316/* Assert that CANDIDATE is offered as a suggestion for TARGET. */
317
318static void
319assert_suggested_for (const location &loc, const char *candidate,
320 const char *target)
321{
322 auto_vec<const char *> candidates;
323 candidates.safe_push (candidate);
324 ASSERT_EQ_AT (loc, candidate, find_closest_string (target, &candidates))do { const char *desc_ = "ASSERT_EQ (" "candidate" ", " "find_closest_string (target, &candidates)"
")"; if ((candidate) == (find_closest_string (target, &candidates
))) ::selftest::pass ((loc), desc_); else ::selftest::fail ((
loc), desc_); } while (0)
;
325}
326
327/* Assert that CANDIDATE is offered as a suggestion for TARGET. */
328
329#define ASSERT_SUGGESTED_FOR(CANDIDATE, TARGET)do { assert_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 329, __FUNCTION__)), CANDIDATE, TARGET); } while (0)
\
330 SELFTEST_BEGIN_STMTdo { \
331 assert_suggested_for (SELFTEST_LOCATION(::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 331, __FUNCTION__))
, CANDIDATE, TARGET); \
332 SELFTEST_END_STMT} while (0)
333
334/* Assert that CANDIDATE is not offered as a suggestion for TARGET. */
335
336static void
337assert_not_suggested_for (const location &loc, const char *candidate,
338 const char *target)
339{
340 auto_vec<const char *> candidates;
341 candidates.safe_push (candidate);
342 ASSERT_EQ_AT (loc, NULL, find_closest_string (target, &candidates))do { const char *desc_ = "ASSERT_EQ (" "NULL" ", " "find_closest_string (target, &candidates)"
")"; if ((nullptr) == (find_closest_string (target, &candidates
))) ::selftest::pass ((loc), desc_); else ::selftest::fail ((
loc), desc_); } while (0)
;
343}
344
345/* Assert that CANDIDATE is not offered as a suggestion for TARGET. */
346
347#define ASSERT_NOT_SUGGESTED_FOR(CANDIDATE, TARGET)do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 347, __FUNCTION__)), CANDIDATE, TARGET); } while (0)
\
348 SELFTEST_BEGIN_STMTdo { \
349 assert_not_suggested_for (SELFTEST_LOCATION(::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 349, __FUNCTION__))
, CANDIDATE, TARGET); \
350 SELFTEST_END_STMT} while (0)
351
352/* Verify that we offer varous suggestions that are meaningful,
353 and that we don't offer various other ones that aren't (PR c/82967). */
354
355static void
356test_suggestions ()
357{
358 /* Good suggestions. */
359
360 ASSERT_SUGGESTED_FOR ("m_bar", "bar")do { assert_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 360, __FUNCTION__)), "m_bar", "bar"); } while (0)
;
361 // dist == 2, max_length == 5, min_length == 3
362
363 ASSERT_SUGGESTED_FOR ("MACRO", "MACRAME")do { assert_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 363, __FUNCTION__)), "MACRO", "MACRAME"); } while (0)
;
364 // dist == 3, max_length == 7, min_length == 5
365
366 ASSERT_SUGGESTED_FOR ("gtk_widget_show_all", "GtkWidgetShowAll")do { assert_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 366, __FUNCTION__)), "gtk_widget_show_all", "GtkWidgetShowAll"
); } while (0)
;
367 // dist == 7, max_length == 16, min_length = 19
368
369 ASSERT_SUGGESTED_FOR ("ab", "ac")do { assert_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 369, __FUNCTION__)), "ab", "ac"); } while (0)
;
370 // dist == 1, max_length == min_length = 2
371
372 ASSERT_SUGGESTED_FOR ("ab", "a")do { assert_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 372, __FUNCTION__)), "ab", "a"); } while (0)
;
373 // dist == 1, max_length == 2, min_length = 1
374
375 /* Bad suggestions. */
376
377 ASSERT_NOT_SUGGESTED_FOR ("a", "b")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 377, __FUNCTION__)), "a", "b"); } while (0)
;
378 // dist == 1, max_length == min_length = 1
379
380 ASSERT_NOT_SUGGESTED_FOR ("sqrt", "assert")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 380, __FUNCTION__)), "sqrt", "assert"); } while (0)
;
381 // dist == 3, max_length 6, min_length == 4
382
383 ASSERT_NOT_SUGGESTED_FOR ("INT8_MAX", "PATH_MAX")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 383, __FUNCTION__)), "INT8_MAX", "PATH_MAX"); } while (0)
;
384 // dist == 3, max_length == min_length == 8
385
386 ASSERT_NOT_SUGGESTED_FOR ("nice", "time")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 386, __FUNCTION__)), "nice", "time"); } while (0)
;
387 ASSERT_NOT_SUGGESTED_FOR ("nanl", "name")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 387, __FUNCTION__)), "nanl", "name"); } while (0)
;
388 // dist == 2, max_length == min_length == 4
389
390 ASSERT_NOT_SUGGESTED_FOR ("carg", "bar")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 390, __FUNCTION__)), "carg", "bar"); } while (0)
;
391 ASSERT_NOT_SUGGESTED_FOR ("char", "bar")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 391, __FUNCTION__)), "char", "bar"); } while (0)
;
392 // dist == 2, max_length == 4, min_length == 3
393
394 ASSERT_NOT_SUGGESTED_FOR ("-optimize", "fsanitize")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 394, __FUNCTION__)), "-optimize", "fsanitize"); } while (0)
;
395 // dist == 5, max_length == min_length == 9
396
397 ASSERT_NOT_SUGGESTED_FOR ("__DATE__", "__i386__")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 397, __FUNCTION__)), "__DATE__", "__i386__"); } while (0)
;
398 // dist == 4, max_length == min_length == 8
399
400 ASSERT_NOT_SUGGESTED_FOR ("start_input_device", "InputDevice")do { assert_not_suggested_for ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 400, __FUNCTION__)), "start_input_device", "InputDevice"); }
while (0)
;
401 // dist == 9, max_length == 18, min_length == 11
402}
403
404/* Verify that find_closest_string is sane. */
405
406static void
407test_find_closest_string ()
408{
409 auto_vec<const char *> candidates;
410
411 /* Verify that it can handle an empty vec. */
412 ASSERT_EQ (NULL, find_closest_string ("", &candidates))do { const char *desc_ = "ASSERT_EQ (" "(nullptr)" ", " "(find_closest_string (\"\", &candidates))"
")"; if (((nullptr)) == ((find_closest_string ("", &candidates
)))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 412, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 412, __FUNCTION__)))), desc_); } while (0)
;
2
Calling 'find_closest_string'
413
414 /* Verify that it works sanely for non-empty vecs. */
415 candidates.safe_push ("apple");
416 candidates.safe_push ("banana");
417 candidates.safe_push ("cherry");
418
419 ASSERT_STREQ ("apple", find_closest_string ("app", &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 419, __FUNCTION__)), "\"apple\"", "find_closest_string (\"app\", &candidates)"
, ("apple"), (find_closest_string ("app", &candidates)));
} while (0)
;
420 ASSERT_STREQ ("banana", find_closest_string ("banyan", &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 420, __FUNCTION__)), "\"banana\"", "find_closest_string (\"banyan\", &candidates)"
, ("banana"), (find_closest_string ("banyan", &candidates
))); } while (0)
;
421 ASSERT_STREQ ("cherry", find_closest_string ("berry", &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 421, __FUNCTION__)), "\"cherry\"", "find_closest_string (\"berry\", &candidates)"
, ("cherry"), (find_closest_string ("berry", &candidates)
)); } while (0)
;
422 ASSERT_EQ (NULL, find_closest_string ("not like the others", &candidates))do { const char *desc_ = "ASSERT_EQ (" "(nullptr)" ", " "(find_closest_string (\"not like the others\", &candidates))"
")"; if (((nullptr)) == ((find_closest_string ("not like the others"
, &candidates)))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 422, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 422, __FUNCTION__)))), desc_); } while (0)
;
423
424 /* The order of the vec can matter, but it should not matter for these
425 inputs. */
426 candidates.truncate (0);
427 candidates.safe_push ("cherry");
428 candidates.safe_push ("banana");
429 candidates.safe_push ("apple");
430 ASSERT_STREQ ("apple", find_closest_string ("app", &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 430, __FUNCTION__)), "\"apple\"", "find_closest_string (\"app\", &candidates)"
, ("apple"), (find_closest_string ("app", &candidates)));
} while (0)
;
431 ASSERT_STREQ ("banana", find_closest_string ("banyan", &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 431, __FUNCTION__)), "\"banana\"", "find_closest_string (\"banyan\", &candidates)"
, ("banana"), (find_closest_string ("banyan", &candidates
))); } while (0)
;
432 ASSERT_STREQ ("cherry", find_closest_string ("berry", &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 432, __FUNCTION__)), "\"cherry\"", "find_closest_string (\"berry\", &candidates)"
, ("cherry"), (find_closest_string ("berry", &candidates)
)); } while (0)
;
433 ASSERT_EQ (NULL, find_closest_string ("not like the others", &candidates))do { const char *desc_ = "ASSERT_EQ (" "(nullptr)" ", " "(find_closest_string (\"not like the others\", &candidates))"
")"; if (((nullptr)) == ((find_closest_string ("not like the others"
, &candidates)))) ::selftest::pass ((((::selftest::location
("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 433, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 433, __FUNCTION__)))), desc_); } while (0)
;
434
435 /* If the goal string somehow makes it into the candidate list, offering
436 it as a suggestion will be nonsensical. Verify that we don't offer such
437 suggestions. */
438 ASSERT_EQ (NULL, find_closest_string ("banana", &candidates))do { const char *desc_ = "ASSERT_EQ (" "(nullptr)" ", " "(find_closest_string (\"banana\", &candidates))"
")"; if (((nullptr)) == ((find_closest_string ("banana", &
candidates)))) ::selftest::pass ((((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 438, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 438, __FUNCTION__)))), desc_); } while (0)
;
439
440 /* Example from PR 69968 where transposition helps. */
441 candidates.truncate (0);
442 candidates.safe_push("coordx");
443 candidates.safe_push("coordy");
444 candidates.safe_push("coordz");
445 candidates.safe_push("coordx1");
446 candidates.safe_push("coordy1");
447 candidates.safe_push("coordz1");
448 ASSERT_STREQ ("coordz1", find_closest_string ("coorzd1", &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 448, __FUNCTION__)), "\"coordz1\"", "find_closest_string (\"coorzd1\", &candidates)"
, ("coordz1"), (find_closest_string ("coorzd1", &candidates
))); } while (0)
;
449
450 candidates.truncate (0);
451 candidates.safe_push ("DWARF_GNAT_ENCODINGS_GDB");
452 candidates.safe_push ("DWARF_GNAT_ENCODINGS_ALL");
453 candidates.safe_push ("DWARF_GNAT_ENCODINGS_MINIMAL");
454 ASSERT_STREQ ("DWARF_GNAT_ENCODINGS_ALL",do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 456, __FUNCTION__)), "\"DWARF_GNAT_ENCODINGS_ALL\"", "find_closest_string (\"DWARF_GNAT_ENCODINGS_all\", &candidates)"
, ("DWARF_GNAT_ENCODINGS_ALL"), (find_closest_string ("DWARF_GNAT_ENCODINGS_all"
, &candidates))); } while (0)
455 find_closest_string ("DWARF_GNAT_ENCODINGS_all",do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 456, __FUNCTION__)), "\"DWARF_GNAT_ENCODINGS_ALL\"", "find_closest_string (\"DWARF_GNAT_ENCODINGS_all\", &candidates)"
, ("DWARF_GNAT_ENCODINGS_ALL"), (find_closest_string ("DWARF_GNAT_ENCODINGS_all"
, &candidates))); } while (0)
456 &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 456, __FUNCTION__)), "\"DWARF_GNAT_ENCODINGS_ALL\"", "find_closest_string (\"DWARF_GNAT_ENCODINGS_all\", &candidates)"
, ("DWARF_GNAT_ENCODINGS_ALL"), (find_closest_string ("DWARF_GNAT_ENCODINGS_all"
, &candidates))); } while (0)
;
457
458 /* The same as the previous test, but with a different order of
459 candidates. */
460 candidates.truncate (0);
461 candidates.safe_push ("DWARF_GNAT_ENCODINGS_ALL");
462 candidates.safe_push ("DWARF_GNAT_ENCODINGS_GDB");
463 candidates.safe_push ("DWARF_GNAT_ENCODINGS_MINIMAL");
464 ASSERT_STREQ ("DWARF_GNAT_ENCODINGS_ALL",do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 466, __FUNCTION__)), "\"DWARF_GNAT_ENCODINGS_ALL\"", "find_closest_string (\"DWARF_GNAT_ENCODINGS_all\", &candidates)"
, ("DWARF_GNAT_ENCODINGS_ALL"), (find_closest_string ("DWARF_GNAT_ENCODINGS_all"
, &candidates))); } while (0)
465 find_closest_string ("DWARF_GNAT_ENCODINGS_all",do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 466, __FUNCTION__)), "\"DWARF_GNAT_ENCODINGS_ALL\"", "find_closest_string (\"DWARF_GNAT_ENCODINGS_all\", &candidates)"
, ("DWARF_GNAT_ENCODINGS_ALL"), (find_closest_string ("DWARF_GNAT_ENCODINGS_all"
, &candidates))); } while (0)
466 &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 466, __FUNCTION__)), "\"DWARF_GNAT_ENCODINGS_ALL\"", "find_closest_string (\"DWARF_GNAT_ENCODINGS_all\", &candidates)"
, ("DWARF_GNAT_ENCODINGS_ALL"), (find_closest_string ("DWARF_GNAT_ENCODINGS_all"
, &candidates))); } while (0)
;
467
468 /* Example from PR 105564 where option name with missing equal
469 sign should win. */
470 candidates.truncate (0);
471 candidates.safe_push ("-Wtrivial-auto-var-init");
472 candidates.safe_push ("-ftrivial-auto-var-init=");
473 ASSERT_STREQ ("-ftrivial-auto-var-init=",do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 475, __FUNCTION__)), "\"-ftrivial-auto-var-init=\"", "find_closest_string (\"-ftrivial-auto-var-init\", &candidates)"
, ("-ftrivial-auto-var-init="), (find_closest_string ("-ftrivial-auto-var-init"
, &candidates))); } while (0)
474 find_closest_string ("-ftrivial-auto-var-init",do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 475, __FUNCTION__)), "\"-ftrivial-auto-var-init=\"", "find_closest_string (\"-ftrivial-auto-var-init\", &candidates)"
, ("-ftrivial-auto-var-init="), (find_closest_string ("-ftrivial-auto-var-init"
, &candidates))); } while (0)
475 &candidates))do { ::selftest::assert_streq ((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 475, __FUNCTION__)), "\"-ftrivial-auto-var-init=\"", "find_closest_string (\"-ftrivial-auto-var-init\", &candidates)"
, ("-ftrivial-auto-var-init="), (find_closest_string ("-ftrivial-auto-var-init"
, &candidates))); } while (0)
;
476}
477
478/* Test data for test_metric_conditions. */
479
480static const char * const test_data[] = {
481 "",
482 "foo",
483 "food",
484 "boo",
485 "1234567890123456789012345678901234567890123456789012345678901234567890",
486 "abc",
487 "ac",
488 "ca",
489};
490
491/* Verify that get_edit_distance appears to be a sane distance function,
492 even though it doesn't satisfy the conditions for being a metric. (This
493 is because the triangle inequality fails to hold: the distance between
494 "ca" and "ac" is 1, and so is the distance between "abc" and "ac", but
495 the distance between "abc" and "ca" is 3. Algorithms that calculate the
496 true Levenshtein-Damerau metric are much more expensive.) */
497
498static void
499test_metric_conditions ()
500{
501 const int num_test_cases = ARRAY_SIZE (test_data)(sizeof (test_data) / sizeof ((test_data)[0]));
502
503 for (int i = 0; i < num_test_cases; i++)
504 {
505 for (int j = 0; j < num_test_cases; j++)
506 {
507 edit_distance_t dist_ij
508 = get_edit_distance (test_data[i], test_data[j]);
509
510 /* Identity of indiscernibles: d(i, j) > 0 iff i == j. */
511 if (i == j)
512 ASSERT_EQ (dist_ij, 0)do { const char *desc_ = "ASSERT_EQ (" "(dist_ij)" ", " "(0)"
")"; if (((dist_ij)) == ((0))) ::selftest::pass ((((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 512, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 512, __FUNCTION__)))), desc_); } while (0)
;
513 else
514 ASSERT_TRUE (dist_ij > 0)do { const char *desc_ = "ASSERT_TRUE (" "(dist_ij > 0)" ")"
; bool actual_ = ((dist_ij > 0)); if (actual_) ::selftest::
pass (((::selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 514, __FUNCTION__))), desc_); else ::selftest::fail (((::selftest
::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 514, __FUNCTION__))), desc_); } while (0)
;
515
516 /* Symmetry: d(i, j) == d(j, i). */
517 edit_distance_t dist_ji
518 = get_edit_distance (test_data[j], test_data[i]);
519 ASSERT_EQ (dist_ij, dist_ji)do { const char *desc_ = "ASSERT_EQ (" "(dist_ij)" ", " "(dist_ji)"
")"; if (((dist_ij)) == ((dist_ji))) ::selftest::pass ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 519, __FUNCTION__)))), desc_); else ::selftest::fail ((((::
selftest::location ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.cc"
, 519, __FUNCTION__)))), desc_); } while (0)
;
520 }
521 }
522}
523
524/* Run all of the selftests within this file. */
525
526void
527spellcheck_cc_tests ()
528{
529 test_edit_distances ();
530 test_get_edit_distance_cutoff ();
531 test_suggestions ();
532 test_find_closest_string ();
1
Calling 'test_find_closest_string'
533 test_metric_conditions ();
534}
535
536} // namespace selftest
537
538#endif /* #if CHECKING_P */

/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.h

1/* Find near-matches for strings and identifiers.
2 Copyright (C) 2015-2023 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License
17along with GCC; see the file COPYING3. If not see
18<http://www.gnu.org/licenses/>. */
19
20#ifndef GCC_SPELLCHECK_H
21#define GCC_SPELLCHECK_H
22
23typedef unsigned int edit_distance_t;
24const edit_distance_t MAX_EDIT_DISTANCE = UINT_MAX(2147483647 *2U +1U);
25
26/* spellcheck.cc */
27extern edit_distance_t
28get_edit_distance (const char *s, int len_s,
29 const char *t, int len_t);
30
31extern edit_distance_t
32get_edit_distance (const char *s, const char *t);
33
34extern const char *
35find_closest_string (const char *target,
36 const auto_vec<const char *> *candidates);
37
38/* A traits class for describing a string-like type usable by
39 class best_match.
40 Specializations should provide the implementations of the following:
41
42 static size_t get_length (TYPE);
43 static const char *get_string (TYPE);
44
45 get_string should return a non-NULL ptr, which does not need to be
46 0-terminated. */
47
48template <typename TYPE>
49struct edit_distance_traits {};
50
51/* Specialization of edit_distance_traits for C-style strings. */
52
53template <>
54struct edit_distance_traits<const char *>
55{
56 static size_t get_length (const char *str)
57 {
58 gcc_assert (str)((void)(!(str) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.h"
, 58, __FUNCTION__), 0 : 0))
;
59 return strlen (str);
60 }
61
62 static const char *get_string (const char *str)
63 {
64 gcc_assert (str)((void)(!(str) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.h"
, 64, __FUNCTION__), 0 : 0))
;
65 return str;
66 }
67};
68
69extern edit_distance_t get_edit_distance_cutoff (size_t goal_len,
70 size_t candidate_len);
71
72/* A type for use when determining the best match against a string,
73 expressed as a template so that we can match against various
74 string-like types (const char *, frontend identifiers, and preprocessor
75 macros).
76
77 This type accumulates the best possible match against GOAL_TYPE for
78 a sequence of elements of CANDIDATE_TYPE, whilst minimizing the
79 number of calls to get_edit_distance and to
80 edit_distance_traits<T>::get_length. */
81
82template <typename GOAL_TYPE, typename CANDIDATE_TYPE>
83class best_match
84{
85 public:
86 typedef GOAL_TYPE goal_t;
87 typedef CANDIDATE_TYPE candidate_t;
88 typedef edit_distance_traits<goal_t> goal_traits;
89 typedef edit_distance_traits<candidate_t> candidate_traits;
90
91 /* Constructor. */
92
93 best_match (GOAL_TYPE goal,
94 edit_distance_t best_distance_so_far = MAX_EDIT_DISTANCE)
95 : m_goal (goal_traits::get_string (goal)),
96 m_goal_len (goal_traits::get_length (goal)),
97 m_best_candidate (NULLnullptr),
98 m_best_distance (best_distance_so_far),
99 m_best_candidate_len (0)
100 {}
101
102 /* Compare the edit distance between CANDIDATE and m_goal,
103 and if it's the best so far, record it. */
104
105 void consider (candidate_t candidate)
106 {
107 size_t candidate_len = candidate_traits::get_length (candidate);
108
109 /* Calculate a lower bound on the candidate's distance to the goal,
110 based on the difference in lengths; it will require at least
111 this many insertions/deletions. */
112 edit_distance_t min_candidate_distance
113 = abs ((ssize_t)candidate_len - (ssize_t)m_goal_len);
114
115 /* If the candidate's length is sufficiently different to that
116 of the goal string, then the number of insertions/deletions
117 may be >= the best distance so far. If so, we can reject
118 the candidate immediately without needing to compute
119 the exact distance, since it won't be an improvement. */
120 if (min_candidate_distance >= m_best_distance)
9
Assuming 'min_candidate_distance' is < field 'm_best_distance'
10
Taking false branch
121 return;
122
123 /* If the candidate will be unable to beat the criterion in
124 get_best_meaningful_candidate, reject it without computing
125 the exact distance. */
126 edit_distance_t cutoff = get_cutoff (candidate_len);
127 if (min_candidate_distance > cutoff)
11
Assuming 'min_candidate_distance' is <= 'cutoff'
12
Taking false branch
128 return;
129
130 /* Otherwise, compute the distance and see if the candidate
131 has beaten the previous best value. */
132 const char *candidate_str = candidate_traits::get_string (candidate);
133 edit_distance_t dist
134 = get_edit_distance (m_goal, m_goal_len, candidate_str, candidate_len);
135
136 bool is_better = false;
137 if (dist < m_best_distance)
13
Assuming 'dist' is >= field 'm_best_distance'
14
Taking false branch
138 is_better = true;
139 else if (dist
14.1
'dist' is equal to field 'm_best_distance'
14.1
'dist' is equal to field 'm_best_distance'
== m_best_distance)
140 {
141 /* Prefer a candidate that inserts a trailing '=',
142 so that for
143 "-ftrivial-auto-var-init"
144 we suggest
145 "-ftrivial-auto-var-init="
146 rather than
147 "-Wtrivial-auto-var-init". */
148 /* Prefer a candidate has a difference in trailing sign character. */
149 if (candidate_str[candidate_len - 1] == '='
15
Assuming the condition is true
150 && m_goal[m_goal_len - 1] != '=')
16
The left operand of '!=' is a garbage value due to array index out of bounds
151 is_better = true;
152 }
153
154 if (is_better)
155 {
156 m_best_distance = dist;
157 m_best_candidate = candidate;
158 m_best_candidate_len = candidate_len;
159 }
160 }
161
162 /* Assuming that BEST_CANDIDATE is known to be better than
163 m_best_candidate, update (without recomputing the edit distance to
164 the goal). */
165
166 void set_best_so_far (CANDIDATE_TYPE best_candidate,
167 edit_distance_t best_distance,
168 size_t best_candidate_len)
169 {
170 gcc_assert (best_distance < m_best_distance)((void)(!(best_distance < m_best_distance) ? fancy_abort (
"/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/spellcheck.h"
, 170, __FUNCTION__), 0 : 0))
;
171 m_best_candidate = best_candidate;
172 m_best_distance = best_distance;
173 m_best_candidate_len = best_candidate_len;
174 }
175
176 /* Generate the maximum edit distance for which we consider a suggestion
177 to be meaningful, given a candidate of length CANDIDATE_LEN. */
178
179 edit_distance_t get_cutoff (size_t candidate_len) const
180 {
181 return ::get_edit_distance_cutoff (m_goal_len, candidate_len);
182 }
183
184 /* Get the best candidate so far, but applying a filter to ensure
185 that we return NULL if none of the candidates are close to the goal,
186 to avoid offering nonsensical suggestions to the user. */
187
188 candidate_t get_best_meaningful_candidate () const
189 {
190 /* If the edit distance is too high, the suggestion is likely to be
191 meaningless. */
192 if (m_best_candidate)
193 {
194 edit_distance_t cutoff = get_cutoff (m_best_candidate_len);
195 if (m_best_distance > cutoff)
196 return NULLnullptr;
197 }
198
199 /* If the goal string somehow makes it into the candidate list, offering
200 it as a suggestion will be nonsensical e.g.
201 'constexpr' does not name a type; did you mean 'constexpr'?
202 Ultimately such suggestions are due to bugs in constructing the
203 candidate list, but as a band-aid, do not offer suggestions for
204 distance == 0 (where candidate == goal). */
205 if (m_best_distance == 0)
206 return NULLnullptr;
207
208 return m_best_candidate;
209 }
210
211 /* Get the closest candidate so far, without applying any filtering. */
212
213 candidate_t blithely_get_best_candidate () const
214 {
215 return m_best_candidate;
216 }
217
218 edit_distance_t get_best_distance () const { return m_best_distance; }
219 size_t get_best_candidate_length () const { return m_best_candidate_len; }
220
221 private:
222 const char *m_goal;
223 size_t m_goal_len;
224 candidate_t m_best_candidate;
225 edit_distance_t m_best_distance;
226 size_t m_best_candidate_len;
227};
228
229#endif /* GCC_SPELLCHECK_H */