Library: Add <container> tag to Libraries, provide configuration for std::vector, std::deque, std::array and STL strings

Token: Added function to jump to the next template argument
This commit is contained in:
PKEuS 2015-01-03 20:35:33 +01:00
parent eb1c048d2a
commit e39729ffcc
7 changed files with 354 additions and 0 deletions

View File

@ -1932,6 +1932,71 @@
<formatstr/> <formatstr/>
</arg> </arg>
</function> </function>
<container id="stdContainer" endPattern="&gt; !!::">
<type templateParameter="0"/>
<size>
<function name="resize" action="resize"/>
<function name="clear" action="clear"/>
<function name="size" yields="size"/>
<function name="empty" yields="empty"/>
</size>
<access>
<function name="begin" yields="start-iterator"/>
<function name="cbegin" yields="start-iterator"/>
<function name="rbegin" yields="start-iterator"/>
<function name="crbegin" yields="start-iterator"/>
<function name="end" yields="end-iterator"/>
<function name="cend" yields="end-iterator"/>
<function name="rend" yields="end-iterator"/>
<function name="crend" yields="end-iterator"/>
</access>
</container>
<container id="stdVectorDeque" startPattern="std :: vector|deque &lt;" inherits="stdContainer">
<size>
<function name="push_back" action="push"/>
<function name="pop_back" action="pop"/>
<function name="push_front" action="push"/>
<function name="pop_front" action="pop"/>
</size>
<access indexOperator="array-like">
<function name="at" yields="at_index"/>
<function name="front" yields="item"/>
<function name="back" yields="item"/>
<function name="data" yields="buffer"/>
</access>
</container>
<container id="stdArray" startPattern="std :: array &lt;" inherits="stdContainer">
<size templateParameter="1"/>
<access indexOperator="array-like">
<function name="at" yields="at_index"/>
<function name="front" yields="item"/>
<function name="back" yields="item"/>
<function name="data" yields="buffer"/>
</access>
</container>
<container id="stdAllString" inherits="stdContainer">
<type string="std-like"/>
<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="item"/>
<function name="back" yields="item"/>
<function name="data" yields="buffer"/>
<function name="c_str" yields="buffer-nt"/>
<function name="length" yields="size"/>
</access>
</container>
<container id="stdBasicString" startPattern="std :: basic_string &lt;" inherits="stdAllString">
<type templateParameter="0"/>
</container>
<container id="stdString" startPattern="std :: string|wstring|u16string|u32string" endPattern="" inherits="stdAllString">
</container>
<podtype name="int8_t" sign="s" size="1"/> <podtype name="int8_t" sign="s" size="1"/>
<podtype name="int16_t" sign="s" size="2"/> <podtype name="int16_t" sign="s" size="2"/>
<podtype name="int32_t" sign="s" size="4"/> <podtype name="int32_t" sign="s" size="4"/>
@ -1950,6 +2015,7 @@
<podtype name="uint_fast64_t" sign="u"/> <podtype name="uint_fast64_t" sign="u"/>
<podtype name="intptr_t" sign="s"/> <podtype name="intptr_t" sign="s"/>
<podtype name="uintptr_t" sign="u"/> <podtype name="uintptr_t" sign="u"/>
<!--Not part of standard, but widely supported by runtime libraries--> <!--Not part of standard, but widely supported by runtime libraries-->
<function name="itoa"> <function name="itoa">
<noreturn>false</noreturn> <noreturn>false</noreturn>

View File

@ -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<std::string, Container>::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") { else if (nodename == "podtype") {
const char * const name = node->Attribute("name"); const char * const name = node->Attribute("name");
if (!name) if (!name)
@ -535,3 +635,25 @@ bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const
return false; return false;
} }
const Library::Container* Library::detectContainer(const Token* typeStart) const
{
for (std::map<std::string, Container>::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;
}

View File

@ -137,6 +137,51 @@ public:
bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const; 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<std::string, Function> functions;
int type_templateArgNo;
int size_templateArgNo;
bool arrayLike_indexOp;
bool stdStringLike;
Action getAction(const std::string& function) const {
std::map<std::string, Function>::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<std::string, Function>::const_iterator i = functions.find(function);
if (i != functions.end())
return i->second.yield;
return NO_YIELD;
}
};
std::map<std::string, Container> containers;
const Container* detectContainer(const Token* typeStart) const;
class ArgumentChecks { class ArgumentChecks {
public: public:
ArgumentChecks() : ArgumentChecks() :

View File

@ -788,6 +788,19 @@ Token* Token::nextArgumentBeforeCreateLinks2() const
return 0; 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 * Token::findClosingBracket() const
{ {
const Token *closing = nullptr; const Token *closing = nullptr;

View File

@ -649,6 +649,13 @@ public:
*/ */
Token* nextArgumentBeforeCreateLinks2() const; 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() * Returns the closing bracket of opening '<'. Should only be used if link()
* is unavailable. * is unavailable.

View File

@ -920,6 +920,32 @@ Checking unusedvar.cpp...
[unusedvar.cpp:2]: (style) Unused variable: a</programlisting> [unusedvar.cpp:2]: (style) Unused variable: a</programlisting>
</section> </section>
<section>
<title>container</title>
<para>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.</para>
<para>Inside the &lt;container&gt; tag, functions can be defined inside of the tags &lt;size&gt;, &lt;access&gt; and &lt;other&gt; (on your choice). Each of them can specify an action like "resize" and/or the result it yields, for example "end-iterator".</para>
<para>The following example provides a definition for std::vector, based on the definition of "stdContainer" (not shown):</para>
<programlisting>&lt;?xml version="1.0"?&gt;
&lt;def&gt;
&lt;container id="stdVector" startPattern="std :: vector &amp;lt;" inherits="stdContainer"&gt;
&lt;size&gt;
&lt;function name="push_back" action="push"/&gt;
&lt;function name="pop_back" action="pop"/&gt;
&lt;/size&gt;
&lt;access indexOperator="array-like"&gt;
&lt;function name="at" yields="at_index"/&gt;
&lt;function name="front" yields="fixed"/&gt;
&lt;function name="back" yields="fixed"/&gt;
&lt;/access&gt;
&lt;/container&gt;
&lt;/def&gt;</programlisting>
</section>
<section> <section>
<title>Example configuration for strcpy()</title> <title>Example configuration for strcpy()</title>

View File

@ -39,6 +39,7 @@ private:
TEST_CASE(memory2); // define extra "free" allocation functions TEST_CASE(memory2); // define extra "free" allocation functions
TEST_CASE(resource); TEST_CASE(resource);
TEST_CASE(podtype); TEST_CASE(podtype);
TEST_CASE(container);
TEST_CASE(version); TEST_CASE(version);
} }
@ -276,6 +277,80 @@ private:
ASSERT_EQUALS(0, type ? type->sign : '?'); ASSERT_EQUALS(0, type ? type->sign : '?');
} }
void container() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <container id=\"A\" startPattern=\"std :: A &lt;\" endPattern=\"&gt; !!::\">\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"
" </access>\n"
" </container>\n"
" <container id=\"B\" startPattern=\"std :: B &lt;\" inherits=\"A\">\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>";
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 { void version() const {
{ {
const char xmldata [] = "<?xml version=\"1.0\"?>\n" const char xmldata [] = "<?xml version=\"1.0\"?>\n"