diff --git a/cfg/std.cfg b/cfg/std.cfg
index 132f2b679..4cd2a00a0 100644
--- a/cfg/std.cfg
+++ b/cfg/std.cfg
@@ -1932,6 +1932,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1950,6 +2015,7 @@
+
false
diff --git a/lib/library.cpp b/lib/library.cpp
index fa3a67579..ea900f631 100644
--- a/lib/library.cpp
+++ b/lib/library.cpp
@@ -388,6 +388,106 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
}
}
+ else if (nodename == "container") {
+ const char* const id = node->Attribute("id");
+ if (!id)
+ return Error(MISSING_ATTRIBUTE, "id");
+
+ Container& container = containers[id];
+
+ const char* const inherits = node->Attribute("inherits");
+ if (inherits) {
+ std::map::const_iterator i = containers.find(inherits);
+ if (inherits)
+ container = i->second; // Take values from parent and overwrite them if necessary
+ else
+ return Error(BAD_ATTRIBUTE_VALUE, inherits);
+ }
+
+ const char* const startPattern = node->Attribute("startPattern");
+ if (startPattern)
+ container.startPattern = startPattern;
+ const char* const endPattern = node->Attribute("endPattern");
+ if (endPattern)
+ container.endPattern = endPattern;
+
+ for (const tinyxml2::XMLElement *containerNode = node->FirstChildElement(); containerNode; containerNode = containerNode->NextSiblingElement()) {
+ const std::string containerNodeName = containerNode->Name();
+ if (containerNodeName == "size" || containerNodeName == "access" || containerNodeName == "other") {
+ for (const tinyxml2::XMLElement *functionNode = containerNode->FirstChildElement(); functionNode; functionNode = functionNode->NextSiblingElement()) {
+ if (std::string(functionNode->Name()) != "function")
+ return Error(BAD_ELEMENT, functionNode->Name());
+
+ const char* const functionName = functionNode->Attribute("name");
+ if (!functionName)
+ return Error(MISSING_ATTRIBUTE, "name");
+
+ const char* const action_ptr = functionNode->Attribute("action");
+ Container::Action action = Container::NO_ACTION;
+ if (action_ptr) {
+ std::string actionName = action_ptr;
+ if (actionName == "resize")
+ action = Container::RESIZE;
+ else if (actionName == "clear")
+ action = Container::CLEAR;
+ else if (actionName == "push")
+ action = Container::PUSH;
+ else if (actionName == "pop")
+ action = Container::POP;
+ else
+ return Error(BAD_ATTRIBUTE_VALUE, actionName);
+ }
+
+ const char* const yield_ptr = functionNode->Attribute("yields");
+ Container::Yield yield = Container::NO_YIELD;
+ if (yield_ptr) {
+ std::string yieldName = yield_ptr;
+ if (yieldName == "at_index")
+ yield = Container::AT_INDEX;
+ else if (yieldName == "item")
+ yield = Container::ITEM;
+ else if (yieldName == "buffer")
+ yield = Container::BUFFER;
+ else if (yieldName == "buffer-nt")
+ yield = Container::BUFFER_NT;
+ else if (yieldName == "start-iterator")
+ yield = Container::START_ITERATOR;
+ else if (yieldName == "end-iterator")
+ yield = Container::END_ITERATOR;
+ else if (yieldName == "size")
+ yield = Container::SIZE;
+ else if (yieldName == "empty")
+ yield = Container::EMPTY;
+ else
+ return Error(BAD_ATTRIBUTE_VALUE, yieldName);
+ }
+
+ container.functions[functionName].action = action;
+ container.functions[functionName].yield = yield;
+ }
+
+ if (containerNodeName == "size") {
+ const char* const templateArg = containerNode->Attribute("templateParameter");
+ if (templateArg)
+ container.size_templateArgNo = atoi(templateArg);
+ } else if (containerNodeName == "access") {
+ const char* const indexArg = containerNode->Attribute("indexOperator");
+ if (indexArg)
+ container.arrayLike_indexOp = std::string(indexArg) == "array-like";
+ }
+ } else if (containerNodeName == "type") {
+ const char* const templateArg = containerNode->Attribute("templateParameter");
+ if (templateArg)
+ container.type_templateArgNo = atoi(templateArg);
+
+ const char* const string = containerNode->Attribute("string");
+ if (string)
+ container.stdStringLike = std::string(string) == "std-like";
+ } else
+ return Error(BAD_ELEMENT, containerNodeName);
+ }
+ }
+
else if (nodename == "podtype") {
const char * const name = node->Attribute("name");
if (!name)
@@ -535,3 +635,25 @@ bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const
return false;
}
+const Library::Container* Library::detectContainer(const Token* typeStart) const
+{
+ for (std::map::const_iterator i = containers.begin(); i != containers.end(); ++i) {
+ const Container& container = i->second;
+ if (container.startPattern.empty())
+ continue;
+
+ if (Token::Match(typeStart, container.startPattern.c_str())) {
+ if (container.endPattern.empty())
+ return &container;
+
+ for (const Token* tok = typeStart; tok && !tok->varId(); tok = tok->next()) {
+ if (tok->link()) {
+ if (Token::Match(tok->link(), container.endPattern.c_str()))
+ return &container;
+ break;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
diff --git a/lib/library.h b/lib/library.h
index 6560683ac..a1f787e02 100644
--- a/lib/library.h
+++ b/lib/library.h
@@ -137,6 +137,51 @@ public:
bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const;
+ class Container {
+ public:
+ Container() :
+ type_templateArgNo(-1),
+ size_templateArgNo(-1),
+ arrayLike_indexOp(false),
+ stdStringLike(false) {
+ }
+
+ enum Action {
+ RESIZE, CLEAR, PUSH, POP,
+ NO_ACTION
+ };
+ enum Yield {
+ AT_INDEX, ITEM, BUFFER, BUFFER_NT, START_ITERATOR, END_ITERATOR, SIZE, EMPTY,
+ NO_YIELD
+ };
+ struct Function {
+ Action action;
+ Yield yield;
+ };
+ std::string startPattern, endPattern;
+ std::map functions;
+ int type_templateArgNo;
+ int size_templateArgNo;
+ bool arrayLike_indexOp;
+ bool stdStringLike;
+
+ Action getAction(const std::string& function) const {
+ std::map::const_iterator i = functions.find(function);
+ if (i != functions.end())
+ return i->second.action;
+ return NO_ACTION;
+ }
+
+ Yield getYield(const std::string& function) const {
+ std::map::const_iterator i = functions.find(function);
+ if (i != functions.end())
+ return i->second.yield;
+ return NO_YIELD;
+ }
+ };
+ std::map containers;
+ const Container* detectContainer(const Token* typeStart) const;
+
class ArgumentChecks {
public:
ArgumentChecks() :
diff --git a/lib/token.cpp b/lib/token.cpp
index 9eb671a49..077ff248b 100644
--- a/lib/token.cpp
+++ b/lib/token.cpp
@@ -788,6 +788,19 @@ Token* Token::nextArgumentBeforeCreateLinks2() const
return 0;
}
+Token* Token::nextTemplateArgument() const
+{
+ for (const Token* tok = this; tok; tok = tok->next()) {
+ if (tok->str() == ",")
+ return tok->next();
+ else if (tok->link() && Token::Match(tok, "(|{|[|<"))
+ tok = tok->link();
+ else if (Token::Match(tok, ">|;"))
+ return 0;
+ }
+ return 0;
+}
+
const Token * Token::findClosingBracket() const
{
const Token *closing = nullptr;
diff --git a/lib/token.h b/lib/token.h
index a7316663b..b071856a5 100644
--- a/lib/token.h
+++ b/lib/token.h
@@ -649,6 +649,13 @@ public:
*/
Token* nextArgumentBeforeCreateLinks2() const;
+ /**
+ * @return the first token of the next template argument. Does only work on template argument
+ * lists. Requires that Tokenizer::createLinks2() has been called before.
+ * Returns 0, if there is no next argument.
+ */
+ Token* nextTemplateArgument() const;
+
/**
* Returns the closing bracket of opening '<'. Should only be used if link()
* is unavailable.
diff --git a/man/manual.docbook b/man/manual.docbook
index f60f7d443..6eecba9fb 100644
--- a/man/manual.docbook
+++ b/man/manual.docbook
@@ -920,6 +920,32 @@ Checking unusedvar.cpp...
[unusedvar.cpp:2]: (style) Unused variable: a
+
+ container
+
+ A lot of C++ libraries, among those the STL itself, provide containers with very similar functionality. Libraries can be used to tell cppcheck about their behaviour. Each container needs a unique ID. It can optionally have a startPattern, which must be a valid Token::Match pattern and an endPattern that is compared to the linked token of the first token with such a link. The optional attribute "inherits" takes an ID from a previously defined container.
+
+ Inside the <container> tag, functions can be defined inside of the tags <size>, <access> and <other> (on your choice). Each of them can specify an action like "resize" and/or the result it yields, for example "end-iterator".
+
+ The following example provides a definition for std::vector, based on the definition of "stdContainer" (not shown):
+
+ <?xml version="1.0"?>
+<def>
+ <container id="stdVector" startPattern="std :: vector <" inherits="stdContainer">
+ <size>
+ <function name="push_back" action="push"/>
+ <function name="pop_back" action="pop"/>
+ </size>
+ <access indexOperator="array-like">
+ <function name="at" yields="at_index"/>
+ <function name="front" yields="fixed"/>
+ <function name="back" yields="fixed"/>
+ </access>
+ </container>
+</def>
+
+
+
Example configuration for strcpy()
diff --git a/test/testlibrary.cpp b/test/testlibrary.cpp
index 3e8a357f7..7b35de41b 100644
--- a/test/testlibrary.cpp
+++ b/test/testlibrary.cpp
@@ -39,6 +39,7 @@ private:
TEST_CASE(memory2); // define extra "free" allocation functions
TEST_CASE(resource);
TEST_CASE(podtype);
+ TEST_CASE(container);
TEST_CASE(version);
}
@@ -276,6 +277,80 @@ private:
ASSERT_EQUALS(0, type ? type->sign : '?');
}
+ void container() const {
+ const char xmldata[] = "\n"
+ "\n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n" // Inherits all but templateParameter
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ "";
+ tinyxml2::XMLDocument doc;
+ doc.Parse(xmldata, sizeof(xmldata));
+
+ Library library;
+ library.load(doc);
+
+ 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.stdStringLike, false);
+ ASSERT_EQUALS(A.arrayLike_indexOp, false);
+ 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::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.functions.size(), A.functions.size());
+
+ 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 [] = "\n"