Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions lib/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1147,16 +1147,18 @@ bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const
return false;
}

const Library::Container* Library::detectContainerInternal(const Token* typeStart, DetectContainer detect, bool* isIterator) const
const Library::Container* Library::detectContainerInternal(const Token* typeStart, DetectContainer detect, bool* isIterator, bool withoutStd) const
{
for (const std::pair<const std::string, Library::Container> & c : containers) {
const Container& container = c.second;
if (container.startPattern.empty())
continue;

const int offset = (withoutStd && container.startPattern2.find("std :: ") == 0) ? 7 : 0;

// If endPattern is undefined, it will always match, but itEndPattern has to be defined.
if (detect != IteratorOnly && container.endPattern.empty()) {
if (!Token::Match(typeStart, container.startPattern2.c_str()))
if (!Token::Match(typeStart, container.startPattern2.c_str() + offset))
continue;

if (isIterator)
Expand All @@ -1168,7 +1170,7 @@ const Library::Container* Library::detectContainerInternal(const Token* typeStar
if (!tok->link())
continue;

const bool matchedStartPattern = Token::Match(typeStart, container.startPattern2.c_str());
const bool matchedStartPattern = Token::Match(typeStart, container.startPattern2.c_str() + offset);

if (detect != ContainerOnly && matchedStartPattern && Token::Match(tok->link(), container.itEndPattern.c_str())) {
if (isIterator)
Expand Down Expand Up @@ -1196,10 +1198,10 @@ const Library::Container* Library::detectIterator(const Token* typeStart) const
return detectContainerInternal(typeStart, IteratorOnly);
}

const Library::Container* Library::detectContainerOrIterator(const Token* typeStart, bool* isIterator) const
const Library::Container* Library::detectContainerOrIterator(const Token* typeStart, bool* isIterator, bool withoutStd) const
{
bool res;
const Library::Container* c = detectContainerInternal(typeStart, Both, &res);
const Library::Container* c = detectContainerInternal(typeStart, Both, &res, withoutStd);
if (c && isIterator)
*isIterator = res;
return c;
Expand Down Expand Up @@ -1626,9 +1628,9 @@ bool Library::isSmartPointer(const Token* tok) const
return detectSmartPointer(tok);
}

const Library::SmartPointer* Library::detectSmartPointer(const Token* tok) const
const Library::SmartPointer* Library::detectSmartPointer(const Token* tok, bool withoutStd) const
{
std::string typestr;
std::string typestr = withoutStd ? "std::" : "";
while (Token::Match(tok, "%name%|::")) {
typestr += tok->str();
tok = tok->next();
Expand Down Expand Up @@ -1665,6 +1667,13 @@ Library::TypeCheck Library::getTypeCheck(std::string check, std::string typeNam
return it == mTypeChecks.end() ? TypeCheck::def : it->second;
}

bool Library::hasAnyTypeCheck(const std::string& typeName) const
{
return std::any_of(mTypeChecks.begin(), mTypeChecks.end(), [&](const std::pair<std::pair<std::string, std::string>, Library::TypeCheck>& tc) {
return tc.first.second == typeName;
});
}

std::shared_ptr<Token> createTokenFromExpression(const std::string& returnValue,
const Settings* settings,
std::unordered_map<nonneg int, const Token*>* lookupVarId)
Expand Down
7 changes: 4 additions & 3 deletions lib/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ class CPPCHECKLIB Library {
std::map<std::string, Container> containers;
const Container* detectContainer(const Token* typeStart) const;
const Container* detectIterator(const Token* typeStart) const;
const Container* detectContainerOrIterator(const Token* typeStart, bool* isIterator = nullptr) const;
const Container* detectContainerOrIterator(const Token* typeStart, bool* isIterator = nullptr, bool withoutStd = false) const;

class ArgumentChecks {
public:
Expand Down Expand Up @@ -485,7 +485,7 @@ class CPPCHECKLIB Library {

std::unordered_map<std::string, SmartPointer> smartPointers;
bool isSmartPointer(const Token *tok) const;
const SmartPointer* detectSmartPointer(const Token* tok) const;
const SmartPointer* detectSmartPointer(const Token* tok, bool withoutStd = false) const;

struct PodType {
unsigned int size;
Expand Down Expand Up @@ -560,6 +560,7 @@ class CPPCHECKLIB Library {
checkFiniteLifetime, // (unusedvar) object has side effects, but immediate destruction is wrong
};
TypeCheck getTypeCheck(std::string check, std::string typeName) const;
bool hasAnyTypeCheck(const std::string& typeName) const;

private:
// load a <function> xml node
Expand Down Expand Up @@ -655,7 +656,7 @@ class CPPCHECKLIB Library {
}

enum DetectContainer { ContainerOnly, IteratorOnly, Both };
const Library::Container* detectContainerInternal(const Token* typeStart, DetectContainer detect, bool* isIterator = nullptr) const;
const Library::Container* detectContainerInternal(const Token* typeStart, DetectContainer detect, bool* isIterator = nullptr, bool withoutStd = false) const;
};

CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok);
Expand Down
99 changes: 23 additions & 76 deletions lib/tokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9379,106 +9379,53 @@ void Tokenizer::simplifyBitfields()
}
}


// Types and objects in std namespace that are neither functions nor templates
static const std::set<std::string> stdTypes = {
"string", "wstring", "u16string", "u32string",
Comment thread
danmar marked this conversation as resolved.
"iostream", "ostream", "ofstream", "ostringstream",
"istream", "ifstream", "istringstream", "fstream", "stringstream",
"wstringstream", "wistringstream", "wostringstream", "wstringbuf",
"stringbuf", "streambuf", "ios", "filebuf", "ios_base",
"exception", "bad_exception", "bad_alloc",
"logic_error", "domain_error", "invalid_argument_", "length_error",
"out_of_range", "runtime_error", "range_error", "overflow_error", "underflow_error",
"locale",
"cout", "cerr", "clog", "cin",
"wcerr", "wcin", "wclog", "wcout",
"endl", "ends", "flush",
"boolalpha", "noboolalpha", "showbase", "noshowbase",
"showpoint", "noshowpoint", "showpos", "noshowpos",
"skipws", "noskipws", "unitbuf", "nounitbuf", "uppercase", "nouppercase",
"dec", "hex", "oct",
"fixed", "scientific",
"internal", "left", "right",
"fpos", "streamoff", "streampos", "streamsize"
};

static const std::set<std::string> stdTemplates = {
"array", "basic_string", "bitset", "deque", "list", "map", "multimap",
"priority_queue", "queue", "set", "multiset", "stack", "vector", "pair",
"iterator", "iterator_traits",
"unordered_map", "unordered_multimap", "unordered_set", "unordered_multiset",
"tuple", "function"
};
static const std::set<std::string> stdFunctions = {
"getline",
"for_each", "find", "find_if", "find_end", "find_first_of",
"adjacent_find", "count", "count_if", "mismatch", "equal", "search", "search_n",
"copy", "copy_backward", "swap", "swap_ranges", "iter_swap", "transform", "replace",
"replace_if", "replace_copy", "replace_copy_if", "fill", "fill_n", "generate", "generate_n", "remove",
"remove_if", "remove_copy", "remove_copy_if",
"unique", "unique_copy", "reverse", "reverse_copy",
"rotate", "rotate_copy", "random_shuffle", "partition", "stable_partition",
"sort", "stable_sort", "partial_sort", "partial_sort_copy", "nth_element",
"lower_bound", "upper_bound", "equal_range", "binary_search", "merge", "inplace_merge", "includes",
"set_union", "set_intersection", "set_difference",
"set_symmetric_difference", "push_heap", "pop_heap", "make_heap", "sort_heap",
"min", "max", "min_element", "max_element", "lexicographical_compare", "next_permutation", "prev_permutation",
"advance", "back_inserter", "distance", "front_inserter", "inserter",
"make_pair", "make_shared", "make_tuple",
"begin", "cbegin", "rbegin", "crbegin",
"end", "cend", "rend", "crend"
};


// Add std:: in front of std classes, when using namespace std; was given
void Tokenizer::simplifyNamespaceStd()
{
if (!isCPP())
return;

const bool isCPP11 = mSettings->standards.cpp == Standards::CPP11;

std::set<std::string> userFunctions;

for (Token* tok = Token::findsimplematch(list.front(), "using namespace std ;"); tok; tok = tok->next()) {
bool insert = false;
if (Token::Match(tok, "enum class|struct| %name%| :|{")) { // Don't replace within enum definitions
skipEnumBody(&tok);
}
if (!Token::Match(tok->previous(), ".|::|namespace")) {
if (Token::Match(tok, "%name% (")) {
if (isFunctionHead(tok->next(), "{"))
if (!tok->isName() || tok->isKeyword() || tok->isStandardType() || tok->varId())
continue;
if (Token::Match(tok->previous(), ".|::|namespace"))
continue;
if (Token::simpleMatch(tok->next(), "(")) {
if (isFunctionHead(tok->next(), "{"))
userFunctions.insert(tok->str());
else if (isFunctionHead(tok->next(), ";")) {
const Token *start = tok;
while (Token::Match(start->previous(), "%type%|*|&"))
start = start->previous();
if (start != tok && start->isName() && (!start->previous() || Token::Match(start->previous(), "[;{}]")))
userFunctions.insert(tok->str());
else if (isFunctionHead(tok->next(), ";")) {
const Token *start = tok;
while (Token::Match(start->previous(), "%type%|*|&"))
start = start->previous();
if (start != tok && start->isName() && (!start->previous() || Token::Match(start->previous(), "[;{}]")))
userFunctions.insert(tok->str());
}
if (userFunctions.find(tok->str()) == userFunctions.end() && stdFunctions.find(tok->str()) != stdFunctions.end())
insert = true;
} else if (Token::Match(tok, "%name% <") && stdTemplates.find(tok->str()) != stdTemplates.end())
insert = true;
else if (tok->isName() && !tok->varId() && !Token::Match(tok->next(), "(|<") && stdTypes.find(tok->str()) != stdTypes.end())
}
if (userFunctions.find(tok->str()) == userFunctions.end() && mSettings->library.matchArguments(tok, "std::" + tok->str()))
insert = true;
}
} else if (Token::simpleMatch(tok->next(), "<") &&
(mSettings->library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true) || mSettings->library.detectSmartPointer(tok, /*withoutStd*/ true)))
insert = true;
else if (mSettings->library.hasAnyTypeCheck("std::" + tok->str()) ||
mSettings->library.podtype("std::" + tok->str()) ||
mSettings->library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true))
insert = true;

if (insert) {
tok->previous()->insertToken("std");
tok->previous()->linenr(tok->linenr()); // For stylistic reasons we put the std:: in the same line as the following token
tok->previous()->fileIndex(tok->fileIndex());
tok->previous()->insertToken("::");
} else if (isCPP11 && Token::Match(tok, "!!:: tr1 ::"))
tok->next()->str("std");
}
}

for (Token* tok = list.front(); tok; tok = tok->next()) {
if (isCPP11 && Token::simpleMatch(tok, "std :: tr1 ::"))
Token::eraseTokens(tok, tok->tokAt(3));

else if (Token::simpleMatch(tok, "using namespace std ;")) {
if (Token::simpleMatch(tok, "using namespace std ;")) {
Token::eraseTokens(tok, tok->tokAt(4));
tok->deleteThis();
}
Expand Down
12 changes: 12 additions & 0 deletions test/testautovariables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,18 @@ class TestAutoVariables : public TestFixture {
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());

check("using namespace std;\n" // #10971
"struct S { int i = 3; };\n"
"unique_ptr<S> g() {\n"
" auto tp = make_unique<S>();\n"
" return tp;\n"
"}\n"
"void f() {\n"
" const S& s = *g();\n"
" (void)s.i;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:9]: (error) Using reference to dangling temporary.\n", errout.str());
}

void testglobalnamespace() {
Expand Down
8 changes: 8 additions & 0 deletions test/testleakautovar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,14 @@ class TestLeakAutoVar : public TestFixture {
" delete i;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());

check("using namespace std;\n" // #9708
"void f() {\n"
" int* i = new int;\n"
" unique_ptr<int> x(i);\n"
" delete i;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
}

void doublefree9() {
Expand Down
2 changes: 1 addition & 1 deletion test/testsimplifytemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ class TestSimplifyTemplate : public TestFixture {
std::string tok_(const char* file, int line, const char code[], bool debugwarnings = false, cppcheck::Platform::Type type = cppcheck::Platform::Type::Native) {
errout.str("");

const Settings settings1 = settingsBuilder(settings).debugwarnings(debugwarnings).platform(type).build();
const Settings settings1 = settingsBuilder(settings).library("std.cfg").debugwarnings(debugwarnings).platform(type).build();
Tokenizer tokenizer(&settings1, this);

std::istringstream istr(code);
Expand Down
41 changes: 29 additions & 12 deletions test/testtokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class TestTokenizer : public TestFixture {
private:
// If there are unused templates, keep those
const Settings settings0 = settingsBuilder().library("qt.cfg").checkUnusedTemplates().build();
const Settings settings1 = settingsBuilder().library("qt.cfg").checkUnusedTemplates().build();
const Settings settings1 = settingsBuilder().library("qt.cfg").library("std.cfg").checkUnusedTemplates().build();
const Settings settings2 = settingsBuilder().library("qt.cfg").checkUnusedTemplates().build();
const Settings settings_windows = settingsBuilder().library("windows.cfg").checkUnusedTemplates().build();

Expand Down Expand Up @@ -4563,7 +4563,7 @@ class TestTokenizer : public TestFixture {

code = "using namespace std;\n"
"string<wchar_t> s;"; // That's obviously not std::string
ASSERT_EQUALS("string < wchar_t > s ;", tokenizeAndStringify(code));
TODO_ASSERT_EQUALS("string < wchar_t > s ;", "std :: string < wchar_t > s ;", tokenizeAndStringify(code));

code = "using namespace std;\n"
"swap s;"; // That's obviously not std::swap
Expand All @@ -4573,15 +4573,6 @@ class TestTokenizer : public TestFixture {
"std::string s;";
ASSERT_EQUALS("std :: string s ;", tokenizeAndStringify(code));

code = "using namespace std;\n"
"tr1::function <void(int)> f;";
ASSERT_EQUALS("tr1 :: function < void ( int ) > f ;", tokenizeAndStringify(code, true, cppcheck::Platform::Type::Native, "test.cpp", Standards::CPP03));
ASSERT_EQUALS("std :: function < void ( int ) > f ;", tokenizeAndStringify(code));

code = "std::tr1::function <void(int)> f;";
ASSERT_EQUALS("std :: tr1 :: function < void ( int ) > f ;", tokenizeAndStringify(code, true, cppcheck::Platform::Type::Native, "test.cpp", Standards::CPP03));
ASSERT_EQUALS("std :: function < void ( int ) > f ;", tokenizeAndStringify(code));

// #4042 (Do not add 'std ::' to variables)
code = "using namespace std;\n"
"const char * string = \"Hi\";";
Expand All @@ -4596,7 +4587,12 @@ class TestTokenizer : public TestFixture {
"std :: cout << string << std :: endl ;\n"
"return string ;\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code));
TODO_ASSERT_EQUALS(expected,
"std :: string f ( const char * string ) {\n"
"cout << string << endl ;\n"
"return string ;\n"
"}",
tokenizeAndStringify(code));

code = "using namespace std;\n"
"void f() {\n"
Expand Down Expand Up @@ -4651,6 +4647,27 @@ class TestTokenizer : public TestFixture {
tokenizeAndStringify("using namespace std; enum E : int ; void foo ( ) { string s ; }"));

ASSERT_NO_THROW(tokenizeAndStringify("NS_BEGIN(IMAGEIO_2D_DICOM) using namespace std; NS_END")); // #11045

code = "using namespace std;\n"
"void f(const unique_ptr<int>& p) {\n"
" if (!p)\n"
" throw runtime_error(\"abc\");\n"
"}";
expected = "void f ( const std :: unique_ptr < int > & p ) {\n"
"if ( ! p ) {\n"
"throw std :: runtime_error ( \"abc\" ) ; }\n"
"}";
TODO_ASSERT_EQUALS(expected,
"void f ( const std :: unique_ptr < int > & p ) {\n"
"if ( ! p ) {\n"
"throw runtime_error ( \"abc\" ) ; }\n"
"}",
tokenizeAndStringify(code));

code = "using namespace std;\n" // #8454
"void f() { string str = to_string(1); }\n";
expected = "void f ( ) { std :: string str ; str = std :: to_string ( 1 ) ; }";
ASSERT_EQUALS(expected, tokenizeAndStringify(code));
}

void microsoftMemory() {
Expand Down