This commit is contained in:
parent
5e7f2bd904
commit
70745b527a
2
Makefile
2
Makefile
|
@ -887,7 +887,7 @@ test/testvaarg.o: test/testvaarg.cpp lib/addoninfo.h lib/check.h lib/checkvaarg.
|
|||
test/testvalueflow.o: test/testvalueflow.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
|
||||
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvalueflow.cpp
|
||||
|
||||
test/testvarid.o: test/testvarid.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h
|
||||
test/testvarid.o: test/testvarid.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
|
||||
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvarid.cpp
|
||||
|
||||
externals/simplecpp/simplecpp.o: externals/simplecpp/simplecpp.cpp externals/simplecpp/simplecpp.h
|
||||
|
|
|
@ -1571,16 +1571,101 @@ static std::string getIncompleteNameID(const Token* tok)
|
|||
return result + tok->expressionString();
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct ExprIdKey {
|
||||
std::string parentOp;
|
||||
nonneg int operand1;
|
||||
nonneg int operand2;
|
||||
bool operator<(const ExprIdKey& k) const {
|
||||
return std::tie(parentOp, operand1, operand2) < std::tie(k.parentOp, k.operand1, k.operand2);
|
||||
}
|
||||
};
|
||||
using ExprIdMap = std::map<ExprIdKey, nonneg int>;
|
||||
void setParentExprId(Token* tok, bool cpp, ExprIdMap& exprIdMap, nonneg int &id) {
|
||||
if (!tok->astParent() || tok->astParent()->isControlFlowKeyword())
|
||||
return;
|
||||
const Token* op1 = tok->astParent()->astOperand1();
|
||||
if (op1 && op1->exprId() == 0)
|
||||
return;
|
||||
const Token* op2 = tok->astParent()->astOperand2();
|
||||
if (op2 && op2->exprId() == 0)
|
||||
return;
|
||||
|
||||
if (tok->astParent()->isExpandedMacro() || Token::Match(tok->astParent(), "++|--")) {
|
||||
tok->astParent()->exprId(id);
|
||||
++id;
|
||||
setParentExprId(tok->astParent(), cpp, exprIdMap, id);
|
||||
return;
|
||||
}
|
||||
|
||||
ExprIdKey key;
|
||||
key.parentOp = tok->astParent()->str();
|
||||
key.operand1 = op1 ? op1->exprId() : 0;
|
||||
key.operand2 = op2 ? op2->exprId() : 0;
|
||||
|
||||
if (tok->astParent()->isCast() && tok->astParent()->str() == "(") {
|
||||
const Token* typeStartToken;
|
||||
const Token* typeEndToken;
|
||||
if (tok->astParent()->astOperand2()) {
|
||||
typeStartToken = tok->astParent()->astOperand1();
|
||||
typeEndToken = tok;
|
||||
} else {
|
||||
typeStartToken = tok->astParent()->next();
|
||||
typeEndToken = tok->astParent()->link();
|
||||
}
|
||||
std::string type;
|
||||
for (const Token* t = typeStartToken; t != typeEndToken; t = t->next()) {
|
||||
type += " " + t->str();
|
||||
}
|
||||
key.parentOp += type;
|
||||
}
|
||||
|
||||
for (const auto& ref: followAllReferences(op1)) {
|
||||
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
|
||||
key.operand1 = ref.token->exprId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const auto& ref: followAllReferences(op2)) {
|
||||
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
|
||||
key.operand2 = ref.token->exprId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (key.operand1 > key.operand2 && key.operand2 &&
|
||||
Token::Match(tok->astParent(), "%or%|%oror%|+|*|&|&&|^|==|!=")) {
|
||||
// In C++ the order of operands of + might matter
|
||||
if (!cpp ||
|
||||
key.parentOp != "+" ||
|
||||
!tok->astParent()->valueType() ||
|
||||
tok->astParent()->valueType()->isIntegral() ||
|
||||
tok->astParent()->valueType()->isFloat() ||
|
||||
tok->astParent()->valueType()->pointer > 0)
|
||||
std::swap(key.operand1, key.operand2);
|
||||
}
|
||||
|
||||
const auto it = exprIdMap.find(key);
|
||||
if (it == exprIdMap.end()) {
|
||||
exprIdMap[key] = id;
|
||||
tok->astParent()->exprId(id);
|
||||
++id;
|
||||
} else {
|
||||
tok->astParent()->exprId(it->second);
|
||||
}
|
||||
setParentExprId(tok->astParent(), cpp, exprIdMap, id);
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolDatabase::createSymbolDatabaseExprIds()
|
||||
{
|
||||
nonneg int base = 0;
|
||||
// Find highest varId
|
||||
for (const Variable *var : mVariableList) {
|
||||
if (!var)
|
||||
continue;
|
||||
base = std::max<MathLib::bigint>(base, var->declarationId());
|
||||
nonneg int maximumVarId = 0;
|
||||
for (const Token* tok = mTokenizer.list.front(); tok; tok = tok->next()) {
|
||||
if (tok->varId() > maximumVarId)
|
||||
maximumVarId = tok->varId();
|
||||
}
|
||||
nonneg int id = base + 1;
|
||||
nonneg int id = maximumVarId + 1;
|
||||
// Find incomplete vars that are used in constant context
|
||||
std::unordered_map<std::string, nonneg int> unknownConstantIds;
|
||||
const Token* inConstExpr = nullptr;
|
||||
|
@ -1611,7 +1696,6 @@ void SymbolDatabase::createSymbolDatabaseExprIds()
|
|||
});
|
||||
|
||||
for (const Scope * scope : exprScopes) {
|
||||
nonneg int thisId = 0;
|
||||
std::unordered_map<std::string, std::vector<Token*>> exprs;
|
||||
|
||||
std::unordered_map<std::string, nonneg int> unknownIds;
|
||||
|
@ -1638,62 +1722,65 @@ void SymbolDatabase::createSymbolDatabaseExprIds()
|
|||
}
|
||||
|
||||
// Assign IDs
|
||||
ExprIdMap exprIdMap;
|
||||
std::map<std::string, nonneg int> baseIds;
|
||||
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
||||
if (tok->varId() > 0) {
|
||||
tok->exprId(tok->varId());
|
||||
} else if (isExpression(tok)) {
|
||||
exprs[tok->str()].push_back(tok);
|
||||
tok->exprId(id++);
|
||||
if (tok->astParent() && tok->astParent()->exprId() == 0)
|
||||
setParentExprId(tok, mTokenizer.isCPP(), exprIdMap, id);
|
||||
} else if (tok->astParent() && !tok->astOperand1() && !tok->astOperand2()) {
|
||||
if (tok->tokType() == Token::Type::eBracket)
|
||||
continue;
|
||||
if (tok->astParent()->isAssignmentOp())
|
||||
continue;
|
||||
if (tok->isControlFlowKeyword())
|
||||
continue;
|
||||
|
||||
if (id == std::numeric_limits<nonneg int>::max() / 4) {
|
||||
throw InternalError(nullptr, "Ran out of expression ids.", InternalError::INTERNAL);
|
||||
}
|
||||
} else if (isCPP() && Token::simpleMatch(tok, "this")) {
|
||||
if (thisId == 0)
|
||||
thisId = id++;
|
||||
tok->exprId(thisId);
|
||||
if (Token::Match(tok, "%name% <") && tok->next()->link()) {
|
||||
tok->exprId(id);
|
||||
++id;
|
||||
} else {
|
||||
const auto it = baseIds.find(tok->str());
|
||||
if (it != baseIds.end()) {
|
||||
tok->exprId(it->second);
|
||||
} else {
|
||||
baseIds[tok->str()] = id;
|
||||
tok->exprId(id);
|
||||
++id;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply CSE
|
||||
for (const auto& p:exprs) {
|
||||
const std::vector<Token*>& tokens = p.second;
|
||||
const std::size_t N = tokens.size();
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
Token* const tok1 = tokens[i];
|
||||
for (std::size_t j = i + 1; j < N; ++j) {
|
||||
Token* const tok2 = tokens[j];
|
||||
if (tok1->exprId() == tok2->exprId())
|
||||
continue;
|
||||
if (!isSameExpression(isCPP(), true, tok1, tok2, mSettings.library, false, false))
|
||||
continue;
|
||||
nonneg int const cid = std::min(tok1->exprId(), tok2->exprId());
|
||||
tok1->exprId(cid);
|
||||
tok2->exprId(cid);
|
||||
setParentExprId(tok, mTokenizer.isCPP(), exprIdMap, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mark expressions that are unique
|
||||
std::unordered_map<nonneg int, Token*> exprMap;
|
||||
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
||||
if (tok->exprId() == 0)
|
||||
continue;
|
||||
auto p = exprMap.emplace(tok->exprId(), tok);
|
||||
// Already exists so set it to null
|
||||
if (!p.second) {
|
||||
p.first->second = nullptr;
|
||||
if (tok->varId() == 0 && tok->exprId() > 0 && tok->astParent() && !tok->astOperand1() && !tok->astOperand2()) {
|
||||
if (tok->isNumber() || tok->isKeyword() || Token::Match(tok->astParent(), ".|::") ||
|
||||
(Token::simpleMatch(tok->astParent(), "(") && precedes(tok, tok->astParent())))
|
||||
tok->exprId(0);
|
||||
}
|
||||
}
|
||||
for (const auto& p : exprMap) {
|
||||
if (!p.second)
|
||||
}
|
||||
|
||||
// Mark expressions that are unique
|
||||
std::vector<std::pair<Token*, int>> uniqueExprId(id);
|
||||
for (Token* tok = const_cast<Token*>(mTokenizer.list.front()); tok; tok = tok->next()) {
|
||||
const auto id2 = tok->exprId();
|
||||
if (id2 == 0 || id2 <= maximumVarId)
|
||||
continue;
|
||||
if (p.second->variable()) {
|
||||
const Variable* var = p.second->variable();
|
||||
if (var->nameToken() != p.second)
|
||||
uniqueExprId[id2].first = tok;
|
||||
uniqueExprId[id2].second++;
|
||||
}
|
||||
for (const auto& p : uniqueExprId) {
|
||||
if (!p.first || p.second != 1)
|
||||
continue;
|
||||
if (p.first->variable()) {
|
||||
const Variable* var = p.first->variable();
|
||||
if (var->nameToken() != p.first)
|
||||
continue;
|
||||
}
|
||||
p.second->setUniqueExprId();
|
||||
}
|
||||
p.first->setUniqueExprId();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1269,6 +1269,9 @@ std::string Token::stringify(const stringifyOptions& options) const
|
|||
} else if (options.exprid && mImpl->mExprId != 0) {
|
||||
ret += '@';
|
||||
ret += (options.idtype ? "expr" : "");
|
||||
if ((mImpl->mExprId & (1U << efIsUnique)) != 0)
|
||||
ret += "UNIQUE";
|
||||
else
|
||||
ret += std::to_string(mImpl->mExprId);
|
||||
}
|
||||
|
||||
|
|
|
@ -907,11 +907,8 @@ public:
|
|||
|
||||
bool isUniqueExprId() const
|
||||
{
|
||||
if (mImpl->mExprId > 0) {
|
||||
return (mImpl->mExprId & (1 << efIsUnique)) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging purposes, prints token and all tokens
|
||||
|
|
|
@ -153,115 +153,6 @@ def test_progress_j(tmpdir):
|
|||
assert stderr == ""
|
||||
|
||||
|
||||
@pytest.mark.timeout(10)
|
||||
def test_slow_array_many_floats(tmpdir):
|
||||
# 11649
|
||||
# cppcheck valueflow takes a long time when an array has many floats
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("const float f[] = {\n")
|
||||
for i in range(20000):
|
||||
f.write(' 13.6f,\n')
|
||||
f.write("};\n")
|
||||
cppcheck([filename]) # should not take more than ~1 second
|
||||
|
||||
|
||||
@pytest.mark.timeout(10)
|
||||
def test_slow_array_many_strings(tmpdir):
|
||||
# 11901
|
||||
# cppcheck valueflow takes a long time when analyzing a file with many strings
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("const char *strings[] = {\n")
|
||||
for i in range(20000):
|
||||
f.write(' "abc",\n')
|
||||
f.write("};\n")
|
||||
cppcheck([filename]) # should not take more than ~1 second
|
||||
|
||||
|
||||
@pytest.mark.timeout(10)
|
||||
def test_slow_long_line(tmpdir):
|
||||
# simplecpp #314
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("#define A() static const int a[] = {\\\n")
|
||||
for i in range(5000):
|
||||
f.write(" -123, 456, -789,\\\n")
|
||||
f.write("};\n")
|
||||
cppcheck([filename]) # should not take more than ~1 second
|
||||
|
||||
|
||||
@pytest.mark.timeout(60)
|
||||
def test_slow_large_constant_expression(tmpdir):
|
||||
# 12182
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("""
|
||||
#define FLAG1 0
|
||||
#define FLAG2 0
|
||||
#define FLAG3 0
|
||||
#define FLAG4 0
|
||||
#define FLAG5 0
|
||||
#define FLAG6 0
|
||||
#define FLAG7 0
|
||||
#define FLAG8 0
|
||||
#define FLAG9 0
|
||||
#define FLAG10 0
|
||||
#define FLAG11 0
|
||||
#define FLAG12 0
|
||||
#define FLAG13 0
|
||||
#define FLAG14 0
|
||||
#define FLAG15 0
|
||||
#define FLAG16 0
|
||||
#define FLAG17 0
|
||||
#define FLAG18 0
|
||||
#define FLAG19 0
|
||||
#define FLAG20 0
|
||||
#define FLAG21 0
|
||||
#define FLAG22 0
|
||||
#define FLAG23 0
|
||||
#define FLAG24 0
|
||||
|
||||
#define maxval(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
#define E_SAMPLE_SIZE maxval( FLAG1, \
|
||||
maxval( FLAG2, \
|
||||
maxval( FLAG3, \
|
||||
maxval( FLAG4, \
|
||||
maxval( FLAG5, \
|
||||
maxval( FLAG6, \
|
||||
maxval( FLAG7, \
|
||||
maxval( FLAG8, \
|
||||
maxval( FLAG9, \
|
||||
maxval( FLAG10, \
|
||||
maxval( FLAG11, \
|
||||
maxval( FLAG12, \
|
||||
maxval( FLAG13, \
|
||||
maxval( FLAG14, \
|
||||
FLAG15 ))))))))))))))
|
||||
|
||||
#define SAMPLE_SIZE maxval( E_SAMPLE_SIZE, \
|
||||
maxval( sizeof(st), \
|
||||
maxval( FLAG16, \
|
||||
maxval( FLAG17, \
|
||||
maxval( FLAG18, \
|
||||
maxval( FLAG19, \
|
||||
maxval( FLAG20, \
|
||||
maxval( FLAG21, \
|
||||
maxval( FLAG22, \
|
||||
maxval( FLAG23, \
|
||||
FLAG24 ))))))))))
|
||||
|
||||
typedef struct {
|
||||
int n;
|
||||
} st;
|
||||
|
||||
x = SAMPLE_SIZE;
|
||||
""")
|
||||
|
||||
cppcheck([filename])
|
||||
|
||||
|
||||
def test_execute_addon_failure(tmpdir):
|
||||
test_file = os.path.join(tmpdir, 'test.cpp')
|
||||
with open(test_file, 'wt') as f:
|
||||
|
@ -852,7 +743,7 @@ inline void f2()
|
|||
##file {}
|
||||
2: void f2 ( )
|
||||
3: {{
|
||||
4: int i@var1 ; i@var1 =@expr1073741828 0 ;
|
||||
4: int i@var1 ; i@var1 = 0 ;
|
||||
5: }}
|
||||
|
||||
##file {}
|
||||
|
@ -861,7 +752,7 @@ inline void f2()
|
|||
2:
|
||||
3: void f1 ( )
|
||||
4: {{
|
||||
5: int i@var2 ; i@var2 =@expr1073741829 0 ;
|
||||
5: int i@var2 ; i@var2 = 0 ;
|
||||
6: }}
|
||||
|
||||
##file {}
|
||||
|
@ -871,7 +762,7 @@ inline void f2()
|
|||
3:
|
||||
4: void f ( )
|
||||
5: {{
|
||||
6: int i@var3 ; i@var3 =@expr1073741830 0 ;
|
||||
6: int i@var3 ; i@var3 = 0 ;
|
||||
7: }}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
# python -m pytest test-other.py
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
from testutils import cppcheck, assert_cppcheck
|
||||
|
||||
|
||||
|
||||
@pytest.mark.timeout(10)
|
||||
def test_slow_array_many_floats(tmpdir):
|
||||
# 11649
|
||||
# cppcheck valueflow takes a long time when an array has many floats
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("const float f[] = {\n")
|
||||
for i in range(20000):
|
||||
f.write(' 13.6f,\n')
|
||||
f.write("};\n")
|
||||
cppcheck([filename]) # should not take more than ~1 second
|
||||
|
||||
|
||||
@pytest.mark.timeout(10)
|
||||
def test_slow_array_many_strings(tmpdir):
|
||||
# 11901
|
||||
# cppcheck valueflow takes a long time when analyzing a file with many strings
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("const char *strings[] = {\n")
|
||||
for i in range(20000):
|
||||
f.write(' "abc",\n')
|
||||
f.write("};\n")
|
||||
cppcheck([filename]) # should not take more than ~1 second
|
||||
|
||||
|
||||
@pytest.mark.timeout(10)
|
||||
def test_slow_long_line(tmpdir):
|
||||
# simplecpp #314
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("#define A() static const int a[] = {\\\n")
|
||||
for i in range(5000):
|
||||
f.write(" -123, 456, -789,\\\n")
|
||||
f.write("};\n")
|
||||
cppcheck([filename]) # should not take more than ~1 second
|
||||
|
||||
|
||||
@pytest.mark.timeout(60)
|
||||
def test_slow_large_constant_expression(tmpdir):
|
||||
# 12182
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("""
|
||||
#define FLAG1 0
|
||||
#define FLAG2 0
|
||||
#define FLAG3 0
|
||||
#define FLAG4 0
|
||||
#define FLAG5 0
|
||||
#define FLAG6 0
|
||||
#define FLAG7 0
|
||||
#define FLAG8 0
|
||||
#define FLAG9 0
|
||||
#define FLAG10 0
|
||||
#define FLAG11 0
|
||||
#define FLAG12 0
|
||||
#define FLAG13 0
|
||||
#define FLAG14 0
|
||||
#define FLAG15 0
|
||||
#define FLAG16 0
|
||||
#define FLAG17 0
|
||||
#define FLAG18 0
|
||||
#define FLAG19 0
|
||||
#define FLAG20 0
|
||||
#define FLAG21 0
|
||||
#define FLAG22 0
|
||||
#define FLAG23 0
|
||||
#define FLAG24 0
|
||||
|
||||
#define maxval(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
#define E_SAMPLE_SIZE maxval( FLAG1, \
|
||||
maxval( FLAG2, \
|
||||
maxval( FLAG3, \
|
||||
maxval( FLAG4, \
|
||||
maxval( FLAG5, \
|
||||
maxval( FLAG6, \
|
||||
maxval( FLAG7, \
|
||||
maxval( FLAG8, \
|
||||
maxval( FLAG9, \
|
||||
maxval( FLAG10, \
|
||||
maxval( FLAG11, \
|
||||
maxval( FLAG12, \
|
||||
maxval( FLAG13, \
|
||||
maxval( FLAG14, \
|
||||
FLAG15 ))))))))))))))
|
||||
|
||||
#define SAMPLE_SIZE maxval( E_SAMPLE_SIZE, \
|
||||
maxval( sizeof(st), \
|
||||
maxval( FLAG16, \
|
||||
maxval( FLAG17, \
|
||||
maxval( FLAG18, \
|
||||
maxval( FLAG19, \
|
||||
maxval( FLAG20, \
|
||||
maxval( FLAG21, \
|
||||
maxval( FLAG22, \
|
||||
maxval( FLAG23, \
|
||||
FLAG24 ))))))))))
|
||||
|
||||
typedef struct {
|
||||
int n;
|
||||
} st;
|
||||
|
||||
x = SAMPLE_SIZE;
|
||||
""")
|
||||
|
||||
cppcheck([filename])
|
||||
|
||||
@pytest.mark.timeout(10)
|
||||
def test_slow_exprid(tmpdir):
|
||||
# 11885
|
||||
filename = os.path.join(tmpdir, 'hang.c')
|
||||
with open(filename, 'wt') as f:
|
||||
f.write("""
|
||||
int foo(int a, int b)
|
||||
{
|
||||
#define A0(a, b) ((a) + (b))
|
||||
#define A1(a, b) ((a) > (b)) ? A0((a) - (b), (b)) : A0((b) - (a), (a))
|
||||
#define A2(a, b) ((a) > (b)) ? A1((a) - (b), (b)) : A1((b) - (a), (a))
|
||||
#define A3(a, b) ((a) > (b)) ? A2((a) - (b), (b)) : A2((b) - (a), (a))
|
||||
#define A4(a, b) ((a) > (b)) ? A3((a) - (b), (b)) : A3((b) - (a), (a))
|
||||
#define A5(a, b) ((a) > (b)) ? A4((a) - (b), (b)) : A4((b) - (a), (a))
|
||||
#define A6(a, b) ((a) > (b)) ? A5((a) - (b), (b)) : A5((b) - (a), (a))
|
||||
#define A7(a, b) ((a) > (b)) ? A6((a) - (b), (b)) : A6((b) - (a), (a))
|
||||
#define A8(a, b) ((a) > (b)) ? A7((a) - (b), (b)) : A7((b) - (a), (a))
|
||||
#define A9(a, b) ((a) > (b)) ? A8((a) - (b), (b)) : A8((b) - (a), (a))
|
||||
#define A10(a, b) ((a) > (b)) ? A9((a) - (b), (b)) : A9((b) - (a), (a))
|
||||
#define A11(a, b) ((a) > (b)) ? A10((a) - (b), (b)) : A10((b) - (a), (a))
|
||||
return A8(a, b);
|
||||
}
|
||||
""")
|
||||
|
||||
my_env = os.environ.copy()
|
||||
my_env["DISABLE_VALUEFLOW"] = "1"
|
||||
cppcheck([filename], env=my_env)
|
||||
|
||||
|
||||
|
|
@ -990,7 +990,7 @@ private:
|
|||
|
||||
check("struct S { struct T { char c; } *p; };\n" // #6541
|
||||
"char f(S* s) { return s->p ? 'a' : s->p->c; }\n");
|
||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Either the condition 's->p' is redundant or there is possible null pointer dereference: p.\n",
|
||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Either the condition 's->p' is redundant or there is possible null pointer dereference: s->p.\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include "errortypes.h"
|
||||
#include "helpers.h"
|
||||
#include "platform.h"
|
||||
#include "settings.h"
|
||||
#include "standards.h"
|
||||
|
@ -236,6 +237,11 @@ private:
|
|||
|
||||
TEST_CASE(exprid1);
|
||||
TEST_CASE(exprid2);
|
||||
TEST_CASE(exprid3);
|
||||
TEST_CASE(exprid4);
|
||||
TEST_CASE(exprid5);
|
||||
TEST_CASE(exprid6);
|
||||
TEST_CASE(exprid7);
|
||||
|
||||
TEST_CASE(structuredBindings);
|
||||
}
|
||||
|
@ -260,9 +266,11 @@ private:
|
|||
std::string tokenizeExpr_(const char* file, int line, const char code[], const char filename[] = "test.cpp") {
|
||||
errout.str("");
|
||||
|
||||
std::vector<std::string> files(1, filename);
|
||||
Tokenizer tokenizer(&settings, this);
|
||||
std::istringstream istr(code);
|
||||
ASSERT_LOC((tokenizer.tokenize)(istr, filename), file, line);
|
||||
PreprocessorHelper::preprocess(code, files, tokenizer);
|
||||
|
||||
ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line);
|
||||
|
||||
// result..
|
||||
Token::stringifyOptions options = Token::stringifyOptions::forDebugExprId();
|
||||
|
@ -3859,9 +3867,9 @@ private:
|
|||
"2: int x ; int y ;\n"
|
||||
"3: } ;\n"
|
||||
"4: int f ( A a , A b ) {\n"
|
||||
"5: int x@5 ; x@5 =@9 a@3 .@10 x@6 +@11 b@4 .@12 x@7 ;\n"
|
||||
"6: int y@8 ; y@8 =@1073741837 b@4 .@12 x@7 +@11 a@3 .@10 x@6 ;\n"
|
||||
"7: return x@5 +@1073741841 y@8 +@1073741842 a@3 .@1073741843 y@9 +@1073741844 b@4 .@1073741845 y@10 ;\n"
|
||||
"5: int x@5 ; x@5 =@UNIQUE a@3 .@11 x@6 +@13 b@4 .@12 x@7 ;\n"
|
||||
"6: int y@8 ; y@8 =@UNIQUE b@4 .@12 x@7 +@13 a@3 .@11 x@6 ;\n"
|
||||
"7: return x@5 +@UNIQUE y@8 +@UNIQUE a@3 .@UNIQUE y@9 +@UNIQUE b@4 .@UNIQUE y@10 ;\n"
|
||||
"8: }\n";
|
||||
|
||||
ASSERT_EQUALS(expected, actual);
|
||||
|
@ -3878,14 +3886,77 @@ private:
|
|||
|
||||
const char expected[] = "1: struct S { std :: unique_ptr < int > u ; } ;\n"
|
||||
"2: auto f ; f = [ ] ( const S & s ) . std :: unique_ptr < int > {\n"
|
||||
"3: if (@5 auto p@4 =@1073741830 s@3 .@1073741831 u@5 .@1073741832 get (@1073741833 ) ) {\n"
|
||||
"4: return std ::@1073741834 make_unique < int > (@1073741835 *@1073741836 p@4 ) ; }\n"
|
||||
"3: if ( auto p@4 =@UNIQUE s@3 .@UNIQUE u@5 .@UNIQUE get (@UNIQUE ) ) {\n"
|
||||
"4: return std ::@UNIQUE make_unique < int > (@UNIQUE *@UNIQUE p@4 ) ; }\n"
|
||||
"5: return nullptr ;\n"
|
||||
"6: } ;\n";
|
||||
|
||||
ASSERT_EQUALS(expected, actual);
|
||||
}
|
||||
|
||||
void exprid3() {
|
||||
const char code[] = "void f(bool b, int y) {\n"
|
||||
" if (b && y > 0) {}\n"
|
||||
" while (b && y > 0) {}\n"
|
||||
"}\n";
|
||||
const char expected[] = "1: void f ( bool b , int y ) {\n"
|
||||
"2: if ( b@1 &&@5 y@2 >@4 0 ) { }\n"
|
||||
"3: while ( b@1 &&@5 y@2 >@4 0 ) { }\n"
|
||||
"4: }\n";
|
||||
ASSERT_EQUALS(expected, tokenizeExpr(code));
|
||||
}
|
||||
|
||||
void exprid4() {
|
||||
// expanded macro..
|
||||
const char code[] = "#define ADD(x,y) x+y\n"
|
||||
"int f(int a, int b) {\n"
|
||||
" return ADD(a,b) + ADD(a,b);\n"
|
||||
"}\n";
|
||||
const char expected[] = "2: int f ( int a , int b ) {\n"
|
||||
"3: return a@1 $+@UNIQUE b@2 +@UNIQUE a@1 $+@UNIQUE b@2 ;\n"
|
||||
"4: }\n";
|
||||
ASSERT_EQUALS(expected, tokenizeExpr(code));
|
||||
}
|
||||
|
||||
void exprid5() {
|
||||
// references..
|
||||
const char code[] = "int foo(int a) {\n"
|
||||
" int& r = a;\n"
|
||||
" return (a+a)*(r+r);\n"
|
||||
"}\n";
|
||||
const char expected[] = "1: int foo ( int a ) {\n"
|
||||
"2: int & r@2 =@UNIQUE a@1 ;\n"
|
||||
"3: return ( a@1 +@4 a@1 ) *@UNIQUE ( r@2 +@4 r@2 ) ;\n"
|
||||
"4: }\n";
|
||||
ASSERT_EQUALS(expected, tokenizeExpr(code));
|
||||
}
|
||||
|
||||
void exprid6() {
|
||||
// ++ and -- should have UNIQUE exprid
|
||||
const char code[] = "void foo(int *a) {\n"
|
||||
" *a++ = 0;\n"
|
||||
" if (*a++ == 32) {}\n"
|
||||
"}\n";
|
||||
const char expected[] = "1: void foo ( int * a ) {\n"
|
||||
"2: *@UNIQUE a@1 ++@UNIQUE = 0 ;\n"
|
||||
"3: if ( *@UNIQUE a@1 ++@UNIQUE ==@UNIQUE 32 ) { }\n"
|
||||
"4: }\n";
|
||||
ASSERT_EQUALS(expected, tokenizeExpr(code));
|
||||
}
|
||||
|
||||
void exprid7() {
|
||||
// different casts
|
||||
const char code[] = "void foo(int a) {\n"
|
||||
" if ((char)a == (short)a) {}\n"
|
||||
" if ((char)a == (short)a) {}\n"
|
||||
"}\n";
|
||||
const char expected[] = "1: void foo ( int a ) {\n"
|
||||
"2: if ( (@2 char ) a@1 ==@4 (@3 short ) a@1 ) { }\n"
|
||||
"3: if ( (@2 char ) a@1 ==@4 (@3 short ) a@1 ) { }\n"
|
||||
"4: }\n";
|
||||
ASSERT_EQUALS(expected, tokenizeExpr(code));
|
||||
}
|
||||
|
||||
void structuredBindings() {
|
||||
const char code[] = "int foo() { auto [x,y] = xy(); return x+y; }";
|
||||
ASSERT_EQUALS("1: int foo ( ) { auto [ x@1 , y@2 ] = xy ( ) ; return x@1 + y@2 ; }\n",
|
||||
|
|
Loading…
Reference in New Issue