Support floats in valid config (#1297)

* Add tests for invalid ranges

* Refactor loadLibErrors

This reduces the amount of code slightly and will simplify adding
more tests.

* Handle empty valid field

Before this change, the sequence <valid></valid> in a config file would
result in a segmentation fault. Now an empty field results in the error
message:

cppcheck: Failed to load library configuration file 'mycfg.cfg'. Bad attribute value '""'

* Add support for valid for floating point arguments

Previously, it was not possible to add valid ranges to floating point
arguments since it only handled integers. This made ranges not work well
for floating point arguments since arguments were cast to integers
before the ranges were handled.

Fix this by using doubles instead of integers if the argument is a float.
Add some tests for this and make sure errors are printed with enough
precision (somewhat arbitrarily chosen).

Note that it is still only possible to add integer ranges (i.e. -1:1).

* Add support for floats in configuration valid range

Now that it is possible to handle decimal arguments, there is no reason
to not allow non-integer ranges. Take care to not allow broken
configurations.

* Move check to within if-clause

* Move asin{,f,l} and acos{,f,l} input checks to config file
This commit is contained in:
rikardfalkeborn 2018-07-15 22:47:56 +02:00 committed by Daniel Marjamäki
parent b05ae44edb
commit 491ee577c6
9 changed files with 290 additions and 106 deletions

View File

@ -154,7 +154,7 @@
<element name="not-uninit"><empty/></element> <element name="not-uninit"><empty/></element>
<element name="valid"> <element name="valid">
<data type="string"> <data type="string">
<param name="pattern">(-?[0-9]*[,:])*([-]?[0-9]+)?</param> <param name="pattern">(-?[0-9]*(\.[0-9]+)?[,:])*([-]?[0-9]+(\.[0-9]+)?)?</param>
</data> </data>
</element> </element>
<element name="minsize"> <element name="minsize">

View File

@ -108,6 +108,7 @@
<leak-ignore/> <leak-ignore/>
<arg nr="1"> <arg nr="1">
<not-uninit/> <not-uninit/>
<valid>-1.0:1.0</valid>
</arg> </arg>
</function> </function>
<!-- float acosf(float x); --> <!-- float acosf(float x); -->
@ -119,6 +120,7 @@
<leak-ignore/> <leak-ignore/>
<arg nr="1"> <arg nr="1">
<not-uninit/> <not-uninit/>
<valid>-1.0:1.0</valid>
</arg> </arg>
</function> </function>
<!-- long double acosl(long double x); --> <!-- long double acosl(long double x); -->
@ -130,6 +132,7 @@
<leak-ignore/> <leak-ignore/>
<arg nr="1"> <arg nr="1">
<not-uninit/> <not-uninit/>
<valid>-1.0:1.0</valid>
</arg> </arg>
</function> </function>
<!-- double acosh(double x); --> <!-- double acosh(double x); -->
@ -348,6 +351,7 @@
<noreturn>false</noreturn> <noreturn>false</noreturn>
<leak-ignore/> <leak-ignore/>
<arg nr="1"> <arg nr="1">
<valid>-1.0:1.0</valid>
<not-uninit/> <not-uninit/>
</arg> </arg>
</function> </function>
@ -359,6 +363,7 @@
<noreturn>false</noreturn> <noreturn>false</noreturn>
<leak-ignore/> <leak-ignore/>
<arg nr="1"> <arg nr="1">
<valid>-1.0:1.0</valid>
<not-uninit/> <not-uninit/>
</arg> </arg>
</function> </function>
@ -370,6 +375,7 @@
<noreturn>false</noreturn> <noreturn>false</noreturn>
<leak-ignore/> <leak-ignore/>
<arg nr="1"> <arg nr="1">
<valid>-1.0:1.0</valid>
<not-uninit/> <not-uninit/>
</arg> </arg>
</function> </function>

View File

@ -32,6 +32,7 @@
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <iomanip>
#include <ostream> #include <ostream>
#include <vector> #include <vector>
@ -119,9 +120,9 @@ void CheckFunctions::invalidFunctionUsage()
invalidFunctionArgBoolError(argtok, functionToken->str(), argnr); invalidFunctionArgBoolError(argtok, functionToken->str(), argnr);
// Are the values 0 and 1 valid? // Are the values 0 and 1 valid?
else if (!mSettings->library.isargvalid(functionToken, argnr, 0)) else if (!mSettings->library.isargvalid(functionToken, argnr, static_cast<MathLib::bigint>(0)))
invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr));
else if (!mSettings->library.isargvalid(functionToken, argnr, 1)) else if (!mSettings->library.isargvalid(functionToken, argnr, static_cast<MathLib::bigint>(1)))
invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr));
} }
} }
@ -139,7 +140,7 @@ void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string
else else
errmsg << "Invalid $symbol() argument nr " << argnr << '.'; errmsg << "Invalid $symbol() argument nr " << argnr << '.';
if (invalidValue) if (invalidValue)
errmsg << " The value is " << invalidValue->intvalue << " but the valid values are '" << validstr << "'."; errmsg << " The value is " << std::setprecision(10) << (invalidValue->isIntValue() ? invalidValue->intvalue : invalidValue->floatValue) << " but the valid values are '" << validstr << "'.";
else else
errmsg << " The value is 0 or 1 (boolean) but the valid values are '" << validstr << "'."; errmsg << " The value is 0 or 1 (boolean) but the valid values are '" << validstr << "'.";
if (invalidValue) if (invalidValue)
@ -241,11 +242,6 @@ void CheckFunctions::checkMathFunctions()
} }
} }
// acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond
else if (Token::Match(tok, "acos|acosl|acosf|asin|asinf|asinl ( %num% )")) {
if (std::fabs(MathLib::toDoubleNumber(tok->strAt(2))) > 1.0)
mathfunctionCallWarning(tok);
}
// sqrt( x ): if x is negative the result is undefined // sqrt( x ): if x is negative the result is undefined
else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )")) { else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )")) {
if (MathLib::isNegative(tok->strAt(2))) if (MathLib::isNegative(tok->strAt(2)))

View File

@ -43,6 +43,18 @@ static std::vector<std::string> getnames(const char *names)
return ret; return ret;
} }
static void gettokenlistfromvalid(const std::string& valid, TokenList& tokenList)
{
std::istringstream istr(valid + ',');
tokenList.createTokens(istr);
for (Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok,"- %num%")) {
tok->str("-" + tok->strAt(1));
tok->deleteNext();
}
}
}
Library::Library() : mAllocId(0) Library::Library() : mAllocId(0)
{ {
} }
@ -578,19 +590,33 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co
const char *p = argnode->GetText(); const char *p = argnode->GetText();
bool error = false; bool error = false;
bool range = false; bool range = false;
bool has_dot = false;
if (!p)
return Error(BAD_ATTRIBUTE_VALUE, "\"\"");
error = *p == '.';
for (; *p; p++) { for (; *p; p++) {
if (std::isdigit(*p)) if (std::isdigit(*p))
error |= (*(p+1) == '-'); error |= (*(p+1) == '-');
else if (*p == ':') else if (*p == ':') {
error |= range; error |= range | (*(p+1) == '.');
range = true;
has_dot = false;
}
else if (*p == '-') else if (*p == '-')
error |= (!std::isdigit(*(p+1))); error |= (!std::isdigit(*(p+1)));
else if (*p == ',') else if (*p == ',') {
range = false; range = false;
error |= *(p+1) == '.';
has_dot = false;
}
else if (*p == '.') {
error |= has_dot | (!std::isdigit(*(p+1)));
has_dot = true;
}
else else
error = true; error = true;
range |= (*p == ':');
} }
if (error) if (error)
return Error(BAD_ATTRIBUTE_VALUE, argnode->GetText()); return Error(BAD_ATTRIBUTE_VALUE, argnode->GetText());
@ -705,15 +731,10 @@ bool Library::isargvalid(const Token *ftok, int argnr, const MathLib::bigint arg
const ArgumentChecks *ac = getarg(ftok, argnr); const ArgumentChecks *ac = getarg(ftok, argnr);
if (!ac || ac->valid.empty()) if (!ac || ac->valid.empty())
return true; return true;
else if (ac->valid.find('.') != std::string::npos)
return isargvalid(ftok, argnr, static_cast<double>(argvalue));
TokenList tokenList(nullptr); TokenList tokenList(nullptr);
std::istringstream istr(ac->valid + ','); gettokenlistfromvalid(ac->valid, tokenList);
tokenList.createTokens(istr);
for (Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok,"- %num%")) {
tok->str("-" + tok->strAt(1));
tok->deleteNext();
}
}
for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { for (const Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (tok->isNumber() && argvalue == MathLib::toLongNumber(tok->str())) if (tok->isNumber() && argvalue == MathLib::toLongNumber(tok->str()))
return true; return true;
@ -727,6 +748,24 @@ bool Library::isargvalid(const Token *ftok, int argnr, const MathLib::bigint arg
return false; return false;
} }
bool Library::isargvalid(const Token *ftok, int argnr, double argvalue) const
{
const ArgumentChecks *ac = getarg(ftok, argnr);
if (!ac || ac->valid.empty())
return true;
TokenList tokenList(nullptr);
gettokenlistfromvalid(ac->valid, tokenList);
for (const Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toDoubleNumber(tok->str()) && argvalue <= MathLib::toDoubleNumber(tok->strAt(2)))
return true;
if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toDoubleNumber(tok->str()))
return true;
if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toDoubleNumber(tok->strAt(1)))
return true;
}
return false;
}
std::string Library::getFunctionName(const Token *ftok, bool *error) const std::string Library::getFunctionName(const Token *ftok, bool *error) const
{ {
if (!ftok) { if (!ftok) {

View File

@ -301,6 +301,7 @@ public:
} }
bool isargvalid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const; bool isargvalid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const;
bool isargvalid(const Token *ftok, int argnr, double argvalue) const;
const std::string& validarg(const Token *ftok, int argnr) const { const std::string& validarg(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr); const ArgumentChecks *arg = getarg(ftok, argnr);

View File

@ -1497,7 +1497,8 @@ const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, unsigned int
const ValueFlow::Value *ret = nullptr; const ValueFlow::Value *ret = nullptr;
std::list<ValueFlow::Value>::const_iterator it; std::list<ValueFlow::Value>::const_iterator it;
for (it = mValues->begin(); it != mValues->end(); ++it) { for (it = mValues->begin(); it != mValues->end(); ++it) {
if (it->isIntValue() && !settings->library.isargvalid(ftok, argnr, it->intvalue)) { if ((it->isIntValue() && !settings->library.isargvalid(ftok, argnr, it->intvalue)) ||
(it->isFloatValue() && !settings->library.isargvalid(ftok, argnr, it->floatValue))) {
if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive())) if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive()))
ret = &(*it); ret = &(*it);
if (!ret->isInconclusive() && !ret->condition) if (!ret->isInconclusive() && !ret->condition)

View File

@ -1498,7 +1498,8 @@ Checking range.c...
-10:20 =&gt; all values between -10 and 20 are valid -10:20 =&gt; all values between -10 and 20 are valid
:0 =&gt; all values that are less or equal to 0 are valid :0 =&gt; all values that are less or equal to 0 are valid
0: =&gt; all values that are greater or equal to 0 are valid 0: =&gt; all values that are greater or equal to 0 are valid
0,2:32 =&gt; the value 0 and all values between 2 and 32 are valid </programlisting> 0,2:32 =&gt; the value 0 and all values between 2 and 32 are valid
-1.5:5.6 =&gt; all values between -1.5 and 5.6 are valid </programlisting>
</section> </section>
<section> <section>

View File

@ -604,9 +604,9 @@ private:
" std::cout << acosf(1.1) << std::endl;\n" " std::cout << acosf(1.1) << std::endl;\n"
" std::cout << acosl(1.1) << std::endl;\n" " std::cout << acosl(1.1) << std::endl;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 1.1 to acos() leads to implementation-defined result.\n" ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:4]: (warning) Passing value 1.1 to acosf() leads to implementation-defined result.\n" "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:5]: (warning) Passing value 1.1 to acosl() leads to implementation-defined result.\n", errout.str()); "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout.str());
check("void foo()\n" check("void foo()\n"
"{\n" "{\n"
@ -614,9 +614,9 @@ private:
" std::cout << acosf(-1.1) << std::endl;\n" " std::cout << acosf(-1.1) << std::endl;\n"
" std::cout << acosl(-1.1) << std::endl;\n" " std::cout << acosl(-1.1) << std::endl;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.1 to acos() leads to implementation-defined result.\n" ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:4]: (warning) Passing value -1.1 to acosf() leads to implementation-defined result.\n" "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:5]: (warning) Passing value -1.1 to acosl() leads to implementation-defined result.\n", errout.str()); "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout.str());
} }
void mathfunctionCall_asin() { void mathfunctionCall_asin() {
@ -665,9 +665,9 @@ private:
" std::cout << asinf(1.1) << std::endl;\n" " std::cout << asinf(1.1) << std::endl;\n"
" std::cout << asinl(1.1) << std::endl;\n" " std::cout << asinl(1.1) << std::endl;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 1.1 to asin() leads to implementation-defined result.\n" ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:4]: (warning) Passing value 1.1 to asinf() leads to implementation-defined result.\n" "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:5]: (warning) Passing value 1.1 to asinl() leads to implementation-defined result.\n", errout.str()); "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout.str());
check("void foo()\n" check("void foo()\n"
"{\n" "{\n"
@ -675,9 +675,9 @@ private:
" std::cout << asinf(-1.1) << std::endl;\n" " std::cout << asinf(-1.1) << std::endl;\n"
" std::cout << asinl(-1.1) << std::endl;\n" " std::cout << asinl(-1.1) << std::endl;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.1 to asin() leads to implementation-defined result.\n" ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:4]: (warning) Passing value -1.1 to asinf() leads to implementation-defined result.\n" "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n"
"[test.cpp:5]: (warning) Passing value -1.1 to asinl() leads to implementation-defined result.\n", errout.str()); "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout.str());
} }
void mathfunctionCall_pow() { void mathfunctionCall_pow() {

View File

@ -282,6 +282,11 @@ private:
" <arg nr=\"3\"><valid>1:5,8</valid></arg>\n" " <arg nr=\"3\"><valid>1:5,8</valid></arg>\n"
" <arg nr=\"4\"><valid>-1,5</valid></arg>\n" " <arg nr=\"4\"><valid>-1,5</valid></arg>\n"
" <arg nr=\"5\"><valid>:1,5</valid></arg>\n" " <arg nr=\"5\"><valid>:1,5</valid></arg>\n"
" <arg nr=\"6\"><valid>1.5:</valid></arg>\n"
" <arg nr=\"7\"><valid>-6.7:-5.5,-3.3:-2.7</valid></arg>\n"
" <arg nr=\"8\"><valid>0.0:</valid></arg>\n"
" <arg nr=\"9\"><valid>:2.0</valid></arg>\n"
" <arg nr=\"10\"><valid>0.0</valid></arg>\n"
" </function>\n" " </function>\n"
"</def>"; "</def>";
@ -289,41 +294,125 @@ private:
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
TokenList tokenList(nullptr); TokenList tokenList(nullptr);
std::istringstream istr("foo(a,b,c,d,e);"); std::istringstream istr("foo(a,b,c,d,e,f,g,h,i,j);");
tokenList.createTokens(istr); tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front()); tokenList.front()->next()->astOperand1(tokenList.front());
// 1- // 1-
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, -10)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, static_cast<MathLib::bigint>(-10)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, 0)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, -10.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 1)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, static_cast<MathLib::bigint>(0)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 10)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, 0.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, static_cast<MathLib::bigint>(1)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 1.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, static_cast<MathLib::bigint>(10)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 10.0));
// -7-0 // -7-0
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, -10)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, static_cast<MathLib::bigint>(-10)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -7)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, -10.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -3)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, -7.5));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, 0)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, -7.1));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, 1)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, static_cast<MathLib::bigint>(-7)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -7.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, static_cast<MathLib::bigint>(-3)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -3.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -3.5));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, static_cast<MathLib::bigint>(0)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, 0.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, 0.5));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, static_cast<MathLib::bigint>(1)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, 1.0));
// 1-5,8 // 1-5,8
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 0)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(0)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 1)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 0.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 3)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(1)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 5)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 1.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 6)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(3)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 7)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 3.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 8)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(5)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 9)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 5.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(6)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 6.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(7)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 7.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(8)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 8.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, static_cast<MathLib::bigint>(9)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 9.0));
// -1,5 // -1,5
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, -10)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, static_cast<MathLib::bigint>(-10)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 4, -1)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, -10.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 4, static_cast<MathLib::bigint>(-1)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, -1.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, 5.000001));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, 5.5));
// :1,5 // :1,5
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, -10)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, static_cast<MathLib::bigint>(-10)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, 1)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, -10.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 5, 2)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, static_cast<MathLib::bigint>(1)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, 1.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 5, static_cast<MathLib::bigint>(2)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 5, 2.0));
// 1.5:
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 6, static_cast<MathLib::bigint>(0)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 6, 0.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 6, static_cast<MathLib::bigint>(1)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 6, 1.499999));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 6, 1.5));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 6, static_cast<MathLib::bigint>(2)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 6, static_cast<MathLib::bigint>(10)));
// -6.7:-5.5,-3.3:-2.7
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, static_cast<MathLib::bigint>(-7)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, -7.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, -6.7000001));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, -6.7));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, static_cast<MathLib::bigint>(-6)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, -6.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, -5.5));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, -5.4999999));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, -3.3000001));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, -3.3));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, static_cast<MathLib::bigint>(-3)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, -3.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 7, -2.7));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, -2.6999999));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, static_cast<MathLib::bigint>(-2)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, -2.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, static_cast<MathLib::bigint>(0)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, 0.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, static_cast<MathLib::bigint>(3)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, 3.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, static_cast<MathLib::bigint>(6)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 7, 6.0));
// 0.0:
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 8, static_cast<MathLib::bigint>(-1)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 8, -1.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 8, -0.00000001));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 8, static_cast<MathLib::bigint>(0)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 8, 0.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 8, 0.000000001));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 8, static_cast<MathLib::bigint>(1)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 8, 1.0));
// :2.0
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 9, static_cast<MathLib::bigint>(-1)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 9, -1.0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 9, static_cast<MathLib::bigint>(2)));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 9, 2.0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 9, 2.00000001));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 9, static_cast<MathLib::bigint>(200)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 9, 200.0));
// 0.0
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 10, static_cast<MathLib::bigint>(0)));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 10, 0.0));
} }
void function_arg_minsize() const { void function_arg_minsize() const {
@ -724,54 +813,105 @@ private:
} }
} }
void loadLibError(const char xmldata [], Library::ErrorCode errorcode, const char* file, unsigned line) const {
Library library;
assertEquals(file, line, errorcode, readLibrary(library, xmldata).errorcode);
}
#define LOADLIBERROR(xmldata, errorcode) loadLibError(xmldata, errorcode, __FILE__, __LINE__)
#define LOADLIB_ERROR_INVALID_RANGE(valid) LOADLIBERROR("<?xml version=\"1.0\"?>\n" \
"<def>\n" \
"<function name=\"f\">\n" \
"<arg nr=\"1\">\n" \
"<valid>" valid "</valid>\n" \
"</arg>\n" \
"</function>\n" \
"</def>", \
Library::BAD_ATTRIBUTE_VALUE)
void loadLibErrors() const { void loadLibErrors() const {
// UNKNOWN_ELEMENT
{ LOADLIBERROR("<?xml version=\"1.0\"?>\n"
const char xmldata [] = "<?xml version=\"1.0\"?>\n" "<def>\n"
"<def>\n" " <X name=\"uint8_t,std::uint8_t\" size=\"1\"/>\n"
" <X name=\"uint8_t,std::uint8_t\" size=\"1\"/>\n" "</def>",
"</def>"; Library::UNKNOWN_ELEMENT);
Library library;
ASSERT_EQUALS(Library::UNKNOWN_ELEMENT, readLibrary(library, xmldata).errorcode); // #define without attributes
} LOADLIBERROR("<?xml version=\"1.0\"?>\n"
// MISSING_ATTRIBUTE "<def>\n"
{ " <define />\n" // no attributes provided at all
// #define without attributes "</def>",
{ Library::MISSING_ATTRIBUTE);
const char xmldata [] = "<?xml version=\"1.0\"?>\n"
"<def>\n" // #define with name but without value
" <define />\n" // no attributes provided at all LOADLIBERROR("<?xml version=\"1.0\"?>\n"
"</def>"; "<def>\n"
Library library; " <define name=\"foo\" />\n" // no value provided
ASSERT_EQUALS(Library::MISSING_ATTRIBUTE, readLibrary(library, xmldata).errorcode); "</def>",
} Library::MISSING_ATTRIBUTE);
// #define with name but without value
{ LOADLIBERROR("<?xml version=\"1.0\"?>\n"
const char xmldata [] = "<?xml version=\"1.0\"?>\n" "<def>\n"
"<def>\n" " <define value=\"1\" />\n" // no name provided
" <define name=\"foo\" />\n" // no value provided "</def>",
"</def>"; Library::MISSING_ATTRIBUTE);
Library library;
ASSERT_EQUALS(Library::MISSING_ATTRIBUTE, readLibrary(library, xmldata).errorcode); LOADLIBERROR("<?xml version=\"1.0\"?>\n"
} "<X>\n"
// #define with value but without a name "</X>",
{ Library::UNSUPPORTED_FORMAT);
const char xmldata [] = "<?xml version=\"1.0\"?>\n"
"<def>\n" // empty range
" <define value=\"1\" />\n" // no name provided LOADLIB_ERROR_INVALID_RANGE("");
"</def>";
Library library; // letter as range
ASSERT_EQUALS(Library::MISSING_ATTRIBUTE, readLibrary(library, xmldata).errorcode); LOADLIB_ERROR_INVALID_RANGE("a");
}
} // letter and number as range
// UNSUPPORTED_FORMAT LOADLIB_ERROR_INVALID_RANGE("1a");
{
const char xmldata [] = "<?xml version=\"1.0\"?>\n" // digit followed by dash
"<X>\n" LOADLIB_ERROR_INVALID_RANGE("0:2-1");
"</X>";
Library library; // single dash
ASSERT_EQUALS(Library::UNSUPPORTED_FORMAT, readLibrary(library, xmldata).errorcode); LOADLIB_ERROR_INVALID_RANGE("-");
}
// range with multiple colons
LOADLIB_ERROR_INVALID_RANGE("1:2:3");
// extra dot
LOADLIB_ERROR_INVALID_RANGE("1.0.0:10");
// consecutive dots
LOADLIB_ERROR_INVALID_RANGE("1..0:10");
// dot followed by dash
LOADLIB_ERROR_INVALID_RANGE("1.-0:10");
// dot without preceding number
LOADLIB_ERROR_INVALID_RANGE(".5:10");
// dash followed by dot
LOADLIB_ERROR_INVALID_RANGE("-.5:10");
// colon followed by dot without preceding number
LOADLIB_ERROR_INVALID_RANGE("0:.5");
// colon followed by dash followed by dot
LOADLIB_ERROR_INVALID_RANGE("-10:-.5");
// dot not followed by number
LOADLIB_ERROR_INVALID_RANGE("1:5.");
// dot not followed by number
LOADLIB_ERROR_INVALID_RANGE("1.:5");
// dot followed by comma
LOADLIB_ERROR_INVALID_RANGE("1:5.,6:10");
// comma followed by dot
LOADLIB_ERROR_INVALID_RANGE("-10:0,.5:");
} }
}; };