Skip to content

Commit 7660a98

Browse files
Merge pull request #1627 from romainbrenguier/bugfix/string-last-index-of#TG-592
Fix case of empty string in String.lastIndexOf and String.indexOf TG-592
2 parents 9749321 + 9ebdc88 commit 7660a98

File tree

4 files changed

+78
-31
lines changed

4 files changed

+78
-31
lines changed
Binary file not shown.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
public class Test {
2+
int fromIndex;
3+
4+
public void check(String input_String1, String input_String2, int i) {
5+
if(input_String1 != null && input_String2 != null) {
6+
if (i == 0) {
7+
// The last occurrence of the empty string "" is considered to
8+
// occur at the index value this.length()
9+
int lio = input_String1.lastIndexOf(input_String2);
10+
if (input_String2.length() == 0)
11+
assert lio == input_String1.length();
12+
} else if (i == 1) {
13+
// Contradiction with the previous condition (should fail).
14+
assert "foo".lastIndexOf("") != 3;
15+
} else if (i == 2 && input_String2.length() > 0) {
16+
int lio = input_String1.lastIndexOf(input_String2.charAt(0), fromIndex);
17+
if (input_String2.length() == 0)
18+
assert lio == fromIndex;
19+
} else if (i == 3) {
20+
assert "foo".lastIndexOf("", 2) != 2;
21+
}
22+
}
23+
}
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--function Test.check --refine-strings --string-max-length 100
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
assertion at file Test.java line 11 .* SUCCESS$
7+
assertion at file Test.java line 14 .* FAILURE$
8+
assertion at file Test.java line 18 .* SUCCESS$
9+
assertion at file Test.java line 20 .* FAILURE$

src/solvers/refinement/string_constraint_generator_indexof.cpp

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of(
8080
/// `haystack` of the first occurrence of `needle` starting the search at
8181
/// `from_index`, or `-1` if needle does not occur at or after position
8282
/// `from_index`.
83+
/// If needle is an empty string then the result is `from_index`.
8384
///
8485
/// These axioms are:
8586
/// 1. \f$ contains \Rightarrow {\tt from\_index} \le \tt{index}
@@ -93,6 +94,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of(
9394
/// 5. \f$ \forall n \in [{\tt from\_index},|{\tt haystack}|-|{\tt needle}|]
9495
/// .\ \lnot contains \Rightarrow (\exists m \in [0,|{\tt needle}|)
9596
/// .\ {\tt haystack}[m+n] \ne {\tt needle}[m]) \f$
97+
/// 6. \f$ |{\tt needle}| = 0 \Rightarrow \tt{index} = from_index \f$
9698
/// \param haystack: an array of character expression
9799
/// \param needle: an array of character expression
98100
/// \param from_index: an integer expression
@@ -152,13 +154,19 @@ exprt string_constraint_generatort::add_axioms_for_index_of_string(
152154
needle);
153155
axioms.push_back(a5);
154156

157+
const implies_exprt a6(
158+
equal_exprt(needle.length(), from_integer(0, index_type)),
159+
equal_exprt(offset, from_index));
160+
axioms.push_back(a6);
161+
155162
return offset;
156163
}
157164

158165
/// Add axioms stating that the returned value is the index within haystack of
159166
/// the last occurrence of needle starting the search backward at from_index (ie
160167
/// the index is smaller or equal to from_index), or -1 if needle does not occur
161168
/// before from_index.
169+
/// If `needle` is the empty string, the result is `from_index`.
162170
///
163171
/// These axioms are:
164172
/// 1. \f$ contains \Rightarrow -1 \le {\tt index}
@@ -178,6 +186,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of_string(
178186
/// .\ \lnot contains \Rightarrow
179187
/// (\exists m \in [0,|{\tt needle}|)
180188
/// .\ {\tt haystack}[m+n] \ne {\tt needle}[m]) \f$
189+
/// 6. \f$ |{\tt needle}| = 0 \Rightarrow index = from_index \f$
181190
/// \param haystack: an array of characters expression
182191
/// \param needle: an array of characters expression
183192
/// \param from_index: integer expression
@@ -238,6 +247,11 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of_string(
238247
needle);
239248
axioms.push_back(a5);
240249

250+
const implies_exprt a6(
251+
equal_exprt(needle.length(), from_integer(0, index_type)),
252+
equal_exprt(offset, from_index));
253+
axioms.push_back(a6);
254+
241255
return offset;
242256
}
243257

@@ -295,13 +309,16 @@ exprt string_constraint_generatort::add_axioms_for_index_of(
295309
/// \todo Change argument names to match add_axioms_for_last_index_of_string
296310
///
297311
/// These axioms are :
298-
/// 1. \f$ -1 \le {\tt index} \le {\tt from\_index} \f$
312+
/// 1. \f$ -1 \le {\tt index} \le {\tt from\_index}
313+
/// \land {\tt index} < |{\tt haystack}| \f$
299314
/// 2. \f$ {\tt index} = -1 \Leftrightarrow \lnot contains\f$
300-
/// 3. \f$ contains \Rightarrow ({\tt index} \le {\tt from\_index} \land
301-
/// {\tt haystack}[i] = {\tt needle} )\f$
302-
/// 4. \f$ \forall n \in [{\tt index} +1, {\tt from\_index}+1)
315+
/// 3. \f$ contains \Rightarrow
316+
/// {\tt haystack}[{\tt index}] = {\tt needle} )\f$
317+
/// 4. \f$ \forall n \in [{\tt index} +1,
318+
/// min({\tt from\_index}+1, |{\tt haystack}|))
303319
/// .\ contains \Rightarrow {\tt haystack}[n] \ne {\tt needle} \f$
304-
/// 5. \f$ \forall m \in [0, {\tt from\_index}+1)
320+
/// 5. \f$ \forall m \in [0,
321+
/// min({\tt from\_index}+1, |{\tt haystack}|))
305322
/// .\ \lnot contains \Rightarrow {\tt haystack}[m] \ne {\tt needle}\f$
306323
/// \param str: an array of characters expression
307324
/// \param c: a character expression
@@ -314,42 +331,41 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of(
314331
const exprt &from_index)
315332
{
316333
const typet &index_type = str.length().type();
317-
symbol_exprt index=fresh_exist_index("last_index_of", index_type);
318-
symbol_exprt contains=fresh_boolean("contains_in_last_index_of");
334+
const symbol_exprt index = fresh_exist_index("last_index_of", index_type);
335+
const symbol_exprt contains = fresh_boolean("contains_in_last_index_of");
319336

320-
exprt index1=from_integer(1, index_type);
321-
exprt minus1=from_integer(-1, index_type);
322-
exprt from_index_plus_one=plus_exprt_with_overflow_check(from_index, index1);
323-
and_exprt a1(
337+
const exprt minus1 = from_integer(-1, index_type);
338+
const and_exprt a1(
324339
binary_relation_exprt(index, ID_ge, minus1),
325-
binary_relation_exprt(index, ID_lt, from_index_plus_one));
340+
binary_relation_exprt(index, ID_le, from_index),
341+
binary_relation_exprt(index, ID_lt, str.length()));
326342
axioms.push_back(a1);
327343

328-
equal_exprt a2(not_exprt(contains), equal_exprt(index, minus1));
344+
const notequal_exprt a2(contains, equal_exprt(index, minus1));
329345
axioms.push_back(a2);
330346

331-
implies_exprt a3(
332-
contains,
333-
and_exprt(
334-
binary_relation_exprt(from_index, ID_ge, index),
335-
equal_exprt(str[index], c)));
347+
const implies_exprt a3(contains, equal_exprt(str[index], c));
336348
axioms.push_back(a3);
337349

338-
symbol_exprt n=fresh_univ_index("QA_last_index_of1", index_type);
339-
string_constraintt a4(
350+
const exprt index1 = from_integer(1, index_type);
351+
const plus_exprt from_index_plus_one(from_index, index1);
352+
const if_exprt end_index(
353+
binary_relation_exprt(from_index_plus_one, ID_le, str.length()),
354+
from_index_plus_one,
355+
str.length());
356+
357+
const symbol_exprt n = fresh_univ_index("QA_last_index_of1", index_type);
358+
const string_constraintt a4(
340359
n,
341360
plus_exprt(index, index1),
342-
from_index_plus_one,
361+
end_index,
343362
contains,
344-
not_exprt(equal_exprt(str[n], c)));
363+
notequal_exprt(str[n], c));
345364
axioms.push_back(a4);
346365

347-
symbol_exprt m=fresh_univ_index("QA_last_index_of2", index_type);
348-
string_constraintt a5(
349-
m,
350-
from_index_plus_one,
351-
not_exprt(contains),
352-
not_exprt(equal_exprt(str[m], c)));
366+
const symbol_exprt m = fresh_univ_index("QA_last_index_of2", index_type);
367+
const string_constraintt a5(
368+
m, end_index, not_exprt(contains), notequal_exprt(str[m], c));
353369
axioms.push_back(a5);
354370

355371
return index;
@@ -384,9 +400,7 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of(
384400
const typet &char_type = str.content().type().subtype();
385401
PRECONDITION(f.type() == index_type);
386402

387-
const exprt from_index =
388-
args.size() == 2 ? minus_exprt(str.length(), from_integer(1, index_type))
389-
: args[2];
403+
const exprt from_index = args.size() == 2 ? str.length() : args[2];
390404

391405
if(c.type().id()==ID_unsignedbv || c.type().id()==ID_signedbv)
392406
{

0 commit comments

Comments
 (0)