QDTP: Fix handling of no-digit numeric fields

A numeric field with no digits is Intermediate, since digits can be
typed into it. The testing for this was complicated by the fact that a
sign might be either a sign in the field or the start of a following
separator.

Break out the testing of separator matching at the start of a view.
This simplifies the existing checks for full separator match and we
can use it in the no-digit numeric field's handling to make the check
more robust (matching the whole separator, rather than only its first
character). It incidentally prepares the way for other changes.

Pick-to: 6.6 6.5
Task-number: QTBUG-114909
Change-Id: I5abfccbcae3cba0979b4723c400de038fe2bf324
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2023-07-11 18:54:49 +02:00
parent fbd259856d
commit 61b2a6f587

View File

@ -727,6 +727,14 @@ int QDateTimeParser::sectionMaxSize(int index) const
return sectionMaxSize(sn.type, sn.count);
}
// Separator matching
static int matchesSeparator(QStringView text, QStringView separator)
{
// -1 if not a match, else length of prefix of text that does match.
// For now, just check for exact match:
return text.startsWith(separator) ? separator.size() : -1;
}
/*!
\internal
@ -845,12 +853,26 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
case MinuteSection:
case SecondSection:
case MSecSection: {
const auto checkSeparator = [&result, field=QStringView{m_text}.sliced(offset),
negativeYearOffset, sectionIndex, this]() {
// No-digit field if next separator is here, otherwise invalid.
const auto &sep = separators.at(sectionIndex + 1);
if (matchesSeparator(field.sliced(negativeYearOffset), sep) != -1)
result = ParsedSection(Intermediate, 0, negativeYearOffset);
else if (negativeYearOffset && matchesSeparator(field, sep) != -1)
result = ParsedSection(Intermediate, 0, 0);
else
return false;
return true;
};
int used = negativeYearOffset;
// We already sliced off the - sign if it was legitimately present.
// We already sliced off the - sign if it was acceptable.
// QLocale::toUInt() would accept a sign, so we must reject it overtly:
if (sectionTextRef.startsWith(u'-')
|| sectionTextRef.startsWith(u'+')) {
if (separators.at(sectionIndex + 1).startsWith(sectionTextRef[0]))
result = ParsedSection(Intermediate, 0, used);
// However, a sign here may indicate a field with no digits, if it
// starts the next separator:
checkSeparator();
break;
}
QStringView digitsStr = sectionTextRef.left(digitCount(sectionTextRef));
@ -884,13 +906,10 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
}
if (lastVal == -1) {
const auto &sep = separators.at(sectionIndex + 1);
if (sep.startsWith(sectionTextRef[0])
|| (negate && sep.startsWith(m_text.at(offset))))
result = ParsedSection(Intermediate, 0, 0);
else
if (!checkSeparator()) {
QDTPDEBUG << "invalid because" << sectionTextRef << "can't become a uint"
<< lastVal;
}
} else {
if (negate)
lastVal = -lastVal;
@ -1182,13 +1201,14 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const
for (int index = 0; index < sectionNodesCount; ++index) {
Q_ASSERT(state != Invalid);
const QString &separator = separators.at(index);
if (QStringView{m_text}.mid(pos, separator.size()) != separator) {
QDTPDEBUG << "invalid because" << QStringView{m_text}.mid(pos, separator.size())
<< "!=" << separator
int step = matchesSeparator(QStringView{m_text}.sliced(pos), separator);
if (step == -1) {
QDTPDEBUG << "invalid because" << QStringView{m_text}.sliced(pos)
<< "does not start with" << separator
<< index << pos << currentSectionIndex;
return StateNode();
}
pos += separator.size();
pos += step;
sectionNodes[index].pos = pos;
int *current = nullptr;
int zoneOffset; // Needed to serve as *current when setting zone
@ -1287,9 +1307,10 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const
isSet |= sn.type;
}
if (QStringView{m_text}.sliced(pos) != separators.last()) {
int step = matchesSeparator(QStringView{m_text}.sliced(pos), separators.last());
if (step == -1 || step + pos < m_text.size()) {
QDTPDEBUG << "invalid because" << QStringView{m_text}.sliced(pos)
<< "!=" << separators.last() << pos;
<< "does not match" << separators.last() << pos;
return StateNode();
}