Add backtrace to valueflow in debug mode (#4195)

This commit is contained in:
Paul Fultz II 2022-06-12 00:13:42 -05:00 committed by GitHub
parent 187460a277
commit cee48e5e19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 45 deletions

View File

@ -54,7 +54,7 @@ jobs:
- name: Self check (unusedFunction)
if: false # TODO: fails with preprocessorErrorDirective - see #10667
run: |
./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__GNUC__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --inconclusive --enable=unusedFunction --exception-handling -rp=. --project=cmake.output/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr
./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__CPPCHECK__ -D__GNUC__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --inconclusive --enable=unusedFunction --exception-handling -rp=. --project=cmake.output/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr
env:
DISABLE_VALUEFLOW: 1
@ -76,6 +76,6 @@ jobs:
# TODO: find a way to report unmatched suppressions without need to add information checks
- name: Self check (unusedFunction / no test)
run: |
./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__GNUC__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --inconclusive --enable=unusedFunction --exception-handling -rp=. --project=cmake.output.notest/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr
./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__CPPCHECK__ -D__GNUC__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --inconclusive --enable=unusedFunction --exception-handling -rp=. --project=cmake.output.notest/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr
env:
DISABLE_VALUEFLOW: 1

View File

@ -561,7 +561,7 @@ $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/
$(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/utils.o $(libcppdir)/utils.cpp
$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h
$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/valueflow.o $(libcppdir)/valueflow.cpp
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h

View File

@ -6,7 +6,7 @@ macro(use_cxx11)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif ()
else ()
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to use")
set (CMAKE_CXX_STANDARD_REQUIRED ON)
if (POLICY CMP0025)
cmake_policy(SET CMP0025 NEW)

71
lib/sourcelocation.h Normal file
View File

@ -0,0 +1,71 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2022 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef sourcelocationH
#define sourcelocationH
#ifdef __CPPCHECK__
#define CPPCHECK_HAS_SOURCE_LOCATION 0
#define CPPCHECK_HAS_SOURCE_LOCATION_TS 0
#elif defined(__has_include)
#if __has_include(<source_location>) && __cplusplus >= 202003L
#define CPPCHECK_HAS_SOURCE_LOCATION 1
#else
#define CPPCHECK_HAS_SOURCE_LOCATION 0
#endif
#if __has_include(<experimental/source_location>) && __cplusplus >= 201402L
#define CPPCHECK_HAS_SOURCE_LOCATION_TS 1
#else
#define CPPCHECK_HAS_SOURCE_LOCATION_TS 0
#endif
#else
#define CPPCHECK_HAS_SOURCE_LOCATION 0
#define CPPCHECK_HAS_SOURCE_LOCATION_TS 0
#endif
#if CPPCHECK_HAS_SOURCE_LOCATION
#include <source_location>
using SourceLocation = std::source_location;
#elif CPPCHECK_HAS_SOURCE_LOCATION_TS
#include <experimental/source_location>
using SourceLocation = std::experimental::source_location;
#else
struct SourceLocation {
static SourceLocation current() {
return SourceLocation();
}
std::uint_least32_t m_line = 0;
std::uint_least32_t m_column = 0;
const char* m_file_name = "";
const char* m_function_name = "";
std::uint_least32_t line() const {
return m_line;
}
std::uint_least32_t column() const {
return m_column;
}
const char* file_name() const {
return m_file_name;
}
const char* function_name() const {
return m_function_name;
}
};
#endif
#endif

View File

@ -93,6 +93,7 @@
#include "programmemory.h"
#include "reverseanalyzer.h"
#include "settings.h"
#include "sourcelocation.h"
#include "standards.h"
#include "symboldatabase.h"
#include "token.h"
@ -137,6 +138,38 @@ static void bailoutInternal(const std::string& type, TokenList *tokenlist, Error
#define bailoutIncompleteVar(tokenlist, errorLogger, tok, what) bailout2("valueFlowBailoutIncompleteVar", tokenlist, errorLogger, tok, what)
static std::string debugString(const ValueFlow::Value& v)
{
std::string kind;
switch (v.valueKind) {
case ValueFlow::Value::ValueKind::Impossible:
case ValueFlow::Value::ValueKind::Known:
kind = "always";
break;
case ValueFlow::Value::ValueKind::Inconclusive:
kind = "inconclusive";
break;
case ValueFlow::Value::ValueKind::Possible:
kind = "possible";
break;
}
return kind + " " + v.toString();
}
static void setSourceLocation(ValueFlow::Value& v,
SourceLocation ctx,
const Token* tok,
SourceLocation local = SourceLocation::current())
{
std::string file = ctx.file_name();
if (file.empty())
return;
std::string s = Path::stripDirectoryPart(file) + ":" + MathLib::toString(ctx.line()) + ": " + ctx.function_name() +
" => " + local.function_name() + ": " + debugString(v);
v.debugPath.emplace_back(tok, s);
}
static void changeKnownToPossible(std::list<ValueFlow::Value> &values, int indirect=-1)
{
for (ValueFlow::Value& v: values) {
@ -619,7 +652,10 @@ static ValueFlow::Value truncateImplicitConversion(Token* parent, const ValueFlo
}
/** set ValueFlow value and perform calculations if possible */
static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* settings)
static void setTokenValue(Token* tok,
ValueFlow::Value value,
const Settings* settings,
SourceLocation loc = SourceLocation::current())
{
// Skip setting values that are too big since its ambiguous
if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) &&
@ -629,6 +665,9 @@ static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* se
if (!value.isImpossible() && value.isIntValue())
value = truncateImplicitConversion(tok->astParent(), value, settings);
if (settings->debugnormal)
setSourceLocation(value, loc, tok);
if (!tok->addValue(value))
return;
@ -1907,43 +1946,58 @@ ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, const ValueFlow::Va
static Analyzer::Result valueFlowForward(Token* startToken,
const Token* endToken,
const Token* exprTok,
const ValueFlow::Value& value,
TokenList* const tokenlist)
ValueFlow::Value value,
TokenList* const tokenlist,
SourceLocation loc = SourceLocation::current())
{
return valueFlowGenericForward(startToken, endToken, makeAnalyzer(exprTok, value, tokenlist), tokenlist->getSettings());
if (tokenlist->getSettings()->debugnormal)
setSourceLocation(value, loc, startToken);
return valueFlowGenericForward(startToken,
endToken,
makeAnalyzer(exprTok, std::move(value), tokenlist),
tokenlist->getSettings());
}
static Analyzer::Result valueFlowForward(Token* startToken,
const Token* endToken,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* const tokenlist)
std::list<ValueFlow::Value> values,
TokenList* const tokenlist,
SourceLocation loc = SourceLocation::current())
{
Analyzer::Result result{};
for (const ValueFlow::Value& v : values) {
result.update(valueFlowForward(startToken, endToken, exprTok, v, tokenlist));
for (ValueFlow::Value& v : values) {
result.update(valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, loc));
}
return result;
}
template<class ValueOrValues>
static Analyzer::Result valueFlowForward(Token* startToken, const Token* exprTok, const ValueOrValues& v, TokenList* tokenlist)
static Analyzer::Result valueFlowForward(Token* startToken,
const Token* exprTok,
ValueOrValues v,
TokenList* tokenlist,
SourceLocation loc = SourceLocation::current())
{
const Token* endToken = nullptr;
const Function* f = Scope::nestedInFunction(startToken->scope());
if (f && f->functionScope)
endToken = f->functionScope->bodyEnd;
return valueFlowForward(startToken, endToken, exprTok, v, tokenlist);
return valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, loc);
}
static Analyzer::Result valueFlowForwardRecursive(Token* top,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* const tokenlist)
std::list<ValueFlow::Value> values,
TokenList* const tokenlist,
SourceLocation loc = SourceLocation::current())
{
Analyzer::Result result{};
for (const ValueFlow::Value& v : values) {
result.update(valueFlowGenericForward(top, makeAnalyzer(exprTok, v, tokenlist), tokenlist->getSettings()));
for (ValueFlow::Value& v : values) {
if (tokenlist->getSettings()->debugnormal)
setSourceLocation(v, loc, top);
result.update(
valueFlowGenericForward(top, makeAnalyzer(exprTok, std::move(v), tokenlist), tokenlist->getSettings()));
}
return result;
}
@ -1951,10 +2005,13 @@ static Analyzer::Result valueFlowForwardRecursive(Token* top,
static void valueFlowReverse(Token* tok,
const Token* const endToken,
const Token* const varToken,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist)
std::list<ValueFlow::Value> values,
TokenList* tokenlist,
SourceLocation loc = SourceLocation::current())
{
for (const ValueFlow::Value& v : values) {
for (ValueFlow::Value& v : values) {
if (tokenlist->getSettings()->debugnormal)
setSourceLocation(v, loc, tok);
valueFlowGenericReverse(tok, endToken, makeReverseAnalyzer(varToken, v, tokenlist), tokenlist->getSettings());
}
}
@ -1966,12 +2023,13 @@ static void valueFlowReverse(TokenList* tokenlist,
ValueFlow::Value val,
const ValueFlow::Value& val2,
ErrorLogger* /*errorLogger*/,
const Settings* = nullptr)
const Settings* = nullptr,
SourceLocation loc = SourceLocation::current())
{
std::list<ValueFlow::Value> values = {val};
if (val2.varId != 0)
values.push_back(val2);
valueFlowReverse(tok, nullptr, varToken, values, tokenlist);
valueFlowReverse(tok, nullptr, varToken, values, tokenlist, loc);
}
static bool isConditionKnown(const Token* tok, bool then)
@ -4740,7 +4798,11 @@ static const Token* findIncompleteVar(const Token* start, const Token* end)
return nullptr;
}
static ValueFlow::Value makeConditionValue(long long val, const Token* condTok, bool assume)
static ValueFlow::Value makeConditionValue(long long val,
const Token* condTok,
bool assume,
const Settings* settings = nullptr,
SourceLocation loc = SourceLocation::current())
{
ValueFlow::Value v(val);
v.setKnown();
@ -4749,6 +4811,8 @@ static ValueFlow::Value makeConditionValue(long long val, const Token* condTok,
v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is true");
else
v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is false");
if (settings && settings->debugnormal)
setSourceLocation(v, loc, condTok);
return v;
}
@ -5559,26 +5623,29 @@ struct ConditionHandler {
const Token* stop,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist) const
TokenList* tokenlist,
SourceLocation loc = SourceLocation::current()) const
{
return valueFlowForward(start->next(), stop, exprTok, values, tokenlist);
return valueFlowForward(start->next(), stop, exprTok, values, tokenlist, loc);
}
virtual Analyzer::Result forward(Token* top,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist) const
TokenList* tokenlist,
SourceLocation loc = SourceLocation::current()) const
{
return valueFlowForwardRecursive(top, exprTok, values, tokenlist);
return valueFlowForwardRecursive(top, exprTok, values, tokenlist, loc);
}
virtual void reverse(Token* start,
const Token* endToken,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist) const
TokenList* tokenlist,
SourceLocation loc = SourceLocation::current()) const
{
return valueFlowReverse(start, endToken, exprTok, values, tokenlist);
return valueFlowReverse(start, endToken, exprTok, values, tokenlist, loc);
}
void traverseCondition(TokenList* tokenlist,
@ -8353,23 +8420,12 @@ static void valueFlowDebug(TokenList* tokenlist, ErrorLogger* errorLogger)
for (Token* tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->getTokenDebug() != TokenDebug::ValueFlow)
continue;
if (tok->astParent() && tok->astParent()->getTokenDebug() == TokenDebug::ValueFlow)
continue;
for (const ValueFlow::Value& v : tok->values()) {
std::string kind;
switch (v.valueKind) {
case ValueFlow::Value::ValueKind::Impossible:
case ValueFlow::Value::ValueKind::Known:
kind = "always";
break;
case ValueFlow::Value::ValueKind::Inconclusive:
kind = "inconclusive";
break;
case ValueFlow::Value::ValueKind::Possible:
kind = "possible";
break;
}
std::string msg = "The value is " + kind + " " + v.toString();
std::string msg = "The value is " + debugString(v);
ErrorPath errorPath = v.errorPath;
errorPath.insert(errorPath.end(), v.debugPath.begin(), v.debugPath.end());
errorPath.emplace_back(tok, "");
errorLogger->reportErr({errorPath, tokenlist, Severity::debug, "valueFlow", msg, CWE{0}, Certainty::normal});
}

View File

@ -342,6 +342,8 @@ namespace ValueFlow {
ErrorPath errorPath;
ErrorPath debugPath;
/** For calculated values - varId that calculated value depends on */
nonneg int varId;