Go to file
Giuseppe D'Angelo 6cee204d56 QS(V)/QBA(V)/QL1S::lastIndexOf: fix the offset calculations
When trying to fix 0-length matches at the end of a QString,
be83ff65c4 actually introduced a
regression due to how lastIndexOf interprets its `from` parameter.

The "established" (=legacy) interpretation of a negative `from` is that
it is supposed to indicate that we want the last match at offset `from +
size()`. With the default from of -1, that means we want a match
starting at most at position `size() - 1` inclusive, i.e. *at* the last
position in the string. The aforementioned commit changed that, by
allowing a match at position `size()` instead, and this behavioral
change broke code.

The problem the commit tried to fix was that empty matches *are* allowed
to happen at position size(): the last match of regexp // inside the
string "test" is indeed at position 4 (the regexp matches 5 times).

Changing the meaning of negative from to include that last position (in
general: to include position `from+size()+1` as the last valid matching
position, in case of a negative `from`) has unfortunately broken client
code. Therefore, we need to revert it. This patch does that, adapting
the tests as necessary (drive-by: a broken #undef is removed).

Reverting the patch however is not sufficient. What we are facing here
is an historical API mistake that forces the default `from` (-1) to
*skip* the truly last possible match; the mistake is that thre is simply
no way to pass a negative `from` and obtain that match. This means that
the revert will now cause code like this:

  str.lastIndexOf(QRE("")); // `from` defaulted to -1

NOT to return str.size(), which is counter-intuitive and wrong. Other
APIs expose this inconsistency: for instance, using
QRegularExpressionIterator would actually yield a last match at position
str.size(). Similarly, using QString::count would return `str.size()+1`.
Note that, in general, it's still possible for clients to call

  str.lastIndexOf(~~~, str.size())

to get the "truly last" match.

This patch also tries to fix this case ("have our cake and eat it").

First and foremost, a couple of bugs in QByteArray and QString code are
fixed (when dealing with 0-length needles).

Second, a lastIndexOf overload is added. One overload is the "legacy"
one, that will honor the pre-existing semantics of negative `from`. The
new overload does NOT take a `from` parameter at all, and will actually
match from the truly end (by simply calling `lastIndexOf(~~~, size())`
internally).

These overloads are offered for all the existing lastIndexOf()
overloads, not only the ones taking QRE.

This means that code simply using `lastIndexOf` without any `from`
parameter get the "correct" behavior for 0-length matches, and code that
specifies one gets the legacy behavior. Matches of length > 0 are not
affected anyways, as they can't match at position size().

[ChangeLog][Important Behavior Changes] A regression in the behavior of
the lastIndexOf() function on text-related containers and views
(QString, QStringView, QByteArray, QByteArrayView, QLatin1String) has
been fixed, and the behavior made consistent and more in line with
user expectations. When lastIndexOf() is invoked with a negative `from`
position, the last match has now to start at the last character in the
container/view (before, it was at the position *past* the last
character). This makes a difference when using lastIndexOf() with a
needle that has 0 length (for instance an empty string, a regular
expression that can match 0 characters, and so on); any other case is
unaffected. To retrieve the "truly last" match, one can pass a
positive `from` offset to lastIndexOf() (basically, pass `size()` as the
`from` parameter). To make calls such as `text.lastIndexOf(~~~);`, that
do not pass any `from` parameter, behave properly, a new lastIndexOf()
overload has been added to all the text containers/views. This overload
does not take a `from` parameter at all, and will search starting from
one character past the end of the text, therefore returning a correct
result when used with needles that may yield 0-length matches. Client
code may need to be recompiled in order to use this new overload.
Conversely, client code that needs to skip the "truly last" match now
needs to pass -1 as the `from` parameter instead of relying on the
default.

Change-Id: I5e92bdcf1a57c2c3cca97b6adccf0883d00a92e5
Fixes: QTBUG-94215
Pick-to: 6.2
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
2021-08-19 01:55:01 +02:00
.github/workflows Github Actions: many minor updates 2021-01-22 18:48:54 +01:00
bin CMake: Allow usage of QtStandaloneTestTemplateProject as package component 2021-04-23 13:31:04 +02:00
cmake CMake: Include quiet packages in feature summary 2021-08-18 22:15:35 +02:00
coin Apply custom timeouts to cross compilations 2021-08-10 12:21:58 +00:00
config.tests Fix the precompile_header configure test 2021-06-19 01:08:23 +02:00
dist Add Qt 6.0.0 changes file 2020-11-16 10:02:08 +02:00
doc Add note on selecting the device which is used to run tests on Android 2021-08-18 19:53:38 +00:00
examples Raise cmake_minimum_required to VERSION 3.16 in examples 2021-08-17 19:18:54 +02:00
lib Purge all fonts 2015-08-18 19:59:14 +00:00
libexec Fix global header exclusion regex 2021-07-09 13:56:18 +02:00
mkspecs Fix location of qtattributionsscanner 2021-07-14 18:16:15 +02:00
qmake Doc: Note that qmake's CONFIG values are case-sensitive 2021-08-18 14:17:29 +00:00
src QS(V)/QBA(V)/QL1S::lastIndexOf: fix the offset calculations 2021-08-19 01:55:01 +02:00
tests QS(V)/QBA(V)/QL1S::lastIndexOf: fix the offset calculations 2021-08-19 01:55:01 +02:00
util CMake: Bump project versions 2021-08-02 12:42:29 +02:00
.cmake.conf CMake: Bump min required CMake version for static Qt builds to 3.21 2021-08-10 16:51:53 +02:00
.gitattributes Give batch files CRLF line endings 2020-11-04 15:02:29 +00:00
.gitignore testlib: Accurately name JUnit test, and only run for JUnitXML reporter 2021-07-29 15:39:40 +02:00
.lgtm.yml Skip LGTM analysis for the bootstrap library and tools 2020-07-16 01:04:34 +02:00
.qmake.conf Bump version 2021-02-18 07:20:21 +02:00
.tag Update the git-archive export options 2012-09-07 15:39:31 +02:00
CMakeLists.txt CMake: Enforce minimum CMake version in user projects 2021-08-04 16:03:08 +02:00
conanfile.py Conan: simplify Conan recipe 2021-06-21 15:19:16 +03:00
config_help.txt Remove dysfunctional -coverage configure argument 2021-08-10 12:00:13 +02:00
configure Remove unsupported, host-related options from configure shell script 2021-08-05 02:44:27 +02:00
configure.bat Remove superfluous variable assignments from configure.bat 2021-03-11 16:39:54 +01:00
configure.cmake Remove dysfunctional -coverage configure argument 2021-08-10 12:00:13 +02:00
dependencies.yaml Re-add dependencies.yaml now that qt5.git wip/qt6 builds fine 2019-09-18 13:19:31 +02:00
LICENSE.FDL Initial import from the monolithic Qt. 2011-04-27 12:05:43 +02:00
LICENSE.GPL2 Add new license header templates and license files 2016-01-14 20:43:46 +00:00
LICENSE.GPL3 Add new license header templates and license files 2016-01-14 20:43:46 +00:00
LICENSE.GPL3-EXCEPT Add new license header templates and license files 2016-01-14 20:43:46 +00:00
LICENSE.LGPL3 Add new license header templates and license files 2016-01-14 20:43:46 +00:00
LICENSE.LGPLv3 Remove LICENSE.GPLv3, LICENSE.LGPLv21, LGPL_EXCEPTION.txt 2018-04-16 11:02:14 +00:00
LICENSE.QT-LICENSE-AGREEMENT Update enterprise license agreement v4.2.1 2019-12-18 13:07:19 +02:00
qt_cmdline.cmake Remove dysfunctional -coverage configure argument 2021-08-10 12:00:13 +02:00
sync.profile Implement generating of a module cpp export header 2021-06-24 20:40:49 +02:00