CI: Add selfcheck using Cppcheck Premium. Activates Misra C++ 2008 and Cert C++ 2016 checkers. (#5623)

This commit is contained in:
Daniel Marjamäki 2023-11-06 15:31:47 +01:00 committed by GitHub
parent 83ac6bfa0f
commit fc8c244675
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 268 additions and 30 deletions

43
.github/workflows/cppcheck-premium.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: cppcheck-premium
on:
push:
branches:
- 'main'
- 'releases/**'
tags:
- '2.*'
pull_request:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-22.04 # run on the latest image only
env:
PREMIUM_VERSION: devdrop-20231105
steps:
- uses: actions/checkout@v3
- name: Download cppcheckpremium
run: |
wget https://files.cppchecksolutions.com/cppcheckpremium-${{ env.PREMIUM_VERSION }}-amd64.tar.gz
tar xzf cppcheckpremium-${{ env.PREMIUM_VERSION }}-amd64.tar.gz
- name: Generate a license file
run: |
echo cppcheck > cppcheck.lic
echo 231231 >> cppcheck.lic
echo 80000 >> cppcheck.lic
echo 57e08c39523ab54d >> cppcheck.lic
echo path:lib >> cppcheck.lic
- name: Check
run: |
cppcheckpremium-${{ env.PREMIUM_VERSION }}/premiumaddon --check-loc-license cppcheck.lic > cppcheck-premium-loc
cppcheckpremium-${{ env.PREMIUM_VERSION }}/cppcheck -j$(nproc) -D__GNUC__ -D__CPPCHECK__ --suppressions-list=cppcheckpremium-suppressions --platform=unix64 --enable=style --premium=misra-c++-2008 --premium=cert-c++-2016 --error-exitcode=1 lib

View File

@ -0,0 +1,186 @@
# False positives
premium-misra-cpp-2008-5-17-1
premium-misra-cpp-2008-5-0-6
premium-misra-cpp-2008-7-2-1
premium-misra-cpp-2008-3-3-2
# open source warnings are handled by the selfcheck.yml
noExplicitConstructor
postfixOperator
shadowFunction
useStlAlgorithm
# we need to declare reserved identifier _CRTDBG_MAP_ALLOC
premium-cert-dcl51-cpp
# TODO: Is there unsafe allocations, in case of exceptions) in cppcheck
premium-cert-err58-cpp
# we have global objects
premium-cert-err58-cpp
# TODO: Exception objects must be nothrow copy constructible.
premium-cert-err60-cpp
# TODO should we throw Token?
premium-cert-err61-cpp
# TODO: Detect errors when converting a string to a number. The library function 'atoi()' shall not be used.
premium-cert-err62-cpp
# TODO: Can we reduce some const_cast?
premium-cert-exp55-cpp
# sometimes a void function does not have side effects
premium-misra-cpp-2008-0-1-8
# unused arguments, misra rules are too strict
premium-misra-cpp-2008-0-1-11
premium-misra-cpp-2008-0-1-12
# we sometimes don't care about return value from functions
premium-misra-cpp-2008-0-1-7
# TODO: can we prevent commented out code?
premium-misra-cpp-2008-2-7-2
premium-misra-cpp-2008-2-7-3
# NA
premium-misra-cpp-2008-2-10-1
# objects of a class often has the lowercase name of the class.
premium-misra-cpp-2008-2-10-4
# flag |= ..
premium-misra-cpp-2008-4-5-1
# Token/Variable flags are enum constants and we use those in bitwise operations by intention.
premium-misra-cpp-2008-4-5-2
# intentional addition of char to string: const std::string end(':' + cfg + ':' + Path::simplifyPath(sourcefile));
premium-misra-cpp-2008-4-5-3
# too strict operator precedence warnings
premium-misra-cpp-2008-5-0-2
# we are less strict about signedness. what bug is there here: unsigned int col = 0
premium-misra-cpp-2008-5-0-4
# intentional integral-to-float conversion
premium-misra-cpp-2008-5-0-5
# intentional addition of char literal: c = 'a' + (temp - 10);
premium-misra-cpp-2008-5-0-11
# conversion of char-to-int is intentional sometimes
premium-misra-cpp-2008-5-0-12
# pointer-to-bool conversion is common
premium-misra-cpp-2008-5-0-14
# pointer arithmetic is not uncommon in cppcheck code
premium-misra-cpp-2008-5-0-15
# it's only a problem if signed expression is negative
premium-misra-cpp-2008-5-0-21
# Intentional safe operands of &&: return !stdValue.empty() && str == getCPP();
premium-misra-cpp-2008-5-2-1
# const_cast performs intentional const casting
premium-misra-cpp-2008-5-2-5
# safe code: const char *next = static_cast<const char*>(std::memchr(pattern, ' ', pattern_len));
premium-misra-cpp-2008-5-2-8
# we intentionally cast pointer to integer when creating id for dumpfile
premium-misra-cpp-2008-5-2-9
# we intentionally mix increment with other operators in expressions
premium-misra-cpp-2008-5-2-10
# intentional array-to-pointer decay
premium-misra-cpp-2008-5-2-12
# we write !pointer by intention
premium-misra-cpp-2008-5-3-1
# for (;;)
premium-misra-cpp-2008-6-2-3
# it's not a bug to not put default at the end of a switch body
premium-misra-cpp-2008-6-4-6
# looping linked list => not well formed for loop
premium-misra-cpp-2008-6-5-1
premium-misra-cpp-2008-6-5-2
premium-misra-cpp-2008-6-5-3
premium-misra-cpp-2008-6-5-4
premium-misra-cpp-2008-6-5-5
premium-misra-cpp-2008-6-5-6
# we like early returns
premium-misra-cpp-2008-6-6-3
premium-misra-cpp-2008-6-6-4
premium-misra-cpp-2008-6-6-5
# we have local functions by intention
premium-misra-cpp-2008-7-3-1
# intentional: return reference from method to non-const reference parameter
premium-misra-cpp-2008-7-5-3
# intentional declaration of multiple variables
premium-misra-cpp-2008-8-0-1
# we intentionally don't use & before function names
premium-misra-cpp-2008-8-4-4
# cppcheck does not care about this enumerator rule
premium-misra-cpp-2008-8-5-3
# TODO Fix these
premium-misra-cpp-2008-9-3-1
# we use unions by intention sometimes
premium-misra-cpp-2008-9-5-1
# overridden methods is safe
premium-misra-cpp-2008-10-3-1
# some classes have public members by intention
premium-misra-cpp-2008-11-0-1
# rule should not apply to deleted copy assignment operator
premium-misra-cpp-2008-12-8-2
# TODO: this can be fixed by refactoring the code.
premium-misra-cpp-2008-14-6-2
# function specializations: TODO check if we should refactor
premium-misra-cpp-2008-14-8-2
# we use preprocessor when it makes sense
premium-misra-cpp-2008-16-0-1
premium-misra-cpp-2008-16-2-1
premium-misra-cpp-2008-16-2-2
premium-misra-cpp-2008-16-3-2
# TODO do we need to catch string conversion errors (using atoi)?
premium-misra-cpp-2008-18-0-2
# what standard alternative is there for std::getenv
premium-misra-cpp-2008-18-0-3
# <ctime> is used by intention
premium-misra-cpp-2008-18-0-4
# code is safe. we use std::strcmp by intention
premium-misra-cpp-2008-18-0-5
# we do avoid using new/delete
premium-misra-cpp-2008-18-4-1
# <cstdio> is used by intention
premium-misra-cpp-2008-27-0-1

View File

@ -33,6 +33,8 @@ struct Analyzer {
struct Action { struct Action {
Action() = default; Action() = default;
Action(const Action&) = default;
Action& operator=(const Action& rhs) = default;
template<class T, template<class T,
REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ), REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),

View File

@ -2241,7 +2241,7 @@ bool isScopeBracket(const Token* tok)
} }
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )> template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
T* getTokenArgumentFunctionImpl(T* tok, int& argn) static T* getTokenArgumentFunctionImpl(T* tok, int& argn)
{ {
argn = -1; argn = -1;
{ {
@ -2892,7 +2892,7 @@ const Token* findThisChanged(const Token* start, const Token* end, int indirect,
} }
template<class Find> template<class Find>
const Token* findExpressionChangedImpl(const Token* expr, static const Token* findExpressionChangedImpl(const Token* expr,
const Token* start, const Token* start,
const Token* end, const Token* end,
const Settings* settings, const Settings* settings,
@ -3095,7 +3095,7 @@ const Token *findLambdaStartToken(const Token *last)
} }
template<class T> template<class T>
T* findLambdaEndTokenGeneric(T* first) static T* findLambdaEndTokenGeneric(T* first)
{ {
auto maybeLambda = [](T* tok) -> bool { auto maybeLambda = [](T* tok) -> bool {
while (Token::Match(tok, "*|%name%|::|>")) { while (Token::Match(tok, "*|%name%|::|>")) {

View File

@ -995,7 +995,7 @@ static bool checkFloatRelation(const std::string &op, const double value1, const
} }
template<class T> template<class T>
T getvalue3(const T value1, const T value2) static T getvalue3(const T value1, const T value2)
{ {
const T min = std::min(value1, value2); const T min = std::min(value1, value2);
if (min== std::numeric_limits<T>::max()) if (min== std::numeric_limits<T>::max())

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef checkersH
#define checkersH
#include <map> #include <map>
#include <string> #include <string>
@ -44,4 +45,4 @@ namespace checkers {
extern CPPCHECKLIB const std::map<std::string, std::string> misraRuleSeverity; extern CPPCHECKLIB const std::map<std::string, std::string> misraRuleSeverity;
} }
#endif

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef checkersReportH
#define checkersReportH
#include "config.h" #include "config.h"
@ -44,4 +45,4 @@ private:
int mAllCheckersCount = 0; int mAllCheckersCount = 0;
}; };
#endif

View File

@ -2178,6 +2178,10 @@ Variable& Variable::operator=(const Variable &var)
if (this == &var) if (this == &var)
return *this; return *this;
ValueType* vt = nullptr;
if (var.mValueType)
vt = new ValueType(*var.mValueType);
mNameToken = var.mNameToken; mNameToken = var.mNameToken;
mTypeStartToken = var.mTypeStartToken; mTypeStartToken = var.mTypeStartToken;
mTypeEndToken = var.mTypeEndToken; mTypeEndToken = var.mTypeEndToken;
@ -2188,9 +2192,7 @@ Variable& Variable::operator=(const Variable &var)
mScope = var.mScope; mScope = var.mScope;
mDimensions = var.mDimensions; mDimensions = var.mDimensions;
delete mValueType; delete mValueType;
mValueType = nullptr; mValueType = vt;
if (var.mValueType)
mValueType = new ValueType(*var.mValueType);
return *this; return *this;
} }
@ -2344,9 +2346,9 @@ void Variable::setValueType(const ValueType &valueType)
if (declType && !declType->next()->valueType()) if (declType && !declType->next()->valueType())
return; return;
} }
ValueType* vt = new ValueType(valueType);
delete mValueType; delete mValueType;
mValueType = nullptr; mValueType = vt;
mValueType = new ValueType(valueType);
if ((mValueType->pointer > 0) && (!isArray() || Token::Match(mNameToken->previous(), "( * %name% )"))) if ((mValueType->pointer > 0) && (!isArray() || Token::Match(mNameToken->previous(), "( * %name% )")))
setFlag(fIsPointer, true); setFlag(fIsPointer, true);
setFlag(fIsConst, mValueType->constness & (1U << mValueType->pointer)); setFlag(fIsConst, mValueType->constness & (1U << mValueType->pointer));
@ -6359,7 +6361,7 @@ static void setAutoTokenProperties(Token * const autoTok)
autoTok->isStandardType(true); autoTok->isStandardType(true);
} }
bool isContainerYieldElement(Library::Container::Yield yield) static bool isContainerYieldElement(Library::Container::Yield yield)
{ {
return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX || return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX ||
yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT;

View File

@ -618,7 +618,6 @@ namespace {
std::pair<Token*, Token*> mRangeType; std::pair<Token*, Token*> mRangeType;
std::pair<Token*, Token*> mRangeTypeQualifiers; std::pair<Token*, Token*> mRangeTypeQualifiers;
std::pair<Token*, Token*> mRangeAfterVar; std::pair<Token*, Token*> mRangeAfterVar;
std::string mTypedefName; // Name of typedef type
Token* mNameToken{nullptr}; Token* mNameToken{nullptr};
bool mFail = false; bool mFail = false;
bool mReplaceFailed = false; bool mReplaceFailed = false;
@ -643,7 +642,6 @@ namespace {
if (Token::Match(nameToken, "%name% ;")) { if (Token::Match(nameToken, "%name% ;")) {
mRangeType = rangeBefore; mRangeType = rangeBefore;
mRangeTypeQualifiers = rangeQualifiers; mRangeTypeQualifiers = rangeQualifiers;
mTypedefName = nameToken->str();
Token* typeName = rangeBefore.second->previous(); Token* typeName = rangeBefore.second->previous();
if (typeName->isKeyword()) { if (typeName->isKeyword()) {
(void)num; (void)num;

View File

@ -2237,7 +2237,7 @@ struct SingleRange {
}; };
template<class T> template<class T>
SingleRange<T> MakeSingleRange(T& x) static SingleRange<T> MakeSingleRange(T& x)
{ {
return {&x}; return {&x};
} }
@ -6804,7 +6804,7 @@ ValuePtr<InferModel> ValueFlow::makeIntegralInferModel() {
return IntegralInferModel{}; return IntegralInferModel{};
} }
ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val) static ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val)
{ {
if (!varTok) if (!varTok)
return ValueFlow::Value{}; return ValueFlow::Value{};
@ -7374,7 +7374,7 @@ struct MultiValueFlowAnalyzer : ValueFlowAnalyzer {
}; };
template<class Key, class F> template<class Key, class F>
bool productParams(const Settings* settings, const std::unordered_map<Key, std::list<ValueFlow::Value>>& vars, F f) static bool productParams(const Settings* settings, const std::unordered_map<Key, std::list<ValueFlow::Value>>& vars, F f)
{ {
using Args = std::vector<std::unordered_map<Key, ValueFlow::Value>>; using Args = std::vector<std::unordered_map<Key, ValueFlow::Value>>;
Args args(1); Args args(1);
@ -7607,7 +7607,7 @@ struct IteratorRange
}; };
template<class Iterator> template<class Iterator>
IteratorRange<Iterator> MakeIteratorRange(Iterator start, Iterator last) static IteratorRange<Iterator> MakeIteratorRange(Iterator start, Iterator last)
{ {
return {start, last}; return {start, last};
} }
@ -8260,7 +8260,7 @@ static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value)
value); value);
} }
ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings* settings) static ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings* settings)
{ {
if (value.isContainerSizeValue()) if (value.isContainerSizeValue())
return ContainerExpressionAnalyzer(exprTok, std::move(value), tokenlist, settings); return ContainerExpressionAnalyzer(exprTok, std::move(value), tokenlist, settings);
@ -8268,7 +8268,7 @@ ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, co
return ExpressionAnalyzer(expr, std::move(value), tokenlist, settings); return ExpressionAnalyzer(expr, std::move(value), tokenlist, settings);
} }
ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings* settings) static ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings* settings)
{ {
if (value.isContainerSizeValue()) if (value.isContainerSizeValue())
return ContainerExpressionAnalyzer(exprTok, std::move(value), tokenlist, settings); return ContainerExpressionAnalyzer(exprTok, std::move(value), tokenlist, settings);
@ -9380,7 +9380,7 @@ struct ValueFlowPassAdaptor : ValueFlowPass {
const char* mName = nullptr; const char* mName = nullptr;
bool mCPP = false; bool mCPP = false;
F mRun; F mRun;
ValueFlowPassAdaptor(const char* pname, bool pcpp, F prun) : mName(pname), mCPP(pcpp), mRun(prun) {} ValueFlowPassAdaptor(const char* pname, bool pcpp, F prun) : ValueFlowPass(), mName(pname), mCPP(pcpp), mRun(prun) {}
const char* name() const override { const char* name() const override {
return mName; return mName;
} }
@ -9394,7 +9394,7 @@ struct ValueFlowPassAdaptor : ValueFlowPass {
}; };
template<class F> template<class F>
ValueFlowPassAdaptor<F> makeValueFlowPassAdaptor(const char* name, bool cpp, F run) static ValueFlowPassAdaptor<F> makeValueFlowPassAdaptor(const char* name, bool cpp, F run)
{ {
return {name, cpp, run}; return {name, cpp, run};
} }

View File

@ -1,6 +1,9 @@
// For a release version x.y.z the MAJOR should be x and both MINOR and DEVMINOR should be y. // For a release version x.y.z the MAJOR should be x and both MINOR and DEVMINOR should be y.
// After a release the DEVMINOR is incremented. MAJOR=x MINOR=y, DEVMINOR=y+1 // After a release the DEVMINOR is incremented. MAJOR=x MINOR=y, DEVMINOR=y+1
#ifndef versionH
#define versionH
#define CPPCHECK_MAJOR_VERSION 2 #define CPPCHECK_MAJOR_VERSION 2
#define CPPCHECK_MINOR_VERSION 12 #define CPPCHECK_MINOR_VERSION 12
#define CPPCHECK_DEVMINOR_VERSION 13 #define CPPCHECK_DEVMINOR_VERSION 13
@ -16,3 +19,5 @@
#define CPPCHECK_VERSION CPPCHECK_MAJOR_VERSION,CPPCHECK_MINOR_VERSION,99,0 #define CPPCHECK_VERSION CPPCHECK_MAJOR_VERSION,CPPCHECK_MINOR_VERSION,99,0
#endif #endif
#define LEGALCOPYRIGHT L"Copyright (C) 2007-2023 Cppcheck team." #define LEGALCOPYRIGHT L"Copyright (C) 2007-2023 Cppcheck team."
#endif