From 8af044255ddb67731167ccf3da678eaf6b432b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 24 Jun 2012 13:39:14 +0200 Subject: [PATCH] Tokenizer: Added new function isFunctionParameterPassedByValue that check if a parameter is passed by value --- lib/tokenize.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++ lib/tokenize.h | 8 +++++ test/testtokenize.cpp | 23 +++++++++++++ 3 files changed, 107 insertions(+) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index ef7cd68d2..94f76186b 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -6089,6 +6089,15 @@ bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsign ret = true; } + // parameter in function call.. + if (tok3->varId() == varid && Token::Match(tok3->previous(), "[(,] %var% [,)]")) { + // If the parameter is passed by value then simplify it + if (isFunctionParameterPassedByValue(tok3)) { + tok3->str(value); + ret = true; + } + } + // Variable is used somehow in a non-defined pattern => bail out if (tok3->varId() == varid) { // This is a really generic bailout so let's try to avoid this. @@ -7298,6 +7307,73 @@ const Token *Tokenizer::getFunctionTokenByName(const char funcname[]) const return NULL; } +bool Tokenizer::isFunctionParameterPassedByValue(const Token *fpar) const +{ + // TODO: If symbol database is available, use it. + const Token *ftok; + + // Look at function call, what parameter number is it? + unsigned int paranthesis = 1; + unsigned int parameter = 1; + for (ftok = fpar; ftok; ftok = ftok->previous()) { + if (ftok->str() == "(") { + --paranthesis; + if (paranthesis == 0) { + break; + } + } else if (ftok->str() == ")") { + ++paranthesis; + } else if (paranthesis == 1 && ftok->str() == ",") { + ++parameter; + } else if (Token::Match(ftok, "[;{}]")) { + break; + } + } + + // Is this a function call? + if (ftok && Token::Match(ftok->tokAt(-2), "[;{}=] %var% (")) { + const std::string functionName(ftok->previous()->str() + " ("); + + // Locate function declaration.. + unsigned int indentlevel = 0; + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (tok->str() == "{") + ++indentlevel; + else if (tok->str() == "}") + indentlevel = (indentlevel > 0) ? indentlevel - 1U : 0U; + else if (indentlevel == 0 && tok->isName() && Token::simpleMatch(tok, functionName.c_str())) { + // Goto parameter + tok = tok->tokAt(2); + unsigned int par = 1; + while (tok && par < parameter) { + if (tok->str() == ")") + break; + if (tok->str() == ",") + ++par; + tok = tok->next(); + } + if (!tok) + return false; + + // If parameter was found, determine if it's passed by value + if (par == parameter) { + bool knowntype = false; + while (tok && tok->isName()) { + knowntype |= tok->isStandardType(); + knowntype |= (tok->str() == "struct"); + tok = tok->next(); + } + if (!tok || !knowntype) + return false; + if (tok->str() != "," && tok->str() != ")") + return false; + return true; + } + } + } + } + return false; +} //--------------------------------------------------------------------------- diff --git a/lib/tokenize.h b/lib/tokenize.h index c3af9557e..4ccddee06 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -160,6 +160,14 @@ public: */ const Token *getFunctionTokenByName(const char funcname[]) const; + /** + * Try to determine if function parameter is passed by value by looking + * at the function declaration. + * @param fpar token for function parameter in the function call + * @return true if the parameter is passed by value. if unsure, false is returned + */ + bool isFunctionParameterPassedByValue(const Token *fpar) const; + /** * get error messages that the tokenizer generate */ diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index c9ce837c5..1afae1830 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -168,6 +168,7 @@ private: TEST_CASE(simplifyKnownVariablesBailOutSwitchBreak); // ticket #2324 TEST_CASE(simplifyKnownVariablesFloat); // #2454 - float variable TEST_CASE(simplifyKnownVariablesClassMember); // #2815 - value of class member may be changed by function call + TEST_CASE(simplifyKnownVariablesFunctionCalls); // Function calls (don't assume pass by reference) TEST_CASE(simplifyExternC); TEST_CASE(varid1); @@ -2515,6 +2516,28 @@ private: ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); } + void simplifyKnownVariablesFunctionCalls() { + { + const char code[] = "void a(int x);" // <- x is passed by value + "void b() {" + " int x = 123;" + " a(x);" // <- replace with a(123); + "}"; + const char expected[] = "void a ( int x ) ; void b ( ) { a ( 123 ) ; }"; + ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); + } + + { + const char code[] = "void a(int &x);" // <- x is passed by reference + "void b() {" + " int x = 123;" + " a(x);" // <- don't replace with a(123); + "}"; + const char expected[] = "void a ( int & x ) ; void b ( ) { int x ; x = 123 ; a ( x ) ; }"; + ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); + } + } + void simplifyKnownVariablesClassMember() { // Ticket #2815 {