Revert "Clang import; This experimental feature didn't "take off" much. After a lot of work we are still far fram the goal. I remove it now but don't rule out completely that it could ever be added again."
This reverts commit 207361b174
.
This commit is contained in:
parent
59ef06819a
commit
d2d2124238
|
@ -121,6 +121,7 @@ jobs:
|
|||
copy .\bin\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel!
|
||||
copy .\bin\cppcheck-core.dll .\cppcheck-core.dll || exit /b !errorlevel!
|
||||
cd test/cli || exit /b !errorlevel!
|
||||
python -m pytest --suppress-no-test-exit-code test-clang-import.py || exit /b !errorlevel!
|
||||
python -m pytest test-helloworld.py || exit /b !errorlevel!
|
||||
python -m pytest test-inline-suppress.py || exit /b !errorlevel!
|
||||
python -m pytest test-more-projects.py || exit /b !errorlevel!
|
||||
|
|
10
Makefile
10
Makefile
|
@ -185,6 +185,7 @@ LIBOBJ = $(libcppdir)/analyzerinfo.o \
|
|||
$(libcppdir)/checkunusedfunctions.o \
|
||||
$(libcppdir)/checkunusedvar.o \
|
||||
$(libcppdir)/checkvaarg.o \
|
||||
$(libcppdir)/clangimport.o \
|
||||
$(libcppdir)/cppcheck.o \
|
||||
$(libcppdir)/ctu.o \
|
||||
$(libcppdir)/errorlogger.o \
|
||||
|
@ -232,6 +233,7 @@ TESTOBJ = test/options.o \
|
|||
test/testbufferoverrun.o \
|
||||
test/testbughuntingchecks.o \
|
||||
test/testcharvar.o \
|
||||
test/testclangimport.o \
|
||||
test/testclass.o \
|
||||
test/testcmdlineparser.o \
|
||||
test/testcondition.o \
|
||||
|
@ -491,7 +493,10 @@ $(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp externals/simplecpp/simple
|
|||
$(libcppdir)/checkvaarg.o: lib/checkvaarg.cpp lib/astutils.h lib/check.h lib/checkvaarg.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
|
||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkvaarg.o $(libcppdir)/checkvaarg.cpp
|
||||
|
||||
$(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/astutils.h lib/check.h lib/checkunusedfunctions.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h
|
||||
$(libcppdir)/clangimport.o: lib/clangimport.cpp lib/astutils.h lib/clangimport.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
|
||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/clangimport.o $(libcppdir)/clangimport.cpp
|
||||
|
||||
$(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/astutils.h lib/check.h lib/checkunusedfunctions.h lib/clangimport.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h
|
||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/cppcheck.o $(libcppdir)/cppcheck.cpp
|
||||
|
||||
$(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
|
||||
|
@ -617,6 +622,9 @@ test/testbughuntingchecks.o: test/testbughuntingchecks.cpp lib/astutils.h lib/co
|
|||
test/testcharvar.o: test/testcharvar.cpp lib/astutils.h lib/check.h lib/checkother.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
|
||||
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcharvar.o test/testcharvar.cpp
|
||||
|
||||
test/testclangimport.o: test/testclangimport.cpp lib/astutils.h lib/clangimport.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
|
||||
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testclangimport.o test/testclangimport.cpp
|
||||
|
||||
test/testclass.o: test/testclass.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/checkclass.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
|
||||
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testclass.o test/testclass.cpp
|
||||
|
||||
|
|
|
@ -235,7 +235,10 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
|
|||
}
|
||||
|
||||
else if (std::strncmp(argv[i], "--clang", 7) == 0) {
|
||||
printMessage("Cppcheck: Clang import has been removed");
|
||||
mSettings->clang = true;
|
||||
if (std::strncmp(argv[i], "--clang=", 8) == 0) {
|
||||
mSettings->clangExecutable = argv[i] + 8;
|
||||
}
|
||||
}
|
||||
|
||||
else if (std::strncmp(argv[i], "--config-exclude=",17) ==0) {
|
||||
|
@ -989,11 +992,18 @@ void CmdLineParser::printHelp()
|
|||
" * faster analysis; Cppcheck will reuse the results if\n"
|
||||
" the hash for a file is unchanged.\n"
|
||||
" * some useful debug information, i.e. commands used to\n"
|
||||
" execute clang-tidy/addons.\n"
|
||||
" execute clang/clang-tidy/addons.\n"
|
||||
" --check-config Check cppcheck configuration. The normal code\n"
|
||||
" analysis is disabled by this flag.\n"
|
||||
" --check-library Show information messages when library files have\n"
|
||||
" incomplete info.\n"
|
||||
" --clang=<path> Experimental: Use Clang parser instead of the builtin Cppcheck\n"
|
||||
" parser. Takes the executable as optional parameter and\n"
|
||||
" defaults to `clang`. Cppcheck will run the given Clang\n"
|
||||
" executable, import the Clang AST and convert it into\n"
|
||||
" Cppcheck data. After that the normal Cppcheck analysis is\n"
|
||||
" used. You must have the executable in PATH if no path is\n"
|
||||
" given.\n"
|
||||
" --config-exclude=<dir>\n"
|
||||
" Path (prefix) to be excluded from configuration\n"
|
||||
" checking. Preprocessor configurations defined in\n"
|
||||
|
|
|
@ -873,6 +873,7 @@ Settings MainWindow::getCppcheckSettings()
|
|||
result.userDefines += define.toStdString();
|
||||
}
|
||||
|
||||
result.clang = mProjectFile->clangParser;
|
||||
result.bugHunting = mProjectFile->bugHunting;
|
||||
result.bugHuntingReport = " ";
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ ProjectFile::ProjectFile(const QString &filename, QObject *parent) :
|
|||
void ProjectFile::clear()
|
||||
{
|
||||
const Settings settings;
|
||||
clangParser = false;
|
||||
bugHunting = false;
|
||||
mRootPath.clear();
|
||||
mBuildDir.clear();
|
||||
|
@ -61,7 +62,7 @@ void ProjectFile::clear()
|
|||
mPlatform.clear();
|
||||
mSuppressions.clear();
|
||||
mAddons.clear();
|
||||
mClangTidy = false;
|
||||
mClangAnalyzer = mClangTidy = false;
|
||||
mAnalyzeAllVsConfigs = false;
|
||||
mCheckHeaders = true;
|
||||
mCheckUnusedTemplates = true;
|
||||
|
@ -117,6 +118,9 @@ bool ProjectFile::read(const QString &filename)
|
|||
if (xmlReader.name() == CppcheckXml::AnalyzeAllVsConfigsElementName)
|
||||
mAnalyzeAllVsConfigs = readBool(xmlReader);
|
||||
|
||||
if (xmlReader.name() == CppcheckXml::Parser)
|
||||
clangParser = true;
|
||||
|
||||
if (xmlReader.name() == CppcheckXml::BugHunting)
|
||||
bugHunting = true;
|
||||
|
||||
|
@ -182,6 +186,7 @@ bool ProjectFile::read(const QString &filename)
|
|||
if (xmlReader.name() == CppcheckXml::ToolsElementName) {
|
||||
QStringList tools;
|
||||
readStringList(tools, xmlReader, CppcheckXml::ToolElementName);
|
||||
mClangAnalyzer = tools.contains(CLANG_ANALYZER);
|
||||
mClangTidy = tools.contains(CLANG_TIDY);
|
||||
}
|
||||
|
||||
|
@ -870,6 +875,12 @@ bool ProjectFile::write(const QString &filename)
|
|||
xmlWriter.writeCharacters(mAnalyzeAllVsConfigs ? "true" : "false");
|
||||
xmlWriter.writeEndElement();
|
||||
|
||||
if (clangParser) {
|
||||
xmlWriter.writeStartElement(CppcheckXml::Parser);
|
||||
xmlWriter.writeCharacters("clang");
|
||||
xmlWriter.writeEndElement();
|
||||
}
|
||||
|
||||
if (bugHunting) {
|
||||
xmlWriter.writeStartElement(CppcheckXml::BugHunting);
|
||||
xmlWriter.writeEndElement();
|
||||
|
@ -1005,6 +1016,8 @@ bool ProjectFile::write(const QString &filename)
|
|||
CppcheckXml::AddonElementName);
|
||||
|
||||
QStringList tools;
|
||||
if (mClangAnalyzer)
|
||||
tools << CLANG_ANALYZER;
|
||||
if (mClangTidy)
|
||||
tools << CLANG_TIDY;
|
||||
writeStringList(xmlWriter,
|
||||
|
@ -1064,6 +1077,8 @@ QStringList ProjectFile::fromNativeSeparators(const QStringList &paths)
|
|||
QStringList ProjectFile::getAddonsAndTools() const
|
||||
{
|
||||
QStringList ret(mAddons);
|
||||
if (mClangAnalyzer)
|
||||
ret << CLANG_ANALYZER;
|
||||
if (mClangTidy)
|
||||
ret << CLANG_TIDY;
|
||||
return ret;
|
||||
|
|
|
@ -192,6 +192,14 @@ public:
|
|||
*/
|
||||
QStringList getAddonsAndTools() const;
|
||||
|
||||
bool getClangAnalyzer() const {
|
||||
return false; //mClangAnalyzer;
|
||||
}
|
||||
|
||||
void setClangAnalyzer(bool c) {
|
||||
mClangAnalyzer = c;
|
||||
}
|
||||
|
||||
bool getClangTidy() const {
|
||||
return mClangTidy;
|
||||
}
|
||||
|
@ -380,6 +388,9 @@ public:
|
|||
mCheckUnknownFunctionReturn = s;
|
||||
}
|
||||
|
||||
/** Use Clang parser */
|
||||
bool clangParser;
|
||||
|
||||
/** Bug hunting */
|
||||
bool bugHunting;
|
||||
protected:
|
||||
|
@ -572,6 +583,9 @@ private:
|
|||
*/
|
||||
QStringList mAddons;
|
||||
|
||||
/** @brief Execute clang analyzer? */
|
||||
bool mClangAnalyzer;
|
||||
|
||||
/** @brief Execute clang-tidy? */
|
||||
bool mClangTidy;
|
||||
|
||||
|
|
|
@ -270,6 +270,10 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
|||
mUI.mCheckUnusedTemplates->setChecked(projectFile->getCheckUnusedTemplates());
|
||||
mUI.mMaxCtuDepth->setValue(projectFile->getMaxCtuDepth());
|
||||
mUI.mMaxTemplateRecursion->setValue(projectFile->getMaxTemplateRecursion());
|
||||
if (projectFile->clangParser)
|
||||
mUI.mBtnClangParser->setChecked(true);
|
||||
else
|
||||
mUI.mBtnCppcheckParser->setChecked(true);
|
||||
mUI.mBtnSafeClasses->setChecked(projectFile->safeChecks.classes);
|
||||
mUI.mBtnBugHunting->setChecked(projectFile->bugHunting);
|
||||
setExcludedPaths(projectFile->getExcludedPaths());
|
||||
|
@ -337,6 +341,7 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
|||
mUI.mAddonMisra->setText(mUI.mAddonMisra->text() + ' ' + tr("(no rule texts file)"));
|
||||
}
|
||||
|
||||
mUI.mToolClangAnalyzer->setChecked(projectFile->getClangAnalyzer());
|
||||
mUI.mToolClangTidy->setChecked(projectFile->getClangTidy());
|
||||
if (CheckThread::clangTidyCmd().isEmpty()) {
|
||||
mUI.mToolClangTidy->setText(tr("Clang-tidy (not found)"));
|
||||
|
@ -363,6 +368,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
|||
projectFile->setCheckPaths(getCheckPaths());
|
||||
projectFile->setExcludedPaths(getExcludedPaths());
|
||||
projectFile->setLibraries(getLibraries());
|
||||
projectFile->clangParser = mUI.mBtnClangParser->isChecked();
|
||||
projectFile->safeChecks.classes = mUI.mBtnSafeClasses->isChecked();
|
||||
projectFile->bugHunting = mUI.mBtnBugHunting->isChecked();
|
||||
if (mUI.mComboBoxPlatform->currentText().endsWith(".xml"))
|
||||
|
@ -402,6 +408,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
|||
if (mUI.mAddonMisra->isChecked())
|
||||
list << "misra";
|
||||
projectFile->setAddons(list);
|
||||
projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked());
|
||||
projectFile->setClangTidy(mUI.mToolClangTidy->isChecked());
|
||||
projectFile->setTags(mUI.mEditTags->text().split(";", QString::SkipEmptyParts));
|
||||
}
|
||||
|
|
|
@ -426,6 +426,32 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_10">
|
||||
<property name="title">
|
||||
<string>Parser</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="mBtnCppcheckParser">
|
||||
<property name="text">
|
||||
<string>Cppcheck (built in)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="mBtnClangParser">
|
||||
<property name="text">
|
||||
<string>Clang (experimental)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_11">
|
||||
<property name="title">
|
||||
|
|
|
@ -69,6 +69,10 @@ void StatsDialog::setProject(const ProjectFile* projectFile)
|
|||
QChartView *chartView;
|
||||
chartView = createChart(statsFile, "cppcheck");
|
||||
mUI.mTabHistory->layout()->addWidget(chartView);
|
||||
if (projectFile->getClangAnalyzer()) {
|
||||
chartView = createChart(statsFile, CLANG_ANALYZER);
|
||||
mUI.mTabHistory->layout()->addWidget(chartView);
|
||||
}
|
||||
if (projectFile->getClangTidy()) {
|
||||
chartView = createChart(statsFile, CLANG_TIDY);
|
||||
mUI.mTabHistory->layout()->addWidget(chartView);
|
||||
|
|
|
@ -211,7 +211,7 @@ static void divByZero(const Token *tok, const ExprEngine::Value &value, ExprEngi
|
|||
if (tok->astParent()->astOperand2() == tok && value.isEqual(dataBase, 0)) {
|
||||
const char * const id = (tok->valueType() && tok->valueType()->isFloat()) ? "bughuntingDivByZeroFloat" : "bughuntingDivByZero";
|
||||
const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue);
|
||||
dataBase->reportError(tok->astParent(),
|
||||
dataBase->reportError(dataBase->settings->clang ? tok : tok->astParent(),
|
||||
Severity::SeverityType::error,
|
||||
id,
|
||||
"There is division, cannot determine that there can't be a division by zero.",
|
||||
|
|
|
@ -188,6 +188,9 @@ void CheckLeakAutoVar::doubleFreeError(const Token *tok, const Token *prevFreeTo
|
|||
|
||||
void CheckLeakAutoVar::check()
|
||||
{
|
||||
if (mSettings->clang)
|
||||
return;
|
||||
|
||||
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||
|
||||
// Local variables that are known to be non-zero.
|
||||
|
|
|
@ -758,6 +758,9 @@ void CheckMemoryLeakInClass::publicAllocationError(const Token *tok, const std::
|
|||
|
||||
void CheckMemoryLeakStructMember::check()
|
||||
{
|
||||
if (mSettings->clang)
|
||||
return;
|
||||
|
||||
const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||
for (const Variable* var : symbolDatabase->variableList()) {
|
||||
if (!var || !var->isLocal() || var->isStatic() || var->isReference())
|
||||
|
|
|
@ -884,6 +884,9 @@ void CheckOther::unreachableCodeError(const Token *tok, bool inconclusive)
|
|||
//---------------------------------------------------------------------------
|
||||
void CheckOther::checkVariableScope()
|
||||
{
|
||||
if (mSettings->clang)
|
||||
return;
|
||||
|
||||
if (!mSettings->severity.isEnabled(Severity::style))
|
||||
return;
|
||||
|
||||
|
|
|
@ -65,6 +65,9 @@ public:
|
|||
|
||||
/** @brief Run checks against the normal token list */
|
||||
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE {
|
||||
if (settings->clang)
|
||||
return;
|
||||
|
||||
CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger);
|
||||
checkUninitVar.check();
|
||||
checkUninitVar.valueFlowUninit();
|
||||
|
|
|
@ -1145,6 +1145,9 @@ void CheckUnusedVar::checkFunctionVariableUsage()
|
|||
if (!mSettings->severity.isEnabled(Severity::style))
|
||||
return;
|
||||
|
||||
if (mSettings->clang)
|
||||
return;
|
||||
|
||||
// Parse all executing scopes..
|
||||
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||
|
||||
|
|
|
@ -95,6 +95,8 @@ void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string&
|
|||
|
||||
void CheckVaarg::va_list_usage()
|
||||
{
|
||||
if (mSettings->clang)
|
||||
return;
|
||||
const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||
for (const Variable* var : symbolDatabase->variableList()) {
|
||||
if (!var || var->isPointer() || var->isReference() || var->isArray() || !var->scope() || var->typeStartToken()->str() != "va_list")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2020 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 clangimportH
|
||||
#define clangimportH
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <istream>
|
||||
|
||||
class Tokenizer;
|
||||
|
||||
namespace clangimport {
|
||||
void parseClangAstDump(Tokenizer *tokenizer, std::istream &f);
|
||||
}
|
||||
|
||||
#endif
|
158
lib/cppcheck.cpp
158
lib/cppcheck.cpp
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "check.h"
|
||||
#include "checkunusedfunctions.h"
|
||||
#include "clangimport.h"
|
||||
#include "ctu.h"
|
||||
#include "library.h"
|
||||
#include "mathlib.h"
|
||||
|
@ -319,8 +320,154 @@ const char * CppCheck::extraVersion()
|
|||
return ExtraVersion;
|
||||
}
|
||||
|
||||
static bool reportClangErrors(std::istream &is, std::function<void(const ErrorMessage&)> reportErr)
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(is, line)) {
|
||||
if (line.empty() || line[0] == ' ' || line[0] == '`' || line[0] == '-')
|
||||
continue;
|
||||
|
||||
std::string::size_type pos3 = line.find(": error: ");
|
||||
if (pos3 == std::string::npos)
|
||||
pos3 = line.find(": fatal error:");
|
||||
if (pos3 == std::string::npos)
|
||||
continue;
|
||||
|
||||
// file:line:column: error: ....
|
||||
const std::string::size_type pos2 = line.rfind(":", pos3 - 1);
|
||||
const std::string::size_type pos1 = line.rfind(":", pos2 - 1);
|
||||
|
||||
if (pos1 >= pos2 || pos2 >= pos3)
|
||||
continue;
|
||||
|
||||
const std::string filename = line.substr(0, pos1);
|
||||
const std::string linenr = line.substr(pos1+1, pos2-pos1-1);
|
||||
const std::string colnr = line.substr(pos2+1, pos3-pos2-1);
|
||||
const std::string msg = line.substr(line.find(":", pos3+1) + 2);
|
||||
|
||||
std::list<ErrorMessage::FileLocation> locationList;
|
||||
ErrorMessage::FileLocation loc;
|
||||
loc.setfile(Path::toNativeSeparators(filename));
|
||||
loc.line = std::atoi(linenr.c_str());
|
||||
loc.column = std::atoi(colnr.c_str());
|
||||
locationList.push_back(loc);
|
||||
ErrorMessage errmsg(locationList,
|
||||
loc.getfile(),
|
||||
Severity::error,
|
||||
msg,
|
||||
"syntaxError",
|
||||
Certainty::normal);
|
||||
reportErr(errmsg);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int CppCheck::check(const std::string &path)
|
||||
{
|
||||
if (mSettings.clang) {
|
||||
if (!mSettings.quiet)
|
||||
mErrorLogger.reportOut(std::string("Checking ") + path + "...");
|
||||
|
||||
const std::string lang = Path::isCPP(path) ? "-x c++" : "-x c";
|
||||
const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, "");
|
||||
const std::string clangcmd = analyzerInfo + ".clang-cmd";
|
||||
const std::string clangStderr = analyzerInfo + ".clang-stderr";
|
||||
const std::string clangAst = analyzerInfo + ".clang-ast";
|
||||
std::string exe = mSettings.clangExecutable;
|
||||
#ifdef _WIN32
|
||||
// append .exe if it is not a path
|
||||
if (Path::fromNativeSeparators(mSettings.clangExecutable).find('/') == std::string::npos) {
|
||||
exe += ".exe";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string flags(lang + " ");
|
||||
if (Path::isCPP(path) && !mSettings.standards.stdValue.empty())
|
||||
flags += "-std=" + mSettings.standards.stdValue + " ";
|
||||
|
||||
for (const std::string &i: mSettings.includePaths)
|
||||
flags += "-I" + i + " ";
|
||||
|
||||
flags += getDefinesFlags(mSettings.userDefines);
|
||||
|
||||
const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path;
|
||||
const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr);
|
||||
if (!mSettings.buildDir.empty()) {
|
||||
std::ofstream fout(clangcmd);
|
||||
fout << exe << " " << args2 << " " << redirect2 << std::endl;
|
||||
} else if (mSettings.verbose && !mSettings.quiet) {
|
||||
mErrorLogger.reportOut(exe + " " + args2);
|
||||
}
|
||||
|
||||
std::string output2;
|
||||
if (!mExecuteCommand(exe,split(args2),redirect2,&output2) || output2.find("TranslationUnitDecl") == std::string::npos) {
|
||||
std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ensure there are not syntax errors...
|
||||
if (!mSettings.buildDir.empty()) {
|
||||
std::ifstream fin(clangStderr);
|
||||
auto reportError = [this](const ErrorMessage& errorMessage) {
|
||||
reportErr(errorMessage);
|
||||
};
|
||||
if (reportClangErrors(fin, reportError))
|
||||
return 0;
|
||||
} else {
|
||||
std::istringstream istr(output2);
|
||||
auto reportError = [this](const ErrorMessage& errorMessage) {
|
||||
reportErr(errorMessage);
|
||||
};
|
||||
if (reportClangErrors(istr, reportError))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mSettings.buildDir.empty()) {
|
||||
std::ofstream fout(clangAst);
|
||||
fout << output2 << std::endl;
|
||||
}
|
||||
|
||||
try {
|
||||
std::istringstream ast(output2);
|
||||
Tokenizer tokenizer(&mSettings, this);
|
||||
tokenizer.list.appendFileIfNew(path);
|
||||
clangimport::parseClangAstDump(&tokenizer, ast);
|
||||
ValueFlow::setValues(&tokenizer.list, const_cast<SymbolDatabase *>(tokenizer.getSymbolDatabase()), this, &mSettings);
|
||||
if (mSettings.debugnormal)
|
||||
tokenizer.printDebugOutput(1);
|
||||
checkNormalTokens(tokenizer);
|
||||
|
||||
// create dumpfile
|
||||
std::ofstream fdump;
|
||||
std::string dumpFile;
|
||||
createDumpFile(mSettings, path, tokenizer.list.getFiles(), nullptr, fdump, dumpFile);
|
||||
if (fdump.is_open()) {
|
||||
fdump << "<dump cfg=\"\">" << std::endl;
|
||||
fdump << " <standards>" << std::endl;
|
||||
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
|
||||
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
|
||||
fdump << " </standards>" << std::endl;
|
||||
tokenizer.dump(fdump);
|
||||
fdump << "</dump>" << std::endl;
|
||||
fdump << "</dumps>" << std::endl;
|
||||
fdump.close();
|
||||
}
|
||||
|
||||
// run addons
|
||||
executeAddons(dumpFile);
|
||||
|
||||
} catch (const InternalError &e) {
|
||||
internalError(path, e.errorMessage);
|
||||
mExitCode = 1; // e.g. reflect a syntax error
|
||||
} catch (const std::exception &e) {
|
||||
internalError(path, e.what());
|
||||
}
|
||||
|
||||
return mExitCode;
|
||||
}
|
||||
|
||||
std::ifstream fin(path);
|
||||
return checkFile(Path::simplifyPath(path), emptyString, fin);
|
||||
}
|
||||
|
@ -337,6 +484,9 @@ unsigned int CppCheck::check(const ImportProject::FileSettings &fs)
|
|||
temp.mSettings = mSettings;
|
||||
if (!temp.mSettings.userDefines.empty())
|
||||
temp.mSettings.userDefines += ';';
|
||||
if (mSettings.clang)
|
||||
temp.mSettings.userDefines += fs.defines;
|
||||
else
|
||||
temp.mSettings.userDefines += fs.cppcheckDefines();
|
||||
temp.mSettings.includePaths = fs.includePaths;
|
||||
temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend());
|
||||
|
@ -346,6 +496,10 @@ unsigned int CppCheck::check(const ImportProject::FileSettings &fs)
|
|||
temp.mSettings.standards.setC(fs.standard);
|
||||
if (fs.platformType != Settings::Unspecified)
|
||||
temp.mSettings.platform(fs.platformType);
|
||||
if (mSettings.clang) {
|
||||
temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend());
|
||||
return temp.check(Path::simplifyPath(fs.filename));
|
||||
}
|
||||
std::ifstream fin(fs.filename);
|
||||
unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg, fin);
|
||||
mSettings.nomsg.addSuppressions(temp.mSettings.nomsg.getSuppressions());
|
||||
|
@ -800,6 +954,10 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
|
|||
check->runChecks(&tokenizer, &mSettings, this);
|
||||
}
|
||||
|
||||
if (mSettings.clang)
|
||||
// TODO: Use CTU for Clang analysis
|
||||
return;
|
||||
|
||||
// Analyse the tokens..
|
||||
|
||||
CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer);
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
<ClCompile Include="checkunusedfunctions.cpp" />
|
||||
<ClCompile Include="checkunusedvar.cpp" />
|
||||
<ClCompile Include="checkvaarg.cpp" />
|
||||
<ClCompile Include="clangimport.cpp" />
|
||||
<ClCompile Include="cppcheck.cpp" />
|
||||
<ClCompile Include="ctu.cpp" />
|
||||
<ClCompile Include="errorlogger.cpp" />
|
||||
|
@ -135,6 +136,7 @@
|
|||
<ClInclude Include="checkunusedfunctions.h" />
|
||||
<ClInclude Include="checkunusedvar.h" />
|
||||
<ClInclude Include="checkvaarg.h" />
|
||||
<ClInclude Include="clangimport.h" />
|
||||
<ClInclude Include="config.h" />
|
||||
<ClInclude Include="cppcheck.h" />
|
||||
<ClInclude Include="ctu.h" />
|
||||
|
|
|
@ -1143,6 +1143,8 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
|||
guiProject.platform = node->GetText();
|
||||
else if (strcmp(node->Name(), CppcheckXml::AnalyzeAllVsConfigsElementName) == 0)
|
||||
guiProject.analyzeAllVsConfigs = node->GetText();
|
||||
else if (strcmp(node->Name(), CppcheckXml::Parser) == 0)
|
||||
temp.clang = true;
|
||||
else if (strcmp(node->Name(), CppcheckXml::AddonsElementName) == 0)
|
||||
temp.addons = readXmlStringList(node, "", CppcheckXml::AddonElementName, nullptr);
|
||||
else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0)
|
||||
|
@ -1188,6 +1190,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
|||
settings->userDefines = temp.userDefines;
|
||||
settings->userUndefs = temp.userUndefs;
|
||||
settings->addons = temp.addons;
|
||||
settings->clang = temp.clang;
|
||||
settings->clangTidy = temp.clangTidy;
|
||||
|
||||
for (const std::string &p : paths)
|
||||
|
|
|
@ -125,6 +125,7 @@ namespace CppcheckXml {
|
|||
const char BuildDirElementName[] = "builddir";
|
||||
const char ImportProjectElementName[] = "importproject";
|
||||
const char AnalyzeAllVsConfigsElementName[] = "analyze-all-vs-configs";
|
||||
const char Parser[] = "parser";
|
||||
const char BugHunting[] = "bug-hunting";
|
||||
const char IncludeDirElementName[] = "includedir";
|
||||
const char DirElementName[] = "dir";
|
||||
|
|
|
@ -32,6 +32,7 @@ HEADERS += $${PWD}/analyzerinfo.h \
|
|||
$${PWD}/checkunusedfunctions.h \
|
||||
$${PWD}/checkunusedvar.h \
|
||||
$${PWD}/checkvaarg.h \
|
||||
$${PWD}/clangimport.h \
|
||||
$${PWD}/cppcheck.h \
|
||||
$${PWD}/ctu.h \
|
||||
$${PWD}/errorlogger.h \
|
||||
|
@ -89,6 +90,7 @@ SOURCES += $${PWD}/analyzerinfo.cpp \
|
|||
$${PWD}/checkunusedfunctions.cpp \
|
||||
$${PWD}/checkunusedvar.cpp \
|
||||
$${PWD}/checkvaarg.cpp \
|
||||
$${PWD}/clangimport.cpp \
|
||||
$${PWD}/cppcheck.cpp \
|
||||
$${PWD}/ctu.cpp \
|
||||
$${PWD}/errorlogger.cpp \
|
||||
|
|
|
@ -36,6 +36,8 @@ Settings::Settings()
|
|||
checkHeaders(true),
|
||||
checkLibrary(false),
|
||||
checkUnusedTemplates(true),
|
||||
clang(false),
|
||||
clangExecutable("clang"),
|
||||
clangTidy(false),
|
||||
daca(false),
|
||||
debugBugHunting(false),
|
||||
|
|
|
@ -140,6 +140,12 @@ public:
|
|||
/** Check unused/uninstantiated templates */
|
||||
bool checkUnusedTemplates;
|
||||
|
||||
/** Use Clang */
|
||||
bool clang;
|
||||
|
||||
/** Custom Clang executable */
|
||||
std::string clangExecutable;
|
||||
|
||||
/** Use clang-tidy */
|
||||
bool clangTidy;
|
||||
|
||||
|
|
|
@ -1823,6 +1823,69 @@ void SymbolDatabase::validate() const
|
|||
//validateVariables();
|
||||
}
|
||||
|
||||
void SymbolDatabase::clangSetVariables(const std::vector<const Variable *> &variableList)
|
||||
{
|
||||
mVariableList = variableList;
|
||||
}
|
||||
|
||||
Variable::Variable(const Token *name_, const std::string &clangType, const Token *typeStart,
|
||||
const Token *typeEnd, nonneg int index_, AccessControl access_,
|
||||
const Type *type_, const Scope *scope_)
|
||||
: mNameToken(name_),
|
||||
mTypeStartToken(typeStart),
|
||||
mTypeEndToken(typeEnd),
|
||||
mIndex(index_),
|
||||
mAccess(access_),
|
||||
mFlags(0),
|
||||
mType(type_),
|
||||
mScope(scope_),
|
||||
mValueType(nullptr)
|
||||
{
|
||||
if (!mTypeStartToken && mTypeEndToken) {
|
||||
mTypeStartToken = mTypeEndToken;
|
||||
while (Token::Match(mTypeStartToken->previous(), "%type%|*|&"))
|
||||
mTypeStartToken = mTypeStartToken->previous();
|
||||
}
|
||||
|
||||
while (Token::Match(mTypeStartToken, "const|struct|static")) {
|
||||
if (mTypeStartToken->str() == "static")
|
||||
setFlag(fIsStatic, true);
|
||||
mTypeStartToken = mTypeStartToken->next();
|
||||
}
|
||||
|
||||
if (Token::simpleMatch(mTypeEndToken, "&"))
|
||||
setFlag(fIsReference, true);
|
||||
else if (Token::simpleMatch(mTypeEndToken, "&&")) {
|
||||
setFlag(fIsReference, true);
|
||||
setFlag(fIsRValueRef, true);
|
||||
}
|
||||
|
||||
std::string::size_type pos = clangType.find("[");
|
||||
if (pos != std::string::npos) {
|
||||
setFlag(fIsArray, true);
|
||||
do {
|
||||
const std::string::size_type pos1 = pos+1;
|
||||
pos = clangType.find("]", pos1);
|
||||
Dimension dim;
|
||||
dim.tok = nullptr;
|
||||
dim.known = pos > pos1;
|
||||
if (pos > pos1)
|
||||
dim.num = MathLib::toLongNumber(clangType.substr(pos1, pos-pos1));
|
||||
else
|
||||
dim.num = 0;
|
||||
mDimensions.push_back(dim);
|
||||
++pos;
|
||||
} while (pos < clangType.size() && clangType[pos] == '[');
|
||||
}
|
||||
|
||||
// Is there initialization in variable declaration
|
||||
const Token *initTok = mNameToken ? mNameToken->next() : nullptr;
|
||||
while (initTok && initTok->str() == "[")
|
||||
initTok = initTok->link()->next();
|
||||
if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq()))
|
||||
setFlag(fIsInit, true);
|
||||
}
|
||||
|
||||
Variable::Variable(const Variable &var, const Scope *scope)
|
||||
: mValueType(nullptr)
|
||||
{
|
||||
|
@ -2170,6 +2233,39 @@ Function::Function(const Tokenizer *mTokenizer,
|
|||
}
|
||||
}
|
||||
|
||||
Function::Function(const Token *tokenDef, const std::string &clangType)
|
||||
: tokenDef(tokenDef),
|
||||
argDef(nullptr),
|
||||
token(nullptr),
|
||||
arg(nullptr),
|
||||
retDef(nullptr),
|
||||
retType(nullptr),
|
||||
functionScope(nullptr),
|
||||
nestedIn(nullptr),
|
||||
initArgCount(0),
|
||||
type(eFunction),
|
||||
access(AccessControl::Public),
|
||||
noexceptArg(nullptr),
|
||||
throwArg(nullptr),
|
||||
templateDef(nullptr),
|
||||
functionPointerUsage(nullptr),
|
||||
mFlags(0)
|
||||
{
|
||||
// operator function
|
||||
if (::isOperator(tokenDef)) {
|
||||
isOperator(true);
|
||||
|
||||
// 'operator =' is special
|
||||
if (tokenDef->str() == "operator=")
|
||||
type = Function::eOperatorEqual;
|
||||
}
|
||||
|
||||
setFlags(tokenDef, tokenDef->scope());
|
||||
|
||||
if (endsWith(clangType, " const", 6))
|
||||
isConst(true);
|
||||
}
|
||||
|
||||
const Token *Function::setFlags(const Token *tok1, const Scope *scope)
|
||||
{
|
||||
// look for end of previous statement
|
||||
|
@ -5951,7 +6047,30 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V
|
|||
parsedecl(type->type()->typeStart, valuetype, defaultSignedness, settings);
|
||||
else if (type->str() == "const")
|
||||
valuetype->constness |= (1 << (valuetype->pointer - pointer0));
|
||||
else if (const Library::Container *container = settings->library.detectContainer(type)) {
|
||||
else if (settings->clang && type->str().size() > 2 && type->str().find("::") < type->str().find("<")) {
|
||||
TokenList typeTokens(settings);
|
||||
std::string::size_type pos1 = 0;
|
||||
do {
|
||||
std::string::size_type pos2 = type->str().find("::", pos1);
|
||||
if (pos2 == std::string::npos) {
|
||||
typeTokens.addtoken(type->str().substr(pos1), 0, 0, 0, false);
|
||||
break;
|
||||
}
|
||||
typeTokens.addtoken(type->str().substr(pos1, pos2 - pos1), 0, 0, 0, false);
|
||||
typeTokens.addtoken("::", 0, 0, 0, false);
|
||||
pos1 = pos2 + 2;
|
||||
} while (pos1 < type->str().size());
|
||||
const Library::Container *container = settings->library.detectContainer(typeTokens.front());
|
||||
if (container) {
|
||||
valuetype->type = ValueType::Type::CONTAINER;
|
||||
valuetype->container = container;
|
||||
} else {
|
||||
const Scope *scope = type->scope();
|
||||
valuetype->typeScope = scope->check->findScope(typeTokens.front(), scope);
|
||||
if (valuetype->typeScope)
|
||||
valuetype->type = (scope->type == Scope::ScopeType::eClass) ? ValueType::Type::RECORD : ValueType::Type::NONSTD;
|
||||
}
|
||||
} else if (const Library::Container *container = settings->library.detectContainer(type)) {
|
||||
valuetype->type = ValueType::Type::CONTAINER;
|
||||
valuetype->container = container;
|
||||
while (Token::Match(type, "%name%|::|<")) {
|
||||
|
|
|
@ -237,6 +237,10 @@ public:
|
|||
evaluate(settings);
|
||||
}
|
||||
|
||||
Variable(const Token *name_, const std::string &clangType, const Token *typeStart,
|
||||
const Token *typeEnd, nonneg int index_, AccessControl access_,
|
||||
const Type *type_, const Scope *scope_);
|
||||
|
||||
Variable(const Variable &var, const Scope *scope);
|
||||
|
||||
Variable(const Variable &var);
|
||||
|
@ -742,6 +746,7 @@ public:
|
|||
enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction, eLambda };
|
||||
|
||||
Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef);
|
||||
Function(const Token *tokenDef, const std::string &clangType);
|
||||
|
||||
const std::string &name() const {
|
||||
return tokenDef->str();
|
||||
|
@ -1379,6 +1384,7 @@ public:
|
|||
/** Set array dimensions when valueflow analysis is completed */
|
||||
void setArrayDimensionsUsingValueFlow();
|
||||
|
||||
void clangSetVariables(const std::vector<const Variable *> &variableList);
|
||||
void createSymbolDatabaseExprIds();
|
||||
|
||||
private:
|
||||
|
|
|
@ -169,6 +169,11 @@ int TokenList::appendFileIfNew(const std::string &fileName)
|
|||
return mFiles.size() - 1;
|
||||
}
|
||||
|
||||
void TokenList::clangSetOrigFiles()
|
||||
{
|
||||
mOrigFiles = mFiles;
|
||||
}
|
||||
|
||||
void TokenList::deleteTokens(Token *tok)
|
||||
{
|
||||
while (tok) {
|
||||
|
|
|
@ -186,6 +186,8 @@ public:
|
|||
*/
|
||||
void simplifyStdType();
|
||||
|
||||
void clangSetOrigFiles();
|
||||
|
||||
bool isKeyword(const std::string &str) const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -132,6 +132,18 @@ directory individually. The following command ignores both the src/b and src/c d
|
|||
|
||||
cppcheck -isrc/b -isrc/c
|
||||
|
||||
### Clang parser (experimental)
|
||||
|
||||
By default Cppcheck uses an internal C/C++ parser. However there is an experimental option to use the Clang parser instead.
|
||||
|
||||
Install `clang`. Then use Cppcheck option `--clang`.
|
||||
|
||||
Technically, Cppcheck will execute `clang` with its `-ast-dump` option. The Clang output is then imported and converted into
|
||||
the normal Cppcheck format. And then normal Cppcheck analysis is performed on that.
|
||||
|
||||
You can also pass a custom Clang executable to the option by using for example `--clang=clang-10`. You can also pass it
|
||||
with a path. On Windows it will append the `.exe` extension unless you use a path.
|
||||
|
||||
## Severities
|
||||
|
||||
The possible severities for messages are:
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
|
||||
# python -m pytest test-clang-import.py
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import pytest
|
||||
from testutils import cppcheck
|
||||
|
||||
try:
|
||||
subprocess.call(['clang', '--version'])
|
||||
except OSError:
|
||||
pytest.skip("'clang' does not exist", allow_module_level=True)
|
||||
|
||||
|
||||
def get_debug_section(title, stdout):
|
||||
s = re.sub(r'0x[0-9a-fA-F]+', '0x12345678', stdout)
|
||||
s = re.sub(r'nestedIn: Struct', 'nestedIn: Class', s)
|
||||
s = re.sub(r'classDef: struct', 'classDef: class', s)
|
||||
s = re.sub(r'isInline: [a-z]+', 'isInline: ---', s)
|
||||
s = re.sub(r'needInitialization: .*', 'needInitialization: ---', s)
|
||||
s = re.sub(r'functionOf: .*', 'functionOf: ---', s)
|
||||
s = re.sub(r'0x12345678 Struct', '0x12345678 Class', s)
|
||||
|
||||
if title == '##AST':
|
||||
# TODO set types
|
||||
s = re.sub(r"return '[a-zA-Z0-9: *]+'", "return", s)
|
||||
|
||||
pos1 = s.find(title)
|
||||
assert pos1 > 0, 'title not found'
|
||||
pos1 = s.find('\n', pos1) + 1
|
||||
assert pos1 > 0
|
||||
pos2 = s.find("\n##", pos1)
|
||||
if pos2 < 0:
|
||||
return s[pos1:]
|
||||
return s[pos1:pos2-1]
|
||||
|
||||
|
||||
def check_symbol_database(code):
|
||||
testfile = 'test.cpp'
|
||||
with open(testfile, 'w+t') as f:
|
||||
f.write(code)
|
||||
ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile])
|
||||
ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile])
|
||||
os.remove(testfile)
|
||||
assert 0 == ret1, stdout1
|
||||
assert 0 == ret2, stdout2
|
||||
assert get_debug_section('### Symbol database', stdout1) == get_debug_section('### Symbol database', stdout2)
|
||||
|
||||
|
||||
def check_ast(code):
|
||||
testfile = 'test.cpp'
|
||||
with open(testfile, 'w+t') as f:
|
||||
f.write(code)
|
||||
ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile])
|
||||
ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile])
|
||||
os.remove(testfile)
|
||||
assert 0 == ret1, stdout1
|
||||
assert 0 == ret2, stdout1
|
||||
assert get_debug_section('##AST', stdout1) == get_debug_section('##AST', stdout2)
|
||||
|
||||
|
||||
def todo_check_ast(code):
|
||||
testfile = 'test.cpp'
|
||||
with open(testfile, 'w+t') as f:
|
||||
f.write(code)
|
||||
ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile])
|
||||
ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile])
|
||||
os.remove(testfile)
|
||||
assert 0 == ret1, stdout1
|
||||
assert 0 == ret2, stdout2
|
||||
assert get_debug_section('##AST', stdout1) != get_debug_section('##AST', stdout2)
|
||||
|
||||
|
||||
|
||||
def test_symbol_database_1():
|
||||
check_symbol_database('int main(){return 0;}')
|
||||
|
||||
def test_symbol_database_2():
|
||||
check_symbol_database('struct Foo { void f(); }; void Foo::f() {}')
|
||||
|
||||
def test_symbol_database_3():
|
||||
check_symbol_database('struct Fred { int a; }; int b; void f(int c, int d) { int e; }')
|
||||
|
||||
def test_symbol_database_4():
|
||||
check_symbol_database('void f(const int x) {}')
|
||||
|
||||
def test_symbol_database_5():
|
||||
check_symbol_database('void f(int);')
|
||||
|
||||
def test_symbol_database_6():
|
||||
check_symbol_database('inline static int foo(int x) { return x; }')
|
||||
|
||||
def test_symbol_database_7():
|
||||
check_symbol_database('struct S {int x;}; void f(struct S *s) {}')
|
||||
|
||||
def test_symbol_database_class_access_1():
|
||||
check_symbol_database('class Fred { void foo ( ) {} } ;')
|
||||
|
||||
def test_symbol_database_class_access_2():
|
||||
check_symbol_database('class Fred { protected: void foo ( ) {} } ;')
|
||||
|
||||
def test_symbol_database_class_access_3():
|
||||
check_symbol_database('class Fred { public: void foo ( ) {} } ;')
|
||||
|
||||
def test_symbol_database_operator():
|
||||
check_symbol_database('struct Fred { void operator=(int x); };')
|
||||
|
||||
def test_symbol_database_struct_1():
|
||||
check_symbol_database('struct S {};')
|
||||
|
||||
def test_ast_calculations():
|
||||
check_ast('int x = 5; int y = (x + 4) * 2;')
|
||||
check_ast('long long dostuff(int x) { return x ? 3 : 5; }')
|
||||
|
||||
def test_ast_control_flow():
|
||||
check_ast('void foo(int x) { if (x > 5){} }')
|
||||
check_ast('int dostuff() { for (int x = 0; x < 10; x++); }')
|
||||
check_ast('void foo(int x) { switch (x) {case 1: break; } }')
|
||||
check_ast('void foo(int a, int b, int c) { foo(a,b,c); }')
|
||||
|
||||
def test_ast():
|
||||
check_ast('struct S { int x; }; S* foo() { return new S(); }')
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue