cppcheck/test/testlibrary.cpp
Rikard Falkeborn 839fcddd8a Fix #6115 (Add support to realloc to cfg files) (#1953)
* Allow to configure realloc like functions

* memleakonrealloc: Bring back tests.

The old memleak checker was removed, and the tests for it was removed in
commit 9765a2dfab4f59102507fe759ef2184b5ed41c9f. This also removed the
tests for memleakOnRealloc. Bring back those tests, somewhat modified
since the checker no longer checks for memory leaks.

* Add realloc to mem leak check

* Add tests of realloc buffer size

* Configure realloc functions

* Add test of freopen

* Allow to configure which element is realloc argument

* Fix wrong close in test

cppcheck now warns for this

* Update manual

* Update docs

* Rename alloc/dalloc/realloc functions

Naming the member function realloc caused problems on appveyor. Rename
the alloc and dealloc functions as well for consistency.

* Change comparisson order

* Remove variable and use function call directly

* Create temporary variable to simplify

* Throw mismatchError on mismatching allocation/reallocation

* Refactor to separate function

* Fix potential nullptr dereference

As pointed out by cppcheck.
2019-07-05 12:44:52 +02:00

956 lines
44 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2019 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "errorlogger.h"
#include "library.h"
#include "settings.h"
#include "standards.h"
#include "testsuite.h"
#include "token.h"
#include "tokenize.h"
#include "tokenlist.h"
#include <tinyxml2.h>
#include <map>
#include <set>
#include <string>
#include <vector>
class TestLibrary : public TestFixture {
public:
TestLibrary() : TestFixture("TestLibrary") { }
private:
Settings settings;
void run() OVERRIDE {
TEST_CASE(empty);
TEST_CASE(function);
TEST_CASE(function_match_scope);
TEST_CASE(function_match_args);
TEST_CASE(function_match_args_default);
TEST_CASE(function_match_var);
TEST_CASE(function_arg);
TEST_CASE(function_arg_any);
TEST_CASE(function_arg_variadic);
TEST_CASE(function_arg_direction);
TEST_CASE(function_arg_valid);
TEST_CASE(function_arg_minsize);
TEST_CASE(function_namespace);
TEST_CASE(function_method);
TEST_CASE(function_baseClassMethod); // calling method in base class
TEST_CASE(function_warn);
TEST_CASE(memory);
TEST_CASE(memory2); // define extra "free" allocation functions
TEST_CASE(memory3);
TEST_CASE(resource);
TEST_CASE(podtype);
TEST_CASE(container);
TEST_CASE(version);
TEST_CASE(loadLibErrors);
}
static Library::Error readLibrary(Library& library, const char* xmldata) {
tinyxml2::XMLDocument doc;
doc.Parse(xmldata);
return library.load(doc);
}
void empty() const {
// Reading an empty library file is considered to be OK
const char xmldata[] = "<?xml version=\"1.0\"?>\n<def/>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT(library.functions.empty());
}
void function() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <noreturn>false</noreturn>\n"
" </function>\n"
"</def>";
TokenList tokenList(nullptr);
std::istringstream istr("foo();");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT_EQUALS(library.functions.size(), 1U);
ASSERT(library.functions.at("foo").argumentChecks.empty());
ASSERT(library.isnotnoreturn(tokenList.front()));
}
void function_match_scope() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"/>"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
{
TokenList tokenList(nullptr);
std::istringstream istr("fred.foo(123);"); // <- wrong scope, not library function
tokenList.createTokens(istr);
ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2)));
}
{
TokenList tokenList(nullptr);
std::istringstream istr("Fred::foo(123);"); // <- wrong scope, not library function
tokenList.createTokens(istr);
ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2)));
}
}
void function_match_args() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"/>"
" </function>\n"
"</def>";
TokenList tokenList(nullptr);
std::istringstream istr("foo();"); // <- too few arguments, not library function
tokenList.createTokens(istr);
Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous());
tokenList.createAst();
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT(library.isNotLibraryFunction(tokenList.front()));
}
void function_match_args_default() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"/>"
" <arg nr=\"2\" default=\"0\"/>"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
{
TokenList tokenList(nullptr);
std::istringstream istr("foo();"); // <- too few arguments, not library function
tokenList.createTokens(istr);
Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous());
tokenList.createAst();
ASSERT(library.isNotLibraryFunction(tokenList.front()));
}
{
TokenList tokenList(nullptr);
std::istringstream istr("foo(a);"); // <- library function
tokenList.createTokens(istr);
Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous());
tokenList.createAst();
ASSERT(!library.isNotLibraryFunction(tokenList.front()));
}
{
TokenList tokenList(nullptr);
std::istringstream istr("foo(a, b);"); // <- library function
tokenList.createTokens(istr);
Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous());
tokenList.createAst();
ASSERT(!library.isNotLibraryFunction(tokenList.front()));
}
{
TokenList tokenList(nullptr);
std::istringstream istr("foo(a, b, c);"); // <- too much arguments, not library function
tokenList.createTokens(istr);
Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous());
tokenList.createAst();
ASSERT(library.isNotLibraryFunction(tokenList.front()));
}
}
void function_match_var() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"/>"
" </function>\n"
"</def>";
TokenList tokenList(nullptr);
std::istringstream istr("Fred foo(123);"); // <- Variable declaration, not library function
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
tokenList.front()->next()->varId(1);
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT(library.isNotLibraryFunction(tokenList.front()->next()));
}
void function_arg() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"><not-uninit/></arg>\n"
" <arg nr=\"2\"><not-null/></arg>\n"
" <arg nr=\"3\"><formatstr/></arg>\n"
" <arg nr=\"4\"><strz/></arg>\n"
" <arg nr=\"5\" default=\"0\"><not-bool/></arg>\n"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[1].notuninit);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[2].notnull);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[3].formatstr);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[4].strz);
ASSERT_EQUALS(false, library.functions["foo"].argumentChecks[4].optional);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].notbool);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].optional);
}
void function_arg_any() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
"<function name=\"foo\">\n"
" <arg nr=\"any\"><not-uninit/></arg>\n"
"</function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[-1].notuninit);
}
void function_arg_variadic() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
"<function name=\"foo\">\n"
" <arg nr=\"1\"></arg>\n"
" <arg nr=\"variadic\"><not-uninit/></arg>\n"
"</function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[-1].notuninit);
TokenList tokenList(nullptr);
std::istringstream istr("foo(a,b,c,d,e);");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
ASSERT_EQUALS(false, library.isuninitargbad(tokenList.front(), 1));
ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 2));
ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 3));
ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 4));
}
void function_arg_direction() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
"<function name=\"foo\">\n"
" <arg nr=\"1\" direction=\"in\"></arg>\n"
" <arg nr=\"2\" direction=\"out\"></arg>\n"
" <arg nr=\"3\" direction=\"inout\"></arg>\n"
" <arg nr=\"4\"></arg>\n"
"</function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
TokenList tokenList(nullptr);
std::istringstream istr("foo(a,b,c,d);");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
ASSERT_EQUALS(Library::ArgumentChecks::Direction::DIR_IN, library.getArgDirection(tokenList.front(), 1));
ASSERT_EQUALS(Library::ArgumentChecks::Direction::DIR_OUT, library.getArgDirection(tokenList.front(), 2));
ASSERT_EQUALS(Library::ArgumentChecks::Direction::DIR_INOUT, library.getArgDirection(tokenList.front(), 3));
ASSERT_EQUALS(Library::ArgumentChecks::Direction::DIR_UNKNOWN, library.getArgDirection(tokenList.front(), 4));
}
void function_arg_valid() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"><valid>1:</valid></arg>\n"
" <arg nr=\"2\"><valid>-7:0</valid></arg>\n"
" <arg nr=\"3\"><valid>1:5,8</valid></arg>\n"
" <arg nr=\"4\"><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"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
TokenList tokenList(nullptr);
std::istringstream istr("foo(a,b,c,d,e,f,g,h,i,j);");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
// 1-
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, -10));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, -10.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, 0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, 0.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 1));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 1.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 10));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 10.0));
// -7-0
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, -10));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -10.0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.5));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.1));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -7));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -7.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -3));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.0));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.5));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, 0));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, 0.0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 0.5));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, 1));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 1.0));
// 1-5,8
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 0.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 1));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 1.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 3));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 3.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 5));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 5.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 6));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 6.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 7));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 7.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 8));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 8.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 9));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 9.0));
// -1,5
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 4, -10));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -10.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 4, -1));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -1.0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.000001));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.5));
// :1,5
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, -10));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, -10.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, 1));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, 1.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 5, 2));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 5, 2.0));
// 1.5:
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 0.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 1));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 1.499999));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 6, 1.5));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 2));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 10));
// -6.7:-5.5,-3.3:-2.7
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -7));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -7.0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -6.7000001));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.7));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -6));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.0));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -5.5));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -5.4999999));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -3.3000001));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.3));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -3));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.0));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -2.7));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.6999999));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -2));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 0.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 3));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 3.0));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 6));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 6.0));
// 0.0:
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 8, -1));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -1.0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -0.00000001));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 0));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.0));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.000000001));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 1));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 1.0));
// :2.0
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, -1));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, -1.0));
ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, 2));
ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, 2.0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 2.00000001));
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 9, 200));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 200.0));
// 0.0
ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 10, 0));
ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 10, 0.0));
}
void function_arg_minsize() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"><minsize type=\"strlen\" arg=\"2\"/></arg>\n"
" <arg nr=\"2\"><minsize type=\"argvalue\" arg=\"3\"/></arg>\n"
" <arg nr=\"3\"/>\n"
" <arg nr=\"4\"><minsize type=\"value\" value=\"500\"/></arg>\n"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
TokenList tokenList(nullptr);
std::istringstream istr("foo(a,b,c,d);");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
// arg1: type=strlen arg2
const std::vector<Library::ArgumentChecks::MinSize> *minsizes = library.argminsizes(tokenList.front(),1);
ASSERT_EQUALS(true, minsizes != nullptr);
ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U);
if (minsizes && minsizes->size() == 1U) {
const Library::ArgumentChecks::MinSize &m = minsizes->front();
ASSERT_EQUALS(Library::ArgumentChecks::MinSize::STRLEN, m.type);
ASSERT_EQUALS(2, m.arg);
}
// arg2: type=argvalue arg3
minsizes = library.argminsizes(tokenList.front(), 2);
ASSERT_EQUALS(true, minsizes != nullptr);
ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U);
if (minsizes && minsizes->size() == 1U) {
const Library::ArgumentChecks::MinSize &m = minsizes->front();
ASSERT_EQUALS(Library::ArgumentChecks::MinSize::ARGVALUE, m.type);
ASSERT_EQUALS(3, m.arg);
}
// arg4: type=value
minsizes = library.argminsizes(tokenList.front(), 4);
ASSERT_EQUALS(true, minsizes != nullptr);
ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U);
if (minsizes && minsizes->size() == 1U) {
const Library::ArgumentChecks::MinSize &m = minsizes->front();
ASSERT_EQUALS(Library::ArgumentChecks::MinSize::VALUE, m.type);
ASSERT_EQUALS(500, m.value);
}
}
void function_namespace() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"Foo::foo,bar\">\n"
" <noreturn>false</noreturn>\n"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT_EQUALS(library.functions.size(), 2U);
ASSERT(library.functions.at("Foo::foo").argumentChecks.empty());
ASSERT(library.functions.at("bar").argumentChecks.empty());
{
TokenList tokenList(nullptr);
std::istringstream istr("Foo::foo();");
tokenList.createTokens(istr);
ASSERT(library.isnotnoreturn(tokenList.front()->tokAt(2)));
}
{
TokenList tokenList(nullptr);
std::istringstream istr("bar();");
tokenList.createTokens(istr);
ASSERT(library.isnotnoreturn(tokenList.front()));
}
}
void function_method() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"CString::Format\">\n"
" <noreturn>false</noreturn>\n"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT_EQUALS(library.functions.size(), 1U);
{
Tokenizer tokenizer(&settings, nullptr);
std::istringstream istr("CString str; str.Format();");
tokenizer.tokenize(istr, "test.cpp");
ASSERT(library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format")));
}
{
Tokenizer tokenizer(&settings, nullptr);
std::istringstream istr("HardDrive hd; hd.Format();");
tokenizer.tokenize(istr, "test.cpp");
ASSERT(!library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format")));
}
}
void function_baseClassMethod() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"Base::f\">\n"
" <arg nr=\"1\"><not-null/></arg>\n"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
{
Tokenizer tokenizer(&settings, nullptr);
std::istringstream istr("struct X : public Base { void dostuff() { f(0); } };");
tokenizer.tokenize(istr, "test.cpp");
ASSERT(library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1));
}
{
Tokenizer tokenizer(&settings, nullptr);
std::istringstream istr("struct X : public Base { void dostuff() { f(1,2); } };");
tokenizer.tokenize(istr, "test.cpp");
ASSERT(!library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1));
}
}
void function_warn() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"a\">\n"
" <warn severity=\"style\" cstd=\"c99\">Message</warn>\n"
" </function>\n"
" <function name=\"b\">\n"
" <warn severity=\"performance\" cppstd=\"c++11\" reason=\"Obsolescent\" alternatives=\"c,d,e\"/>\n"
" </function>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
TokenList tokenList(nullptr);
std::istringstream istr("a(); b();");
tokenList.createTokens(istr);
const Library::WarnInfo* a = library.getWarnInfo(tokenList.front());
const Library::WarnInfo* b = library.getWarnInfo(tokenList.front()->tokAt(4));
ASSERT_EQUALS(2, library.functionwarn.size());
ASSERT(a && b);
if (a && b) {
ASSERT_EQUALS("Message", a->message);
ASSERT_EQUALS(Severity::style, a->severity);
ASSERT_EQUALS(Standards::C99, a->standards.c);
ASSERT_EQUALS(Standards::CPP03, a->standards.cpp);
ASSERT_EQUALS("Obsolescent function 'b' called. It is recommended to use 'c', 'd' or 'e' instead.", b->message);
ASSERT_EQUALS(Severity::performance, b->severity);
ASSERT_EQUALS(Standards::C89, b->standards.c);
ASSERT_EQUALS(Standards::CPP11, b->standards.cpp);
}
}
void memory() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <memory>\n"
" <alloc>CreateX</alloc>\n"
" <dealloc>DeleteX</dealloc>\n"
" </memory>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT(library.functions.empty());
ASSERT(Library::ismemory(library.getAllocFuncInfo("CreateX")));
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX");
ASSERT(af && af->arg == -1);
const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX");
ASSERT(df && df->arg == 1);
}
void memory2() const {
const char xmldata1[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <memory>\n"
" <alloc>malloc</alloc>\n"
" <dealloc>free</dealloc>\n"
" </memory>\n"
"</def>";
const char xmldata2[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <memory>\n"
" <alloc>foo</alloc>\n"
" <dealloc>free</dealloc>\n"
" </memory>\n"
"</def>";
Library library;
library.loadxmldata(xmldata1, sizeof(xmldata1));
library.loadxmldata(xmldata2, sizeof(xmldata2));
ASSERT_EQUALS(library.deallocId("free"), library.allocId("malloc"));
ASSERT_EQUALS(library.deallocId("free"), library.allocId("foo"));
}
void memory3() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <memory>\n"
" <alloc arg=\"5\" init=\"false\">CreateX</alloc>\n"
" <dealloc arg=\"2\">DeleteX</dealloc>\n"
" </memory>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT(library.functions.empty());
const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX");
ASSERT(af && af->arg == 5);
const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX");
ASSERT(df && df->arg == 2);
ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend());
}
void resource() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <resource>\n"
" <alloc>CreateX</alloc>\n"
" <dealloc>DeleteX</dealloc>\n"
" </resource>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
ASSERT(library.functions.empty());
ASSERT(Library::isresource(library.allocId("CreateX")));
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
}
void podtype() const {
{
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <podtype name=\"s8\" sign=\"s\" size=\"1\"/>\n"
" <podtype name=\"u8\" sign=\"u\" size=\"1\"/>\n"
" <podtype name=\"u16\" sign=\"u\" size=\"2\"/>\n"
" <podtype name=\"s16\" sign=\"s\" size=\"2\"/>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
// s8
{
const struct Library::PodType * const type = library.podtype("s8");
ASSERT_EQUALS(true, type != nullptr);
if (type) {
ASSERT_EQUALS(1U, type->size);
ASSERT_EQUALS('s', type->sign);
}
}
// u8
{
const struct Library::PodType * const type = library.podtype("u8");
ASSERT_EQUALS(true, type != nullptr);
if (type) {
ASSERT_EQUALS(1U, type->size);
ASSERT_EQUALS('u', type->sign);
}
}
// u16
{
const struct Library::PodType * const type = library.podtype("u16");
ASSERT_EQUALS(true, type != nullptr);
if (type) {
ASSERT_EQUALS(2U, type->size);
ASSERT_EQUALS('u', type->sign);
}
}
// s16
{
const struct Library::PodType * const type = library.podtype("s16");
ASSERT_EQUALS(true, type != nullptr);
if (type) {
ASSERT_EQUALS(2U, type->size);
ASSERT_EQUALS('s', type->sign);
}
}
// robustness test: provide cfg without PodType
{
const struct Library::PodType * const type = library.podtype("nonExistingPodType");
ASSERT_EQUALS(true, type == nullptr);
}
}
}
void container() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <container id=\"A\" startPattern=\"std :: A &lt;\" endPattern=\"&gt; !!::\" itEndPattern=\"&gt; :: iterator\">\n"
" <type templateParameter=\"1\"/>\n"
" <size templateParameter=\"4\">\n"
" <function name=\"resize\" action=\"resize\"/>\n"
" <function name=\"clear\" action=\"clear\"/>\n"
" <function name=\"size\" yields=\"size\"/>\n"
" <function name=\"empty\" yields=\"empty\"/>\n"
" <function name=\"push_back\" action=\"push\"/>\n"
" <function name=\"pop_back\" action=\"pop\"/>\n"
" </size>\n"
" <access>\n"
" <function name=\"at\" yields=\"at_index\"/>\n"
" <function name=\"begin\" yields=\"start-iterator\"/>\n"
" <function name=\"end\" yields=\"end-iterator\"/>\n"
" <function name=\"data\" yields=\"buffer\"/>\n"
" <function name=\"c_str\" yields=\"buffer-nt\"/>\n"
" <function name=\"front\" yields=\"item\"/>\n"
" <function name=\"find\" action=\"find\"/>\n"
" </access>\n"
" </container>\n"
" <container id=\"B\" startPattern=\"std :: B &lt;\" inherits=\"A\" opLessAllowed=\"false\">\n"
" <size templateParameter=\"3\"/>\n" // Inherits all but templateParameter
" </container>\n"
" <container id=\"C\">\n"
" <type string=\"std-like\"/>\n"
" <access indexOperator=\"array-like\"/>\n"
" </container>\n"
"</def>";
Library library;
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
Library::Container& A = library.containers["A"];
Library::Container& B = library.containers["B"];
Library::Container& C = library.containers["C"];
ASSERT_EQUALS(A.type_templateArgNo, 1);
ASSERT_EQUALS(A.size_templateArgNo, 4);
ASSERT_EQUALS(A.startPattern, "std :: A <");
ASSERT_EQUALS(A.endPattern, "> !!::");
ASSERT_EQUALS(A.itEndPattern, "> :: iterator");
ASSERT_EQUALS(A.stdStringLike, false);
ASSERT_EQUALS(A.arrayLike_indexOp, false);
ASSERT_EQUALS(A.opLessAllowed, true);
ASSERT_EQUALS(Library::Container::SIZE, A.getYield("size"));
ASSERT_EQUALS(Library::Container::EMPTY, A.getYield("empty"));
ASSERT_EQUALS(Library::Container::AT_INDEX, A.getYield("at"));
ASSERT_EQUALS(Library::Container::START_ITERATOR, A.getYield("begin"));
ASSERT_EQUALS(Library::Container::END_ITERATOR, A.getYield("end"));
ASSERT_EQUALS(Library::Container::BUFFER, A.getYield("data"));
ASSERT_EQUALS(Library::Container::BUFFER_NT, A.getYield("c_str"));
ASSERT_EQUALS(Library::Container::ITEM, A.getYield("front"));
ASSERT_EQUALS(Library::Container::NO_YIELD, A.getYield("foo"));
ASSERT_EQUALS(Library::Container::RESIZE, A.getAction("resize"));
ASSERT_EQUALS(Library::Container::CLEAR, A.getAction("clear"));
ASSERT_EQUALS(Library::Container::PUSH, A.getAction("push_back"));
ASSERT_EQUALS(Library::Container::POP, A.getAction("pop_back"));
ASSERT_EQUALS(Library::Container::FIND, A.getAction("find"));
ASSERT_EQUALS(Library::Container::NO_ACTION, A.getAction("foo"));
ASSERT_EQUALS(B.type_templateArgNo, 1);
ASSERT_EQUALS(B.size_templateArgNo, 3);
ASSERT_EQUALS(B.startPattern, "std :: B <");
ASSERT_EQUALS(B.endPattern, "> !!::");
ASSERT_EQUALS(B.itEndPattern, "> :: iterator");
ASSERT_EQUALS(B.functions.size(), A.functions.size());
ASSERT_EQUALS(B.opLessAllowed, false);
ASSERT(C.functions.empty());
ASSERT_EQUALS(C.type_templateArgNo, -1);
ASSERT_EQUALS(C.size_templateArgNo, -1);
ASSERT_EQUALS(C.stdStringLike, true);
ASSERT_EQUALS(C.arrayLike_indexOp, true);
}
void version() const {
{
const char xmldata [] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
"</def>";
Library library;
const Library::Error err = readLibrary(library, xmldata);
ASSERT_EQUALS(err.errorcode, Library::OK);
}
{
const char xmldata [] = "<?xml version=\"1.0\"?>\n"
"<def format=\"1\">\n"
"</def>";
Library library;
const Library::Error err = readLibrary(library, xmldata);
ASSERT_EQUALS(err.errorcode, Library::OK);
}
{
const char xmldata [] = "<?xml version=\"1.0\"?>\n"
"<def format=\"42\">\n"
"</def>";
Library library;
const Library::Error err = readLibrary(library, xmldata);
ASSERT_EQUALS(err.errorcode, Library::UNSUPPORTED_FORMAT);
}
}
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 {
LOADLIBERROR("<?xml version=\"1.0\"?>\n"
"<def>\n"
" <X name=\"uint8_t,std::uint8_t\" size=\"1\"/>\n"
"</def>",
Library::UNKNOWN_ELEMENT);
// #define without attributes
LOADLIBERROR("<?xml version=\"1.0\"?>\n"
"<def>\n"
" <define />\n" // no attributes provided at all
"</def>",
Library::MISSING_ATTRIBUTE);
// #define with name but without value
LOADLIBERROR("<?xml version=\"1.0\"?>\n"
"<def>\n"
" <define name=\"foo\" />\n" // no value provided
"</def>",
Library::MISSING_ATTRIBUTE);
LOADLIBERROR("<?xml version=\"1.0\"?>\n"
"<def>\n"
" <define value=\"1\" />\n" // no name provided
"</def>",
Library::MISSING_ATTRIBUTE);
LOADLIBERROR("<?xml version=\"1.0\"?>\n"
"<X>\n"
"</X>",
Library::UNSUPPORTED_FORMAT);
// empty range
LOADLIB_ERROR_INVALID_RANGE("");
// letter as range
LOADLIB_ERROR_INVALID_RANGE("a");
// letter and number as range
LOADLIB_ERROR_INVALID_RANGE("1a");
// digit followed by dash
LOADLIB_ERROR_INVALID_RANGE("0:2-1");
// single dash
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:");
}
};
REGISTER_TEST(TestLibrary)