diff --git a/.github/workflows/CI-windows.yml b/.github/workflows/CI-windows.yml index 5b9c7e17b..67816efc6 100644 --- a/.github/workflows/CI-windows.yml +++ b/.github/workflows/CI-windows.yml @@ -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! diff --git a/Makefile b/Makefile index f35d85695..088877825 100644 --- a/Makefile +++ b/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 diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 0c8807fea..c2e56db32 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.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= 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=\n" " Path (prefix) to be excluded from configuration\n" " checking. Preprocessor configurations defined in\n" diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 129fe7549..1e00d132d 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -873,6 +873,7 @@ Settings MainWindow::getCppcheckSettings() result.userDefines += define.toStdString(); } + result.clang = mProjectFile->clangParser; result.bugHunting = mProjectFile->bugHunting; result.bugHuntingReport = " "; diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 9984a5039..53ccb92fc 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -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; diff --git a/gui/projectfile.h b/gui/projectfile.h index 647e61037..cd56f9557 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -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; diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 4a2e0c8db..6c5e138ac 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -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)); } diff --git a/gui/projectfiledialog.ui b/gui/projectfiledialog.ui index af9c14e30..e8db9338e 100644 --- a/gui/projectfiledialog.ui +++ b/gui/projectfiledialog.ui @@ -426,6 +426,32 @@ + + + + Parser + + + + + + Cppcheck (built in) + + + true + + + + + + + Clang (experimental) + + + + + + diff --git a/gui/statsdialog.cpp b/gui/statsdialog.cpp index 32dfadd78..7b01b6e85 100644 --- a/gui/statsdialog.cpp +++ b/gui/statsdialog.cpp @@ -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); diff --git a/lib/bughuntingchecks.cpp b/lib/bughuntingchecks.cpp index c85041413..cc29463e6 100644 --- a/lib/bughuntingchecks.cpp +++ b/lib/bughuntingchecks.cpp @@ -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.", diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 7ab27ef07..6562772b5 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -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. diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index b32ea164c..0943c9c9d 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -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()) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 4cd72cc4e..dc40d73a4 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -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; diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h index c852be046..2125b33fb 100644 --- a/lib/checkuninitvar.h +++ b/lib/checkuninitvar.h @@ -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(); diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 57d26d58c..fe37e477f 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -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(); diff --git a/lib/checkvaarg.cpp b/lib/checkvaarg.cpp index b8179ee42..ec2fc037e 100644 --- a/lib/checkvaarg.cpp +++ b/lib/checkvaarg.cpp @@ -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") diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp new file mode 100644 index 000000000..315af9998 --- /dev/null +++ b/lib/clangimport.cpp @@ -0,0 +1,1616 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 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 . + */ + +#include "clangimport.h" +#include "settings.h" +#include "symboldatabase.h" +#include "tokenize.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include + +static const std::string AccessSpecDecl = "AccessSpecDecl"; +static const std::string ArraySubscriptExpr = "ArraySubscriptExpr"; +static const std::string BinaryOperator = "BinaryOperator"; +static const std::string BreakStmt = "BreakStmt"; +static const std::string CallExpr = "CallExpr"; +static const std::string CaseStmt = "CaseStmt"; +static const std::string CharacterLiteral = "CharacterLiteral"; +static const std::string ClassTemplateDecl = "ClassTemplateDecl"; +static const std::string ClassTemplateSpecializationDecl = "ClassTemplateSpecializationDecl"; +static const std::string ConditionalOperator = "ConditionalOperator"; +static const std::string ConstantExpr = "ConstantExpr"; +static const std::string CompoundAssignOperator = "CompoundAssignOperator"; +static const std::string CompoundStmt = "CompoundStmt"; +static const std::string ContinueStmt = "ContinueStmt"; +static const std::string CStyleCastExpr = "CStyleCastExpr"; +static const std::string CXXBindTemporaryExpr = "CXXBindTemporaryExpr"; +static const std::string CXXBoolLiteralExpr = "CXXBoolLiteralExpr"; +static const std::string CXXConstructorDecl = "CXXConstructorDecl"; +static const std::string CXXConstructExpr = "CXXConstructExpr"; +static const std::string CXXDefaultArgExpr = "CXXDefaultArgExpr"; +static const std::string CXXDeleteExpr = "CXXDeleteExpr"; +static const std::string CXXDestructorDecl = "CXXDestructorDecl"; +static const std::string CXXForRangeStmt = "CXXForRangeStmt"; +static const std::string CXXFunctionalCastExpr = "CXXFunctionalCastExpr"; +static const std::string CXXMemberCallExpr = "CXXMemberCallExpr"; +static const std::string CXXMethodDecl = "CXXMethodDecl"; +static const std::string CXXNewExpr = "CXXNewExpr"; +static const std::string CXXNullPtrLiteralExpr = "CXXNullPtrLiteralExpr"; +static const std::string CXXOperatorCallExpr = "CXXOperatorCallExpr"; +static const std::string CXXRecordDecl = "CXXRecordDecl"; +static const std::string CXXStaticCastExpr = "CXXStaticCastExpr"; +static const std::string CXXStdInitializerListExpr = "CXXStdInitializerListExpr"; +static const std::string CXXTemporaryObjectExpr = "CXXTemporaryObjectExpr"; +static const std::string CXXThisExpr = "CXXThisExpr"; +static const std::string CXXThrowExpr = "CXXThrowExpr"; +static const std::string DeclRefExpr = "DeclRefExpr"; +static const std::string DeclStmt = "DeclStmt"; +static const std::string DefaultStmt = "DefaultStmt"; +static const std::string DoStmt = "DoStmt"; +static const std::string EnumConstantDecl = "EnumConstantDecl"; +static const std::string EnumDecl = "EnumDecl"; +static const std::string ExprWithCleanups = "ExprWithCleanups"; +static const std::string FieldDecl = "FieldDecl"; +static const std::string FloatingLiteral = "FloatingLiteral"; +static const std::string ForStmt = "ForStmt"; +static const std::string FunctionDecl = "FunctionDecl"; +static const std::string FunctionTemplateDecl = "FunctionTemplateDecl"; +static const std::string GotoStmt = "GotoStmt"; +static const std::string IfStmt = "IfStmt"; +static const std::string ImplicitCastExpr = "ImplicitCastExpr"; +static const std::string InitListExpr = "InitListExpr"; +static const std::string IntegerLiteral = "IntegerLiteral"; +static const std::string LabelStmt = "LabelStmt"; +static const std::string LinkageSpecDecl = "LinkageSpecDecl"; +static const std::string MaterializeTemporaryExpr = "MaterializeTemporaryExpr"; +static const std::string MemberExpr = "MemberExpr"; +static const std::string NamespaceDecl = "NamespaceDecl"; +static const std::string NullStmt = "NullStmt"; +static const std::string ParenExpr = "ParenExpr"; +static const std::string ParmVarDecl = "ParmVarDecl"; +static const std::string RecordDecl = "RecordDecl"; +static const std::string ReturnStmt = "ReturnStmt"; +static const std::string StringLiteral = "StringLiteral"; +static const std::string SwitchStmt = "SwitchStmt"; +static const std::string TemplateArgument = "TemplateArgument"; +static const std::string TypedefDecl = "TypedefDecl"; +static const std::string UnaryOperator = "UnaryOperator"; +static const std::string UnaryExprOrTypeTraitExpr = "UnaryExprOrTypeTraitExpr"; +static const std::string VarDecl = "VarDecl"; +static const std::string WhileStmt = "WhileStmt"; + +static std::string unquote(const std::string &s) +{ + return (s[0] == '\'') ? s.substr(1, s.size() - 2) : s; +} + + +static std::vector splitString(const std::string &line) +{ + std::vector ret; + std::string::size_type pos1 = line.find_first_not_of(" "); + while (pos1 < line.size()) { + std::string::size_type pos2; + if (std::strchr("*()", line[pos1])) { + ret.push_back(line.substr(pos1,1)); + pos1 = line.find_first_not_of(" ", pos1 + 1); + continue; + } + if (line[pos1] == '<') + pos2 = line.find(">", pos1); + else if (line[pos1] == '\"') + pos2 = line.find("\"", pos1+1); + else if (line[pos1] == '\'') { + pos2 = line.find("\'", pos1+1); + if (pos2 < (int)line.size() - 3 && line.compare(pos2, 3, "\':\'", 0, 3) == 0) + pos2 = line.find("\'", pos2 + 3); + } else { + pos2 = pos1; + while (pos2 < line.size() && (line[pos2] == '_' || line[pos2] == ':' || std::isalnum((unsigned char)line[pos2]))) + ++pos2; + if (pos2 > pos1 && pos2 < line.size() && line[pos2] == '<' && std::isalpha(line[pos1])) { + int tlevel = 1; + while (++pos2 < line.size() && tlevel > 0) { + if (line[pos2] == '<') + ++tlevel; + else if (line[pos2] == '>') + --tlevel; + } + if (tlevel == 0 && pos2 < line.size() && line[pos2] == ' ') { + ret.push_back(line.substr(pos1, pos2-pos1)); + pos1 = pos2 + 1; + continue; + } + } + + pos2 = line.find(" ", pos1) - 1; + if ((std::isalpha(line[pos1]) || line[pos1] == '_') && + line.find("::", pos1) < pos2 && + line.find("::", pos1) < line.find("<", pos1)) { + pos2 = line.find("::", pos1); + ret.push_back(line.substr(pos1, pos2-pos1)); + ret.push_back("::"); + pos1 = pos2 + 2; + continue; + } + if ((std::isalpha(line[pos1]) || line[pos1] == '_') && + line.find("<", pos1) < pos2 && + line.find("<<",pos1) != line.find("<",pos1) && + line.find(">", pos1) != std::string::npos && + line.find(">", pos1) > pos2) { + int level = 0; + for (pos2 = pos1; pos2 < line.size(); ++pos2) { + if (line[pos2] == '<') + ++level; + else if (line[pos2] == '>') { + if (level <= 1) + break; + --level; + } + } + if (level > 1 && pos2 + 1 >= line.size()) + return std::vector {}; + pos2 = line.find(" ", pos2); + if (pos2 != std::string::npos) + --pos2; + } + } + if (pos2 == std::string::npos) { + ret.push_back(line.substr(pos1)); + break; + } + ret.push_back(line.substr(pos1, pos2+1-pos1)); + pos1 = line.find_first_not_of(" ", pos2 + 1); + } + return ret; +} + +static bool contains(const std::vector &haystack, const std::string &needle) +{ + return std::find(haystack.begin(), haystack.end(), needle) != haystack.end(); +} + +namespace clangimport { + struct Data { + struct Decl { + explicit Decl(Scope *scope) : def(nullptr), enumerator(nullptr), function(nullptr), scope(scope), var(nullptr) {} + Decl(Token *def, Variable *var) : def(def), enumerator(nullptr), function(nullptr), scope(nullptr), var(var) {} + Decl(Token *def, Function *function) : def(def), enumerator(nullptr), function(function), scope(nullptr), var(nullptr) {} + Decl(Token *def, Enumerator *enumerator) : def(def), enumerator(enumerator), function(nullptr), scope(nullptr), var(nullptr) {} + void ref(Token *tok) { + if (enumerator) + tok->enumerator(enumerator); + if (function) + tok->function(function); + if (var) { + tok->variable(var); + tok->varId(var->declarationId()); + } + } + Token *def; + Enumerator *enumerator; + Function *function; + Scope *scope; + Variable *var; + }; + + const Settings *mSettings = nullptr; + SymbolDatabase *mSymbolDatabase = nullptr; + + int enumValue = 0; + + void enumDecl(const std::string &addr, Token *nameToken, Enumerator *enumerator) { + Decl decl(nameToken, enumerator); + mDeclMap.insert(std::pair(addr, decl)); + nameToken->enumerator(enumerator); + notFound(addr); + } + + void funcDecl(const std::string &addr, Token *nameToken, Function *function) { + Decl decl(nameToken, function); + mDeclMap.insert(std::pair(addr, decl)); + nameToken->function(function); + notFound(addr); + } + + void scopeDecl(const std::string &addr, Scope *scope) { + Decl decl(scope); + mDeclMap.insert(std::pair(addr, decl)); + } + + void varDecl(const std::string &addr, Token *def, Variable *var) { + Decl decl(def, var); + mDeclMap.insert(std::pair(addr, decl)); + def->varId(++mVarId); + def->variable(var); + if (def->valueType()) + var->setValueType(*def->valueType()); + notFound(addr); + } + + void replaceVarDecl(const Variable *from, Variable *to) { + for (auto &it: mDeclMap) { + Decl &decl = it.second; + if (decl.var == from) + decl.var = to; + } + } + + void ref(const std::string &addr, Token *tok) { + auto it = mDeclMap.find(addr); + if (it != mDeclMap.end()) + it->second.ref(tok); + else + mNotFound[addr].push_back(tok); + } + + std::vector getVariableList() const { + std::vector ret; + ret.resize(mVarId + 1, nullptr); + for (auto it: mDeclMap) { + if (it.second.var) + ret[it.second.var->declarationId()] = it.second.var; + } + return ret; + } + + bool hasDecl(const std::string &addr) const { + return mDeclMap.find(addr) != mDeclMap.end(); + } + + const Scope *getScope(const std::string &addr) { + auto it = mDeclMap.find(addr); + return (it == mDeclMap.end() ? nullptr : it->second.scope); + } + + // "}" tokens that are not end-of-scope + std::set mNotScope; + + std::map scopeAccessControl; + private: + void notFound(const std::string &addr) { + auto it = mNotFound.find(addr); + if (it != mNotFound.end()) { + for (Token *reftok: it->second) + ref(addr, reftok); + mNotFound.erase(it); + } + } + + std::map mDeclMap; + std::map> mNotFound; + int mVarId = 0; + }; + + class AstNode; + using AstNodePtr = std::shared_ptr; + + class AstNode { + public: + AstNode(const std::string &nodeType, const std::string &ext, Data *data) + : nodeType(nodeType), mExtTokens(splitString(ext)), mData(data) + {} + std::string nodeType; + std::vector children; + + void setLocations(TokenList *tokenList, int file, int line, int col); + + void dumpAst(int num = 0, int indent = 0) const; + void createTokens1(TokenList *tokenList) { + //dumpAst(); + if (!tokenList->back()) + setLocations(tokenList, 0, 1, 1); + else + setLocations(tokenList, tokenList->back()->fileIndex(), tokenList->back()->linenr(), 1); + createTokens(tokenList); + if (nodeType == VarDecl || nodeType == RecordDecl || nodeType == TypedefDecl) + addtoken(tokenList, ";"); + mData->mNotScope.clear(); + } + + AstNodePtr getChild(int c) { + if (c >= children.size()) { + std::ostringstream err; + err << "ClangImport: AstNodePtr::getChild(" << c << ") out of bounds. children.size=" << children.size() << " " << nodeType; + for (const std::string &s: mExtTokens) + err << " " << s; + throw InternalError(nullptr, err.str()); + } + return children[c]; + } + private: + Token *createTokens(TokenList *tokenList); + Token *addtoken(TokenList *tokenList, const std::string &str, bool valueType=true); + const ::Type *addTypeTokens(TokenList *tokenList, const std::string &str, const Scope *scope = nullptr); + void addFullScopeNameTokens(TokenList *tokenList, const Scope *recordScope); + Scope *createScope(TokenList *tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def); + Scope *createScope(TokenList *tokenList, Scope::ScopeType scopeType, const std::vector &children2, const Token *def); + Token *createTokensCall(TokenList *tokenList); + void createTokensFunctionDecl(TokenList *tokenList); + void createTokensForCXXRecord(TokenList *tokenList); + Token *createTokensVarDecl(TokenList *tokenList); + std::string getSpelling() const; + std::string getType(int index = 0) const; + std::string getFullType(int index = 0) const; + bool isDefinition() const; + std::string getTemplateParameters() const; + const Scope *getNestedInScope(TokenList *tokenList); + void setValueType(Token *tok); + + int mFile = 0; + int mLine = 1; + int mCol = 1; + std::vector mExtTokens; + Data *mData; + }; +} + +std::string clangimport::AstNode::getSpelling() const +{ + if (nodeType == CompoundAssignOperator) { + int typeIndex = 1; + while (typeIndex < mExtTokens.size() && mExtTokens[typeIndex][0] != '\'') + typeIndex++; + // name is next quoted token + int nameIndex = typeIndex + 1; + while (nameIndex < mExtTokens.size() && mExtTokens[nameIndex][0] != '\'') + nameIndex++; + return (nameIndex < mExtTokens.size()) ? unquote(mExtTokens[nameIndex]) : ""; + } + + if (nodeType == UnaryExprOrTypeTraitExpr) { + int typeIndex = 1; + while (typeIndex < mExtTokens.size() && mExtTokens[typeIndex][0] != '\'') + typeIndex++; + int nameIndex = typeIndex + 1; + return (nameIndex < mExtTokens.size()) ? unquote(mExtTokens[nameIndex]) : ""; + } + + int typeIndex = mExtTokens.size() - 1; + if (nodeType == FunctionDecl || nodeType == CXXConstructorDecl || nodeType == CXXMethodDecl) { + while (typeIndex >= 0 && mExtTokens[typeIndex][0] != '\'') + typeIndex--; + if (typeIndex <= 0) + return ""; + } + if (nodeType == DeclRefExpr) { + while (typeIndex > 0 && std::isalpha(mExtTokens[typeIndex][0])) + typeIndex--; + if (typeIndex <= 0) + return ""; + } + const std::string &str = mExtTokens[typeIndex - 1]; + if (str.compare(0,4,"col:") == 0) + return ""; + if (str.compare(0,8,"= mExtTokens.size()) + return ""; + std::string type = mExtTokens[typeIndex]; + if (type.find("\':\'") != std::string::npos) { + if (index == 0) + type.erase(type.find("\':\'") + 1); + else + type.erase(0, type.find("\':\'") + 2); + } + return type; +} + +bool clangimport::AstNode::isDefinition() const +{ + return contains(mExtTokens, "definition"); +} + +std::string clangimport::AstNode::getTemplateParameters() const +{ + if (children.empty() || children[0]->nodeType != TemplateArgument) + return ""; + std::string templateParameters; + for (AstNodePtr child: children) { + if (child->nodeType == TemplateArgument) { + if (templateParameters.empty()) + templateParameters = "<"; + else + templateParameters += ","; + templateParameters += unquote(child->mExtTokens.back()); + } + } + return templateParameters + ">"; +} + +void clangimport::AstNode::dumpAst(int num, int indent) const +{ + (void)num; + std::cout << std::string(indent, ' ') << nodeType; + for (auto tok: mExtTokens) + std::cout << " " << tok; + std::cout << std::endl; + for (int c = 0; c < children.size(); ++c) { + if (children[c]) + children[c]->dumpAst(c, indent + 2); + else + std::cout << std::string(indent + 2, ' ') << "<<<>>>>" << std::endl; + } +} + +void clangimport::AstNode::setLocations(TokenList *tokenList, int file, int line, int col) +{ + for (const std::string &ext: mExtTokens) { + if (ext.compare(0,5,"appendFileIfNew(ext.substr(1, sep1 - 1)); + line = MathLib::toLongNumber(ext.substr(sep1+1, sep2-sep1)); + } + } + mFile = file; + mLine = line; + mCol = col; + for (auto child: children) { + if (child) + child->setLocations(tokenList, file, line, col); + } +} + +Token *clangimport::AstNode::addtoken(TokenList *tokenList, const std::string &str, bool valueType) +{ + const Scope *scope = getNestedInScope(tokenList); + tokenList->addtoken(str, mLine, mCol, mFile); + tokenList->back()->scope(scope); + if (valueType) + setValueType(tokenList->back()); + return tokenList->back(); +} + +const ::Type * clangimport::AstNode::addTypeTokens(TokenList *tokenList, const std::string &str, const Scope *scope) +{ + if (str.find("\':\'") != std::string::npos) { + return addTypeTokens(tokenList, str.substr(0, str.find("\':\'") + 1), scope); + } + + if (str.compare(0, 16, "'enum (anonymous") == 0) + return nullptr; + + std::string type; + if (str.find(" (") != std::string::npos) { + if (str.find("<") != std::string::npos) + type = str.substr(1, str.find("<")) + "...>"; + else + type = str.substr(1,str.find(" (")-1); + } else + type = unquote(str); + + if (type.find("(*)(") != std::string::npos) { + type.erase(type.find("(*)(")); + type += "*"; + } + if (type.find("(") != std::string::npos) + type.erase(type.find("(")); + + std::stack lpar; + for (const std::string &s: splitString(type)) { + Token *tok = addtoken(tokenList, s, false); + if (tok->str() == "(") + lpar.push(tok); + else if (tok->str() == ")") { + Token::createMutualLinks(tok, lpar.top()); + lpar.pop(); + } + } + + // Set Type + if (!scope) { + scope = tokenList->back() ? tokenList->back()->scope() : nullptr; + if (!scope) + return nullptr; + } + for (const Token *typeToken = tokenList->back(); Token::Match(typeToken, "&|*|%name%"); typeToken = typeToken->previous()) { + if (!typeToken->isName()) + continue; + const ::Type *recordType = scope->check->findVariableType(scope, typeToken); + if (recordType) { + const_cast(typeToken)->type(recordType); + return recordType; + } + } + return nullptr; +} + +void clangimport::AstNode::addFullScopeNameTokens(TokenList *tokenList, const Scope *recordScope) +{ + if (!recordScope) + return; + std::list scopes; + while (recordScope && recordScope != tokenList->back()->scope() && !recordScope->isExecutable()) { + scopes.push_front(recordScope); + recordScope = recordScope->nestedIn; + } + for (const Scope *s: scopes) { + if (!s->className.empty()) { + addtoken(tokenList, s->className); + addtoken(tokenList, "::"); + } + } +} + +const Scope *clangimport::AstNode::getNestedInScope(TokenList *tokenList) +{ + if (!tokenList->back()) + return &mData->mSymbolDatabase->scopeList.front(); + if (tokenList->back()->str() == "}" && mData->mNotScope.find(tokenList->back()) == mData->mNotScope.end()) + return tokenList->back()->scope()->nestedIn; + return tokenList->back()->scope(); +} + +void clangimport::AstNode::setValueType(Token *tok) +{ + for (int i = 0; i < 2; i++) { + const std::string &type = getType(i); + + if (type.find("<") != std::string::npos) + // TODO + continue; + + TokenList decl(nullptr); + addTypeTokens(&decl, type, tok->scope()); + if (!decl.front()) + break; + + ValueType valueType = ValueType::parseDecl(decl.front(), mData->mSettings); + if (valueType.type != ValueType::Type::UNKNOWN_TYPE) { + tok->setValueType(new ValueType(valueType)); + break; + } + } +} + +Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def) +{ + std::vector children2{astNode}; + return createScope(tokenList, scopeType, children2, def); +} + +Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType scopeType, const std::vector & children2, const Token *def) +{ + SymbolDatabase *symbolDatabase = mData->mSymbolDatabase; + + Scope *nestedIn = const_cast(getNestedInScope(tokenList)); + + symbolDatabase->scopeList.push_back(Scope(nullptr, nullptr, nestedIn)); + Scope *scope = &symbolDatabase->scopeList.back(); + if (scopeType == Scope::ScopeType::eEnum) + scope->enumeratorList.reserve(children2.size()); + nestedIn->nestedList.push_back(scope); + scope->type = scopeType; + scope->classDef = def; + scope->check = nestedIn->check; + if (Token::Match(def, "if|for|while (")) { + std::map replaceVar; + for (const Token *vartok = def->tokAt(2); vartok; vartok = vartok->next()) { + if (!vartok->variable()) + continue; + if (vartok->variable()->nameToken() == vartok) { + const Variable *from = vartok->variable(); + scope->varlist.emplace_back(*from, scope); + Variable *to = &scope->varlist.back(); + replaceVar[from] = to; + mData->replaceVarDecl(from, to); + } + if (replaceVar.find(vartok->variable()) != replaceVar.end()) + const_cast(vartok)->variable(replaceVar[vartok->variable()]); + } + std::list &varlist = const_cast(def->scope())->varlist; + for (std::list::iterator var = varlist.begin(); var != varlist.end();) { + if (replaceVar.find(&(*var)) != replaceVar.end()) + varlist.erase(var++); + else + ++var; + } + } + scope->bodyStart = addtoken(tokenList, "{"); + tokenList->back()->scope(scope); + mData->scopeAccessControl[scope] = scope->defaultAccess(); + if (!children2.empty()) { + for (AstNodePtr astNode: children2) { + if (astNode->nodeType == "VisibilityAttr") + continue; + if (astNode->nodeType == AccessSpecDecl) { + if (contains(astNode->mExtTokens, "private")) + mData->scopeAccessControl[scope] = AccessControl::Private; + else if (contains(astNode->mExtTokens, "protected")) + mData->scopeAccessControl[scope] = AccessControl::Protected; + else if (contains(astNode->mExtTokens, "public")) + mData->scopeAccessControl[scope] = AccessControl::Public; + continue; + } + astNode->createTokens(tokenList); + if (scopeType == Scope::ScopeType::eEnum) + astNode->addtoken(tokenList, ","); + else if (!Token::Match(tokenList->back(), "[;{}]")) + astNode->addtoken(tokenList, ";"); + } + } + scope->bodyEnd = addtoken(tokenList, "}"); + Token::createMutualLinks(const_cast(scope->bodyStart), const_cast(scope->bodyEnd)); + mData->scopeAccessControl.erase(scope); + return scope; +} + +Token *clangimport::AstNode::createTokens(TokenList *tokenList) +{ + if (nodeType == ArraySubscriptExpr) { + Token *array = getChild(0)->createTokens(tokenList); + Token *bracket1 = addtoken(tokenList, "["); + Token *index = children[1]->createTokens(tokenList); + Token *bracket2 = addtoken(tokenList, "]"); + bracket1->astOperand1(array); + bracket1->astOperand2(index); + bracket1->link(bracket2); + bracket2->link(bracket1); + return bracket1; + } + if (nodeType == BinaryOperator) { + Token *tok1 = getChild(0)->createTokens(tokenList); + Token *binop = addtoken(tokenList, unquote(mExtTokens.back())); + Token *tok2 = children[1]->createTokens(tokenList); + binop->astOperand1(tok1); + binop->astOperand2(tok2); + return binop; + } + if (nodeType == BreakStmt) + return addtoken(tokenList, "break"); + if (nodeType == CharacterLiteral) { + int c = MathLib::toLongNumber(mExtTokens.back()); + if (c == 0) + return addtoken(tokenList, "\'\\0\'"); + if (c == '\r') + return addtoken(tokenList, "\'\\r\'"); + if (c == '\n') + return addtoken(tokenList, "\'\\n\'"); + if (c == '\t') + return addtoken(tokenList, "\'\\t\'"); + if (c == '\\') + return addtoken(tokenList, "\'\\\\\'"); + if (c < ' ' || c >= 0x80) { + std::ostringstream hex; + hex << std::hex << ((c>>4) & 0xf) << (c&0xf); + return addtoken(tokenList, "\'\\x" + hex.str() + "\'"); + } + return addtoken(tokenList, std::string("\'") + char(c) + std::string("\'")); + } + if (nodeType == CallExpr) + return createTokensCall(tokenList); + if (nodeType == CaseStmt) { + Token *caseToken = addtoken(tokenList, "case"); + Token *exprToken = getChild(0)->createTokens(tokenList); + caseToken->astOperand1(exprToken); + addtoken(tokenList, ":"); + children.back()->createTokens(tokenList); + return nullptr; + } + if (nodeType == ClassTemplateDecl) { + for (AstNodePtr child: children) { + if (child->nodeType == ClassTemplateSpecializationDecl) + child->createTokens(tokenList); + } + return nullptr; + } + if (nodeType == ClassTemplateSpecializationDecl) { + createTokensForCXXRecord(tokenList); + return nullptr; + } + if (nodeType == ConditionalOperator) { + Token *expr1 = getChild(0)->createTokens(tokenList); + Token *tok1 = addtoken(tokenList, "?"); + Token *expr2 = children[1]->createTokens(tokenList); + Token *tok2 = addtoken(tokenList, ":"); + Token *expr3 = children[2]->createTokens(tokenList); + tok2->astOperand1(expr2); + tok2->astOperand2(expr3); + tok1->astOperand1(expr1); + tok1->astOperand2(tok2); + return tok1; + } + if (nodeType == CompoundAssignOperator) { + Token *lhs = getChild(0)->createTokens(tokenList); + Token *assign = addtoken(tokenList, getSpelling()); + Token *rhs = children[1]->createTokens(tokenList); + assign->astOperand1(lhs); + assign->astOperand2(rhs); + return assign; + } + if (nodeType == CompoundStmt) { + for (AstNodePtr child: children) { + child->createTokens(tokenList); + if (!Token::Match(tokenList->back(), "[;{}]")) + child->addtoken(tokenList, ";"); + } + return nullptr; + } + if (nodeType == ConstantExpr) + return children.back()->createTokens(tokenList); + if (nodeType == ContinueStmt) + return addtoken(tokenList, "continue"); + if (nodeType == CStyleCastExpr) { + Token *par1 = addtoken(tokenList, "("); + addTypeTokens(tokenList, '\'' + getType() + '\''); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(getChild(0)->createTokens(tokenList)); + return par1; + } + if (nodeType == CXXBindTemporaryExpr) + return getChild(0)->createTokens(tokenList); + if (nodeType == CXXBoolLiteralExpr) { + addtoken(tokenList, mExtTokens.back()); + tokenList->back()->setValueType(new ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0)); + return tokenList->back(); + } + if (nodeType == CXXConstructExpr) { + if (!children.empty()) + return getChild(0)->createTokens(tokenList); + addTypeTokens(tokenList, '\'' + getType() + '\''); + Token *type = tokenList->back(); + Token *par1 = addtoken(tokenList, "("); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(type); + return par1; + } + if (nodeType == CXXConstructorDecl) { + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == CXXDeleteExpr) { + addtoken(tokenList, "delete"); + getChild(0)->createTokens(tokenList); + return nullptr; + } + if (nodeType == CXXDestructorDecl) { + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == CXXForRangeStmt) { + Token *forToken = addtoken(tokenList, "for"); + Token *par1 = addtoken(tokenList, "("); + AstNodePtr varDecl; + if (children[6]->nodeType == DeclStmt) + varDecl = getChild(6)->getChild(0); + else + varDecl = getChild(5)->getChild(0); + varDecl->mExtTokens.pop_back(); + varDecl->children.clear(); + Token *expr1 = varDecl->createTokens(tokenList); + Token *colon = addtoken(tokenList, ":"); + AstNodePtr range; + for (int i = 0; i < 2; i++) { + if (children[i] && children[i]->nodeType == DeclStmt && children[i]->getChild(0)->nodeType == VarDecl) { + range = children[i]->getChild(0)->getChild(0); + break; + } + } + if (!range) + throw InternalError(tokenList->back(), "Failed to import CXXForRangeStmt. Range?"); + Token *expr2 = range->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + + par1->link(par2); + par2->link(par1); + + colon->astOperand1(expr1); + colon->astOperand2(expr2); + par1->astOperand1(forToken); + par1->astOperand2(colon); + + createScope(tokenList, Scope::ScopeType::eFor, children.back(), forToken); + return nullptr; + } + if (nodeType == CXXMethodDecl) { + for (int i = 0; i+1 < mExtTokens.size(); ++i) { + if (mExtTokens[i] == "prev" && !mData->hasDecl(mExtTokens[i+1])) + return nullptr; + } + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == CXXMemberCallExpr) + return createTokensCall(tokenList); + if (nodeType == CXXNewExpr) { + Token *newtok = addtoken(tokenList, "new"); + if (children.size() == 1 && getChild(0)->nodeType == CXXConstructExpr) { + newtok->astOperand1(getChild(0)->createTokens(tokenList)); + return newtok; + } + std::string type = getType(); + if (type.find("*") != std::string::npos) + type = type.erase(type.rfind("*")); + addTypeTokens(tokenList, type); + if (!children.empty()) { + Token *bracket1 = addtoken(tokenList, "["); + getChild(0)->createTokens(tokenList); + Token *bracket2 = addtoken(tokenList, "]"); + bracket1->link(bracket2); + bracket2->link(bracket1); + } + return newtok; + } + if (nodeType == CXXNullPtrLiteralExpr) + return addtoken(tokenList, "nullptr"); + if (nodeType == CXXOperatorCallExpr) + return createTokensCall(tokenList); + if (nodeType == CXXRecordDecl) { + createTokensForCXXRecord(tokenList); + return nullptr; + } + if (nodeType == CXXStaticCastExpr || nodeType == CXXFunctionalCastExpr) { + Token *cast = addtoken(tokenList, getSpelling()); + Token *par1 = addtoken(tokenList, "("); + Token *expr = getChild(0)->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(cast); + par1->astOperand2(expr); + setValueType(par1); + return par1; + } + if (nodeType == CXXStdInitializerListExpr) + return getChild(0)->createTokens(tokenList); + if (nodeType == CXXTemporaryObjectExpr && !children.empty()) + return getChild(0)->createTokens(tokenList); + if (nodeType == CXXThisExpr) + return addtoken(tokenList, "this"); + if (nodeType == CXXThrowExpr) { + Token *t = addtoken(tokenList, "throw"); + t->astOperand1(getChild(0)->createTokens(tokenList)); + return t; + } + if (nodeType == DeclRefExpr) { + int addrIndex = mExtTokens.size() - 1; + while (addrIndex > 1 && mExtTokens[addrIndex].compare(0,2,"0x") != 0) + --addrIndex; + const std::string addr = mExtTokens[addrIndex]; + std::string name = unquote(getSpelling()); + Token *reftok = addtoken(tokenList, name.empty() ? "" : name); + mData->ref(addr, reftok); + return reftok; + } + if (nodeType == DeclStmt) + return getChild(0)->createTokens(tokenList); + if (nodeType == DefaultStmt) { + addtoken(tokenList, "default"); + addtoken(tokenList, ":"); + children.back()->createTokens(tokenList); + return nullptr; + } + if (nodeType == DoStmt) { + addtoken(tokenList, "do"); + createScope(tokenList, Scope::ScopeType::eDo, getChild(0), tokenList->back()); + Token *tok1 = addtoken(tokenList, "while"); + Token *par1 = addtoken(tokenList, "("); + Token *expr = children[1]->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(tok1); + par1->astOperand2(expr); + return nullptr; + } + if (nodeType == EnumConstantDecl) { + Token *nameToken = addtoken(tokenList, getSpelling()); + Scope *scope = const_cast(nameToken->scope()); + scope->enumeratorList.push_back(Enumerator(nameToken->scope())); + Enumerator *e = &scope->enumeratorList.back(); + e->name = nameToken; + e->value = mData->enumValue++; + e->value_known = true; + mData->enumDecl(mExtTokens.front(), nameToken, e); + return nameToken; + } + if (nodeType == EnumDecl) { + int colIndex = mExtTokens.size() - 1; + while (colIndex > 0 && mExtTokens[colIndex].compare(0,4,"col:") != 0 && mExtTokens[colIndex].compare(0,5,"line:") != 0) + --colIndex; + if (colIndex == 0) + return nullptr; + + mData->enumValue = 0; + Token *enumtok = addtoken(tokenList, "enum"); + Token *nametok = nullptr; + { + int nameIndex = mExtTokens.size() - 1; + while (nameIndex > colIndex && mExtTokens[nameIndex][0] == '\'') + --nameIndex; + if (nameIndex > colIndex) + nametok = addtoken(tokenList, mExtTokens[nameIndex]); + if (mExtTokens.back()[0] == '\'') { + addtoken(tokenList, ":"); + addTypeTokens(tokenList, mExtTokens.back()); + } + } + Scope *enumscope = createScope(tokenList, Scope::ScopeType::eEnum, children, enumtok); + if (nametok) + enumscope->className = nametok->str(); + if (enumscope->bodyEnd && Token::simpleMatch(enumscope->bodyEnd->previous(), ", }")) + const_cast(enumscope->bodyEnd)->deletePrevious(); + + // Create enum type + mData->mSymbolDatabase->typeList.push_back(Type(enumtok, enumscope, enumtok->scope())); + enumscope->definedType = &mData->mSymbolDatabase->typeList.back(); + if (nametok) + const_cast(enumtok->scope())->definedTypesMap[nametok->str()] = enumscope->definedType; + + return nullptr; + } + if (nodeType == ExprWithCleanups) + return getChild(0)->createTokens(tokenList); + if (nodeType == FieldDecl) + return createTokensVarDecl(tokenList); + if (nodeType == FloatingLiteral) + return addtoken(tokenList, mExtTokens.back()); + if (nodeType == ForStmt) { + Token *forToken = addtoken(tokenList, "for"); + Token *par1 = addtoken(tokenList, "("); + Token *expr1 = getChild(0) ? children[0]->createTokens(tokenList) : nullptr; + Token *sep1 = addtoken(tokenList, ";"); + Token *expr2 = children[2] ? children[2]->createTokens(tokenList) : nullptr; + Token *sep2 = addtoken(tokenList, ";"); + Token *expr3 = children[3] ? children[3]->createTokens(tokenList) : nullptr; + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(forToken); + par1->astOperand2(sep1); + sep1->astOperand1(expr1); + sep1->astOperand2(sep2); + sep2->astOperand1(expr2); + sep2->astOperand2(expr3); + createScope(tokenList, Scope::ScopeType::eFor, children[4], forToken); + return nullptr; + } + if (nodeType == FunctionDecl) { + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == FunctionTemplateDecl) { + bool first = true; + for (AstNodePtr child: children) { + if (child->nodeType == FunctionDecl) { + if (!first) + child->createTokens(tokenList); + first = false; + } + } + return nullptr; + } + if (nodeType == GotoStmt) { + addtoken(tokenList, "goto"); + addtoken(tokenList, unquote(mExtTokens[mExtTokens.size() - 2])); + addtoken(tokenList, ";"); + return nullptr; + } + if (nodeType == IfStmt) { + AstNodePtr cond; + AstNodePtr thenCode; + AstNodePtr elseCode; + if (children.size() == 2) { + cond = children[children.size() - 2]; + thenCode = children[children.size() - 1]; + } else { + cond = children[children.size() - 3]; + thenCode = children[children.size() - 2]; + elseCode = children[children.size() - 1]; + } + + Token *iftok = addtoken(tokenList, "if"); + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(iftok); + par1->astOperand2(cond->createTokens(tokenList)); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + createScope(tokenList, Scope::ScopeType::eIf, thenCode, iftok); + if (elseCode) { + elseCode->addtoken(tokenList, "else"); + createScope(tokenList, Scope::ScopeType::eElse, elseCode, tokenList->back()); + } + return nullptr; + } + if (nodeType == ImplicitCastExpr) { + Token *expr = getChild(0)->createTokens(tokenList); + if (!expr->valueType() || contains(mExtTokens, "")) + setValueType(expr); + return expr; + } + if (nodeType == InitListExpr) { + const Scope *scope = tokenList->back()->scope(); + Token *start = addtoken(tokenList, "{"); + start->scope(scope); + for (AstNodePtr child: children) { + if (tokenList->back()->str() != "{") + addtoken(tokenList, ","); + child->createTokens(tokenList); + } + Token *end = addtoken(tokenList, "}"); + end->scope(scope); + start->link(end); + end->link(start); + mData->mNotScope.insert(end); + return start; + } + if (nodeType == IntegerLiteral) + return addtoken(tokenList, mExtTokens.back()); + if (nodeType == LabelStmt) { + addtoken(tokenList, unquote(mExtTokens.back())); + addtoken(tokenList, ":"); + for (auto child: children) + child->createTokens(tokenList); + return nullptr; + } + if (nodeType == LinkageSpecDecl) + return nullptr; + if (nodeType == MaterializeTemporaryExpr) + return getChild(0)->createTokens(tokenList); + if (nodeType == MemberExpr) { + Token *s = getChild(0)->createTokens(tokenList); + Token *dot = addtoken(tokenList, "."); + std::string memberName = getSpelling(); + if (memberName.compare(0, 2, "->") == 0) { + dot->originalName("->"); + memberName = memberName.substr(2); + } else if (memberName.compare(0, 1, ".") == 0) { + memberName = memberName.substr(1); + } + if (memberName.empty()) + memberName = ""; + Token *member = addtoken(tokenList, memberName); + mData->ref(mExtTokens.back(), member); + dot->astOperand1(s); + dot->astOperand2(member); + return dot; + } + if (nodeType == NamespaceDecl) { + if (children.empty()) + return nullptr; + Token *defToken = addtoken(tokenList, "namespace"); + const std::string &s = mExtTokens[mExtTokens.size() - 2]; + Token *nameToken = (s.compare(0,4,"col:")==0 || s.compare(0,5,"line:")==0) ? + addtoken(tokenList, mExtTokens.back()) : nullptr; + Scope *scope = createScope(tokenList, Scope::ScopeType::eNamespace, children, defToken); + if (nameToken) + scope->className = nameToken->str(); + return nullptr; + } + if (nodeType == NullStmt) + return addtoken(tokenList, ";"); + if (nodeType == ParenExpr) { + Token *par1 = addtoken(tokenList, "("); + Token *expr = getChild(0)->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + return expr; + } + if (nodeType == RecordDecl) { + const Token *classDef = addtoken(tokenList, "struct"); + const std::string &recordName = getSpelling(); + if (!recordName.empty()) + addtoken(tokenList, getSpelling()); + if (!isDefinition()) { + addtoken(tokenList, ";"); + return nullptr; + } + + Scope *recordScope = createScope(tokenList, Scope::ScopeType::eStruct, children, classDef); + mData->mSymbolDatabase->typeList.push_back(Type(classDef, recordScope, classDef->scope())); + recordScope->definedType = &mData->mSymbolDatabase->typeList.back(); + if (!recordName.empty()) { + recordScope->className = recordName; + const_cast(classDef->scope())->definedTypesMap[recordName] = recordScope->definedType; + } + + return nullptr; + } + if (nodeType == ReturnStmt) { + Token *tok1 = addtoken(tokenList, "return"); + if (!children.empty()) { + getChild(0)->setValueType(tok1); + tok1->astOperand1(getChild(0)->createTokens(tokenList)); + } + return tok1; + } + if (nodeType == StringLiteral) + return addtoken(tokenList, mExtTokens.back()); + if (nodeType == SwitchStmt) { + Token *tok1 = addtoken(tokenList, "switch"); + Token *par1 = addtoken(tokenList, "("); + Token *expr = children[children.size() - 2]->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(tok1); + par1->astOperand2(expr); + createScope(tokenList, Scope::ScopeType::eSwitch, children.back(), tok1); + return nullptr; + } + if (nodeType == TypedefDecl) { + addtoken(tokenList, "typedef"); + addTypeTokens(tokenList, getType()); + return addtoken(tokenList, getSpelling()); + } + if (nodeType == UnaryOperator) { + int index = (int)mExtTokens.size() - 1; + while (index > 0 && mExtTokens[index][0] != '\'') + --index; + Token *unop = addtoken(tokenList, unquote(mExtTokens[index])); + unop->astOperand1(getChild(0)->createTokens(tokenList)); + return unop; + } + if (nodeType == UnaryExprOrTypeTraitExpr) { + Token *tok1 = addtoken(tokenList, getSpelling()); + Token *par1 = addtoken(tokenList, "("); + if (children.empty()) + addTypeTokens(tokenList, mExtTokens.back()); + else { + AstNodePtr child = getChild(0); + if (child && child->nodeType == ParenExpr) + child = child->getChild(0); + Token *expr = child->createTokens(tokenList); + child->setValueType(expr); + par1->astOperand2(expr); + } + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(tok1); + par1->astOperand2(par1->next()); + setValueType(par1); + return par1; + } + if (nodeType == VarDecl) + return createTokensVarDecl(tokenList); + if (nodeType == WhileStmt) { + AstNodePtr cond = children[children.size() - 2]; + AstNodePtr body = children.back(); + Token *whiletok = addtoken(tokenList, "while"); + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(whiletok); + par1->astOperand2(cond->createTokens(tokenList)); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + createScope(tokenList, Scope::ScopeType::eWhile, body, whiletok); + return nullptr; + } + return addtoken(tokenList, "?" + nodeType + "?"); +} + +Token * clangimport::AstNode::createTokensCall(TokenList *tokenList) +{ + int firstParam; + Token *f; + if (nodeType == CXXOperatorCallExpr) { + firstParam = 2; + Token *obj = getChild(1)->createTokens(tokenList); + Token *dot = addtoken(tokenList, "."); + Token *op = getChild(0)->createTokens(tokenList); + dot->astOperand1(obj); + dot->astOperand2(op); + f = dot; + } else { + firstParam = 1; + f = getChild(0)->createTokens(tokenList); + } + f->setValueType(nullptr); + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(f); + int args = 0; + while (args < children.size() && children[args]->nodeType != CXXDefaultArgExpr) + args++; + Token *child = nullptr; + for (int c = firstParam; c < args; ++c) { + if (child) { + Token *comma = addtoken(tokenList, ","); + comma->setValueType(nullptr); + comma->astOperand1(child); + comma->astOperand2(children[c]->createTokens(tokenList)); + child = comma; + } else { + child = children[c]->createTokens(tokenList); + } + } + par1->astOperand2(child); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + return par1; +} + +void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList) +{ + const bool prev = contains(mExtTokens, "prev"); + const bool hasBody = !children.empty() && children.back()->nodeType == CompoundStmt; + const bool isStatic = contains(mExtTokens, "static"); + const bool isInline = contains(mExtTokens, "inline"); + + const Token *startToken = nullptr; + + SymbolDatabase *symbolDatabase = mData->mSymbolDatabase; + if (nodeType != CXXConstructorDecl && nodeType != CXXDestructorDecl) { + if (isStatic) + addtoken(tokenList, "static"); + if (isInline) + addtoken(tokenList, "inline"); + const Token * const before = tokenList->back(); + addTypeTokens(tokenList, '\'' + getType() + '\''); + startToken = before ? before->next() : tokenList->front(); + } + + if (mExtTokens.size() > 4 && mExtTokens[1] == "parent") + addFullScopeNameTokens(tokenList, mData->getScope(mExtTokens[2])); + + Token *nameToken = addtoken(tokenList, getSpelling() + getTemplateParameters()); + Scope *nestedIn = const_cast(nameToken->scope()); + + if (prev) { + const std::string addr = *(std::find(mExtTokens.begin(), mExtTokens.end(), "prev") + 1); + mData->ref(addr, nameToken); + } + if (!nameToken->function()) { + nestedIn->functionList.push_back(Function(nameToken, unquote(getFullType()))); + mData->funcDecl(mExtTokens.front(), nameToken, &nestedIn->functionList.back()); + if (nodeType == CXXConstructorDecl) + nestedIn->functionList.back().type = Function::Type::eConstructor; + else if (nodeType == CXXDestructorDecl) + nestedIn->functionList.back().type = Function::Type::eDestructor; + else + nestedIn->functionList.back().retDef = startToken; + } + + Function * const function = const_cast(nameToken->function()); + + if (!prev) { + auto accessControl = mData->scopeAccessControl.find(tokenList->back()->scope()); + if (accessControl != mData->scopeAccessControl.end()) + function->access = accessControl->second; + } + + Scope *scope = nullptr; + if (hasBody) { + symbolDatabase->scopeList.push_back(Scope(nullptr, nullptr, nestedIn)); + scope = &symbolDatabase->scopeList.back(); + scope->check = symbolDatabase; + scope->function = function; + scope->classDef = nameToken; + scope->type = Scope::ScopeType::eFunction; + scope->className = nameToken->str(); + nestedIn->nestedList.push_back(scope); + function->hasBody(true); + function->functionScope = scope; + } + + Token *par1 = addtoken(tokenList, "("); + if (!function->arg) + function->arg = par1; + function->token = nameToken; + if (!function->nestedIn) + function->nestedIn = nestedIn; + function->argDef = par1; + // Function arguments + for (int i = 0; i < children.size(); ++i) { + AstNodePtr child = children[i]; + if (child->nodeType != ParmVarDecl) + continue; + if (tokenList->back() != par1) + addtoken(tokenList, ","); + const Type *recordType = addTypeTokens(tokenList, child->mExtTokens.back(), nestedIn); + const Token *typeEndToken = tokenList->back(); + const std::string spelling = child->getSpelling(); + Token *vartok = nullptr; + if (!spelling.empty()) + vartok = child->addtoken(tokenList, spelling); + if (!prev) { + function->argumentList.push_back(Variable(vartok, child->getType(), nullptr, typeEndToken, i, AccessControl::Argument, recordType, scope)); + if (vartok) { + const std::string addr = child->mExtTokens[0]; + mData->varDecl(addr, vartok, &function->argumentList.back()); + } + } else if (vartok) { + const std::string addr = child->mExtTokens[0]; + mData->ref(addr, vartok); + } + } + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + + if (function->isConst()) + addtoken(tokenList, "const"); + + // Function body + if (hasBody) { + symbolDatabase->functionScopes.push_back(scope); + Token *bodyStart = addtoken(tokenList, "{"); + bodyStart->scope(scope); + children.back()->createTokens(tokenList); + Token *bodyEnd = addtoken(tokenList, "}"); + scope->bodyStart = bodyStart; + scope->bodyEnd = bodyEnd; + bodyStart->link(bodyEnd); + bodyEnd->link(bodyStart); + } else { + if (nodeType == CXXConstructorDecl && contains(mExtTokens, "default")) { + addtoken(tokenList, "="); + addtoken(tokenList, "default"); + } + + addtoken(tokenList, ";"); + } +} + +void clangimport::AstNode::createTokensForCXXRecord(TokenList *tokenList) +{ + bool isStruct = contains(mExtTokens, "struct"); + Token * const classToken = addtoken(tokenList, isStruct ? "struct" : "class"); + std::string className; + if (mExtTokens[mExtTokens.size() - 2] == (isStruct?"struct":"class")) + className = mExtTokens.back(); + else + className = mExtTokens[mExtTokens.size() - 2]; + className += getTemplateParameters(); + /*Token *nameToken =*/ addtoken(tokenList, className); + // base classes + bool firstBase = true; + for (AstNodePtr child: children) { + if (child->nodeType == "public" || child->nodeType == "protected" || child->nodeType == "private") { + addtoken(tokenList, firstBase ? ":" : ","); + addtoken(tokenList, child->nodeType); + addtoken(tokenList, unquote(child->mExtTokens.back())); + firstBase = false; + } + } + // definition + if (isDefinition()) { + std::vector children2; + for (AstNodePtr child: children) { + if (child->nodeType == CXXConstructorDecl || + child->nodeType == CXXDestructorDecl || + child->nodeType == CXXMethodDecl || + child->nodeType == FieldDecl || + child->nodeType == VarDecl || + child->nodeType == AccessSpecDecl || + child->nodeType == TypedefDecl) + children2.push_back(child); + } + Scope *scope = createScope(tokenList, isStruct ? Scope::ScopeType::eStruct : Scope::ScopeType::eClass, children2, classToken); + const std::string addr = mExtTokens[0]; + mData->scopeDecl(addr, scope); + scope->className = className; + mData->mSymbolDatabase->typeList.push_back(Type(classToken, scope, classToken->scope())); + scope->definedType = &mData->mSymbolDatabase->typeList.back(); + const_cast(classToken->scope())->definedTypesMap[className] = scope->definedType; + } + addtoken(tokenList, ";"); + const_cast(tokenList->back())->scope(classToken->scope()); +} + +Token * clangimport::AstNode::createTokensVarDecl(TokenList *tokenList) +{ + const std::string addr = mExtTokens.front(); + if (contains(mExtTokens, "static")) + addtoken(tokenList, "static"); + int typeIndex = mExtTokens.size() - 1; + while (typeIndex > 1 && std::isalpha(mExtTokens[typeIndex][0])) + typeIndex--; + const std::string type = mExtTokens[typeIndex]; + const std::string name = mExtTokens[typeIndex - 1]; + const Token *startToken = tokenList->back(); + const ::Type *recordType = addTypeTokens(tokenList, type); + if (!startToken) + startToken = tokenList->front(); + else if (startToken->str() != "static") + startToken = startToken->next(); + Token *vartok1 = addtoken(tokenList, name); + Scope *scope = const_cast(tokenList->back()->scope()); + scope->varlist.push_back(Variable(vartok1, unquote(type), startToken, vartok1->previous(), 0, scope->defaultAccess(), recordType, scope)); + mData->varDecl(addr, vartok1, &scope->varlist.back()); + if (mExtTokens.back() == "cinit" && !children.empty()) { + Token *eq = addtoken(tokenList, "="); + eq->astOperand1(vartok1); + eq->astOperand2(children.back()->createTokens(tokenList)); + return eq; + } else if (mExtTokens.back() == "callinit") { + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(vartok1); + par1->astOperand2(getChild(0)->createTokens(tokenList)); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + return par1; + } else if (mExtTokens.back() == "listinit") { + return getChild(0)->createTokens(tokenList); + } + return vartok1; +} + +static void setTypes(TokenList *tokenList) +{ + for (Token *tok = tokenList->front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof (")) { + for (Token *typeToken = tok->tokAt(2); typeToken->str() != ")"; typeToken = typeToken->next()) { + if (typeToken->type()) + continue; + typeToken->type(typeToken->scope()->findType(typeToken->str())); + } + } + } +} + +static void setValues(Tokenizer *tokenizer, SymbolDatabase *symbolDatabase) +{ + const Settings * const settings = tokenizer->getSettings(); + + for (Scope &scope: symbolDatabase->scopeList) { + if (!scope.definedType) + continue; + + int typeSize = 0; + for (const Variable &var: scope.varlist) { + int mul = 1; + for (const auto &dim: var.dimensions()) { + mul *= dim.num; + } + if (var.valueType()) + typeSize += mul * var.valueType()->typeSize(*settings, true); + } + scope.definedType->sizeOf = typeSize; + } + + for (Token *tok = const_cast(tokenizer->tokens()); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof (")) { + ValueType vt = ValueType::parseDecl(tok->tokAt(2), settings); + int sz = vt.typeSize(*settings, true); + if (sz <= 0) + continue; + long long mul = 1; + for (Token *arrtok = tok->linkAt(1)->previous(); arrtok; arrtok = arrtok->previous()) { + const std::string &a = arrtok->str(); + if (a.size() > 2 && a[0] == '[' && a.back() == ']') + mul *= std::atoi(a.substr(1).c_str()); + else + break; + } + ValueFlow::Value v(mul * sz); + v.setKnown(); + tok->next()->addValue(v); + } + } +} + +void clangimport::parseClangAstDump(Tokenizer *tokenizer, std::istream &f) +{ + TokenList *tokenList = &tokenizer->list; + + tokenizer->createSymbolDatabase(); + SymbolDatabase *symbolDatabase = const_cast(tokenizer->getSymbolDatabase()); + symbolDatabase->scopeList.push_back(Scope(nullptr, nullptr, nullptr)); + symbolDatabase->scopeList.back().type = Scope::ScopeType::eGlobal; + symbolDatabase->scopeList.back().check = symbolDatabase; + + clangimport::Data data; + data.mSettings = tokenizer->getSettings(); + data.mSymbolDatabase = symbolDatabase; + std::string line; + std::vector tree; + while (std::getline(f,line)) { + const std::string::size_type pos1 = line.find("-"); + if (pos1 == std::string::npos) + continue; + if (!tree.empty() && line.substr(pos1) == "-<<>>") { + const int level = (pos1 - 1) / 2; + tree[level - 1]->children.push_back(nullptr); + continue; + } + const std::string::size_type pos2 = line.find(" ", pos1); + if (pos2 < pos1 + 4 || pos2 == std::string::npos) + continue; + const std::string nodeType = line.substr(pos1+1, pos2 - pos1 - 1); + const std::string ext = line.substr(pos2); + + if (pos1 == 1 && endsWith(nodeType, "Decl", 4)) { + if (!tree.empty()) + tree[0]->createTokens1(tokenList); + tree.clear(); + tree.push_back(std::make_shared(nodeType, ext, &data)); + continue; + } + + const int level = (pos1 - 1) / 2; + if (level == 0 || level > tree.size()) + continue; + + AstNodePtr newNode = std::make_shared(nodeType, ext, &data); + tree[level - 1]->children.push_back(newNode); + if (level >= tree.size()) + tree.push_back(newNode); + else + tree[level] = newNode; + } + + if (!tree.empty()) + tree[0]->createTokens1(tokenList); + + // Validation + for (const Token *tok = tokenList->front(); tok; tok = tok->next()) { + if (Token::Match(tok, "(|)|[|]|{|}") && !tok->link()) + throw InternalError(tok, "Token::link() is not set properly"); + } + + if (tokenList->front()) + tokenList->front()->assignIndexes(); + symbolDatabase->clangSetVariables(data.getVariableList()); + symbolDatabase->createSymbolDatabaseExprIds(); + tokenList->clangSetOrigFiles(); + setTypes(tokenList); + setValues(tokenizer, symbolDatabase); +} + diff --git a/lib/clangimport.h b/lib/clangimport.h new file mode 100644 index 000000000..b6e0e3cb0 --- /dev/null +++ b/lib/clangimport.h @@ -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 . + */ + + +//--------------------------------------------------------------------------- +#ifndef clangimportH +#define clangimportH +//--------------------------------------------------------------------------- + +#include + +class Tokenizer; + +namespace clangimport { + void parseClangAstDump(Tokenizer *tokenizer, std::istream &f); +} + +#endif diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index c36552af0..a96acf1cb 100644 --- a/lib/cppcheck.cpp +++ b/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 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 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(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 << "" << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + tokenizer.dump(fdump); + fdump << "" << std::endl; + fdump << "" << 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,7 +484,10 @@ unsigned int CppCheck::check(const ImportProject::FileSettings &fs) temp.mSettings = mSettings; if (!temp.mSettings.userDefines.empty()) temp.mSettings.userDefines += ';'; - temp.mSettings.userDefines += fs.cppcheckDefines(); + 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()); if (fs.standard.find("++") != std::string::npos) @@ -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); diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj index 44cb35f9d..2c40cfe10 100644 --- a/lib/cppcheck.vcxproj +++ b/lib/cppcheck.vcxproj @@ -75,6 +75,7 @@ + @@ -135,6 +136,7 @@ + @@ -570,4 +572,4 @@ xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y - + \ No newline at end of file diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 773d79cda..e2c525de0 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -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) diff --git a/lib/importproject.h b/lib/importproject.h index 8dcf1eaad..7f0b99779 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -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"; diff --git a/lib/lib.pri b/lib/lib.pri index 25049033d..164686bc6 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -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 \ diff --git a/lib/settings.cpp b/lib/settings.cpp index f02061faf..f4e16ce5a 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -36,6 +36,8 @@ Settings::Settings() checkHeaders(true), checkLibrary(false), checkUnusedTemplates(true), + clang(false), + clangExecutable("clang"), clangTidy(false), daca(false), debugBugHunting(false), diff --git a/lib/settings.h b/lib/settings.h index 7a0543773..682a7932d 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -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; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 4abb2e904..e2e2fba97 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1823,6 +1823,69 @@ void SymbolDatabase::validate() const //validateVariables(); } +void SymbolDatabase::clangSetVariables(const std::vector &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%|::|<")) { diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index da8a84550..6a20da32c 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -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 &variableList); void createSymbolDatabaseExprIds(); private: diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 6a1c6ff0a..f5ad39367 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -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) { diff --git a/lib/tokenlist.h b/lib/tokenlist.h index ca8857129..7b644f935 100644 --- a/lib/tokenlist.h +++ b/lib/tokenlist.h @@ -186,6 +186,8 @@ public: */ void simplifyStdType(); + void clangSetOrigFiles(); + bool isKeyword(const std::string &str) const; private: diff --git a/man/manual.md b/man/manual.md index 64cf7d48d..08e5495cf 100644 --- a/man/manual.md +++ b/man/manual.md @@ -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: diff --git a/test/cli/test-clang-import.py b/test/cli/test-clang-import.py new file mode 100644 index 000000000..c4af2ce09 --- /dev/null +++ b/test/cli/test-clang-import.py @@ -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(); }') + diff --git a/test/testclangimport.cpp b/test/testclangimport.cpp new file mode 100644 index 000000000..f5e5fcbd4 --- /dev/null +++ b/test/testclangimport.cpp @@ -0,0 +1,1303 @@ +// Cppcheck - A tool for static C/C++ code analysis +// Copyright (C) 2007-2021 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 . + +#include "clangimport.h" +#include "settings.h" +#include "symboldatabase.h" +#include "tokenize.h" +#include "testsuite.h" + + +class TestClangImport: public TestFixture { +public: + TestClangImport() + :TestFixture("TestClangImport") { + } + + +private: + void run() OVERRIDE { + TEST_CASE(breakStmt); + TEST_CASE(callExpr); + TEST_CASE(caseStmt1); + TEST_CASE(characterLiteral); + TEST_CASE(class1); + TEST_CASE(classTemplateDecl1); + TEST_CASE(classTemplateDecl2); + TEST_CASE(conditionalExpr); + TEST_CASE(compoundAssignOperator); + TEST_CASE(continueStmt); + TEST_CASE(cstyleCastExpr); + TEST_CASE(cxxBoolLiteralExpr); + TEST_CASE(cxxConstructorDecl1); + TEST_CASE(cxxConstructorDecl2); + TEST_CASE(cxxConstructExpr1); + TEST_CASE(cxxConstructExpr2); + TEST_CASE(cxxConstructExpr3); + TEST_CASE(cxxDeleteExpr); + TEST_CASE(cxxDestructorDecl); + TEST_CASE(cxxForRangeStmt1); + TEST_CASE(cxxForRangeStmt2); + TEST_CASE(cxxFunctionalCastExpr); + TEST_CASE(cxxMemberCall); + TEST_CASE(cxxMethodDecl1); + TEST_CASE(cxxMethodDecl2); + TEST_CASE(cxxMethodDecl3); + TEST_CASE(cxxMethodDecl4); + TEST_CASE(cxxNewExpr1); + TEST_CASE(cxxNewExpr2); + TEST_CASE(cxxNullPtrLiteralExpr); + TEST_CASE(cxxOperatorCallExpr); + TEST_CASE(cxxRecordDecl1); + TEST_CASE(cxxRecordDecl2); + TEST_CASE(cxxRecordDeclDerived); + TEST_CASE(cxxStaticCastExpr1); + TEST_CASE(cxxStaticCastExpr2); + TEST_CASE(cxxStaticCastExpr3); + TEST_CASE(cxxStdInitializerListExpr); + TEST_CASE(cxxThrowExpr); + TEST_CASE(defaultStmt); + TEST_CASE(doStmt); + TEST_CASE(enumDecl1); + TEST_CASE(enumDecl2); + TEST_CASE(enumDecl3); + TEST_CASE(enumDecl4); + TEST_CASE(forStmt); + TEST_CASE(funcdecl1); + TEST_CASE(funcdecl2); + TEST_CASE(funcdecl3); + TEST_CASE(funcdecl4); + TEST_CASE(funcdecl5); + TEST_CASE(funcdecl6); + TEST_CASE(functionTemplateDecl1); + TEST_CASE(functionTemplateDecl2); + TEST_CASE(initListExpr); + TEST_CASE(ifelse); + TEST_CASE(ifStmt); + TEST_CASE(labelStmt); + TEST_CASE(memberExpr); + TEST_CASE(namespaceDecl1); + TEST_CASE(namespaceDecl2); + TEST_CASE(recordDecl); + TEST_CASE(switchStmt); + TEST_CASE(typedefDecl1); + TEST_CASE(typedefDecl2); + TEST_CASE(typedefDecl3); + TEST_CASE(unaryExprOrTypeTraitExpr1); + TEST_CASE(unaryExprOrTypeTraitExpr2); + TEST_CASE(unaryOperator); + TEST_CASE(vardecl1); + TEST_CASE(vardecl2); + TEST_CASE(vardecl3); + TEST_CASE(vardecl4); + TEST_CASE(vardecl5); + TEST_CASE(vardecl6); + TEST_CASE(vardecl7); + TEST_CASE(whileStmt1); + TEST_CASE(whileStmt2); + + TEST_CASE(tokenIndex); + TEST_CASE(symbolDatabaseEnum1); + TEST_CASE(symbolDatabaseFunction1); + TEST_CASE(symbolDatabaseFunction2); + TEST_CASE(symbolDatabaseFunction3); + TEST_CASE(symbolDatabaseFunctionConst); + TEST_CASE(symbolDatabaseVariableRef); + TEST_CASE(symbolDatabaseVariableRRef); + TEST_CASE(symbolDatabaseVariablePointerRef); + TEST_CASE(symbolDatabaseNodeType1); + TEST_CASE(symbolDatabaseForVariable); + + TEST_CASE(valueFlow1); + TEST_CASE(valueFlow2); + + TEST_CASE(valueType1); + TEST_CASE(valueType2); + } + + std::string parse(const char clang[]) { + Settings settings; + settings.clang = true; + Tokenizer tokenizer(&settings, this); + std::istringstream istr(clang); + clangimport::parseClangAstDump(&tokenizer, istr); + if (!tokenizer.tokens()) { + return std::string(); + } + return tokenizer.tokens()->stringifyList(true, false, false, false, false); + } + + void breakStmt() { + const char clang[] = "`-FunctionDecl 0x2c31b18 <1.c:1:1, col:34> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x2c31c40 \n" + " `-WhileStmt 0x2c31c20 \n" + " |-<<>>\n" + " |-IntegerLiteral 0x2c31bf8 'int' 0\n" + " `-BreakStmt 0x3687c18 "; + ASSERT_EQUALS("void foo ( ) { while ( 0 ) { break ; } }", parse(clang)); + } + + void callExpr() { + const char clang[] = "`-FunctionDecl 0x2444b60 <1.c:1:1, line:8:1> line:1:6 foo 'void (int)'\n" + " |-ParmVarDecl 0x2444aa0 col:14 used x 'int'\n" + " `-CompoundStmt 0x2444e00 \n" + " `-CallExpr 0x7f5a6c04b158 'bool'\n" + " |-ImplicitCastExpr 0x7f5a6c04b140 'bool (*)(const Token *, const char *, int)' \n" + " | `-DeclRefExpr 0x7f5a6c04b0a8 'bool (const Token *, const char *, int)' lvalue CXXMethod 0x43e5600 'Match' 'bool (const Token *, const char *, int)'\n" + " |-ImplicitCastExpr 0x7f5a6c04b1c8 'const Token *' \n" + " | `-ImplicitCastExpr 0x7f5a6c04b1b0 'Token *' \n" + " | `-DeclRefExpr 0x7f5a6c04b0e0 'Token *' lvalue Var 0x7f5a6c045968 'tokAfterCondition' 'Token *'\n" + " |-ImplicitCastExpr 0x7f5a6c04b1e0 'const char *' \n" + " | `-StringLiteral 0x7f5a6c04b108 'const char [11]' lvalue \"%name% : {\"\n" + " `-CXXDefaultArgExpr 0x7f5a6c04b1f8 <> 'int'\n"; + ASSERT_EQUALS("void foo ( int x@1 ) { Match ( tokAfterCondition , \"%name% : {\" ) ; }", parse(clang)); + } + + void caseStmt1() { + const char clang[] = "`-FunctionDecl 0x2444b60 <1.c:1:1, line:8:1> line:1:6 foo 'void (int)'\n" + " |-ParmVarDecl 0x2444aa0 col:14 used x 'int'\n" + " `-CompoundStmt 0x2444e00 \n" + " `-SwitchStmt 0x2444c88 \n" + " |-<<>>\n" + " |-<<>>\n" + " |-ImplicitCastExpr 0x2444c70 'int' \n" + " | `-DeclRefExpr 0x2444c48 'int' lvalue ParmVar 0x2444aa0 'x' 'int'\n" + " `-CompoundStmt 0x2444de0 \n" + " |-CaseStmt 0x2444cd8 \n" + " | |-IntegerLiteral 0x2444cb8 'int' 16\n" + " | |-<<>>\n" + " | `-CaseStmt 0x2444d30 \n" + " | |-IntegerLiteral 0x2444d10 'int' 32\n" + " | |-<<>>\n" + " | `-BinaryOperator 0x2444db0 'int' '='\n" + " | |-DeclRefExpr 0x2444d68 'int' lvalue ParmVar 0x2444aa0 'x' 'int'\n" + " | `-IntegerLiteral 0x2444d90 'int' 123\n" + " `-BreakStmt 0x2444dd8 "; + ASSERT_EQUALS("void foo ( int x@1 ) { switch ( x@1 ) { case 16 : case 32 : x@1 = 123 ; break ; } }", parse(clang)); + } + + void characterLiteral() { + const char clang[] = "`-VarDecl 0x3df8608 col:6 c 'char' cinit\n" + " `-CharacterLiteral 0x3df86a8 'char' 120"; + ASSERT_EQUALS("char c@1 = 'x' ;", parse(clang)); + } + + void class1() { + const char clang[] = "`-CXXRecordDecl 0x274c638 col:7 class C definition\n" + " |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + " | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + " | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveConstructor exists simple trivial needs_implicit\n" + " | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveAssignment exists simple trivial needs_implicit\n" + " | `-Destructor simple irrelevant trivial needs_implicit\n" + " |-CXXRecordDecl 0x274c758 col:7 implicit class C\n" + " `-CXXMethodDecl 0x274c870 col:16 foo 'void ()'\n" + " `-CompoundStmt 0x274c930 "; + ASSERT_EQUALS("class C { void foo ( ) { } } ;", parse(clang)); + } + + void classTemplateDecl1() { + const char clang[] = "`-ClassTemplateDecl 0x29d1748 col:25 C\n" + " |-TemplateTypeParmDecl 0x29d15f8 col:16 referenced class depth 0 index 0 T\n" + " `-CXXRecordDecl 0x29d16b0 col:25 class C definition\n" + " |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + " | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + " | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveConstructor exists simple trivial needs_implicit\n" + " | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveAssignment exists simple trivial needs_implicit\n" + " | `-Destructor simple irrelevant trivial needs_implicit\n" + " |-CXXRecordDecl 0x29d19b0 col:25 implicit class C\n" + " |-AccessSpecDecl 0x29d1a48 col:29 public\n" + " `-CXXMethodDecl 0x29d1b20 col:39 foo 'T ()'\n" + " `-CompoundStmt 0x29d1c18 \n" + " `-ReturnStmt 0x29d1c00 \n" + " `-IntegerLiteral 0x29d1be0 'int' 0"; + ASSERT_EQUALS("", parse(clang)); + } + + void classTemplateDecl2() { + const char clang[] = "|-ClassTemplateDecl 0x244e748 col:25 C\n" + "| |-TemplateTypeParmDecl 0x244e5f8 col:16 referenced class depth 0 index 0 T\n" + "| |-CXXRecordDecl 0x244e6b0 col:25 class C definition\n" + "| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + "| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + "| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | | |-MoveConstructor exists simple trivial needs_implicit\n" + "| | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | | `-Destructor simple irrelevant trivial needs_implicit\n" + "| | |-CXXRecordDecl 0x244e9b0 col:25 implicit class C\n" + "| | |-AccessSpecDecl 0x244ea48 col:29 public\n" + "| | `-CXXMethodDecl 0x244eb20 col:39 foo 'T ()'\n" + "| | `-CompoundStmt 0x244ec18 \n" + "| | `-ReturnStmt 0x244ec00 \n" + "| | `-IntegerLiteral 0x244ebe0 'int' 0\n" + "| `-ClassTemplateSpecializationDecl 0x244ed78 col:25 class C definition\n" + "| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + "| | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr\n" + "| | |-CopyConstructor simple trivial has_const_param implicit_has_const_param\n" + "| | |-MoveConstructor exists simple trivial\n" + "| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | `-Destructor simple irrelevant trivial needs_implicit\n" + "| |-TemplateArgument type 'int'\n" + "| |-CXXRecordDecl 0x244eff0 prev 0x244ed78 col:25 implicit class C\n" + "| |-AccessSpecDecl 0x244f088 col:29 public\n" + "| |-CXXMethodDecl 0x244f160 col:39 used foo 'int ()'\n" + "| | `-CompoundStmt 0x247cb40 \n" + "| | `-ReturnStmt 0x247cb28 \n" + "| | `-IntegerLiteral 0x244ebe0 'int' 0\n" + "| |-CXXConstructorDecl 0x247c540 col:25 implicit used constexpr C 'void () noexcept' inline default trivial\n" + "| | `-CompoundStmt 0x247ca00 \n" + "| |-CXXConstructorDecl 0x247c658 col:25 implicit constexpr C 'void (const C &)' inline default trivial noexcept-unevaluated 0x247c658\n" + "| | `-ParmVarDecl 0x247c790 col:25 'const C &'\n" + "| `-CXXConstructorDecl 0x247c828 col:25 implicit constexpr C 'void (C &&)' inline default trivial noexcept-unevaluated 0x247c828\n" + "| `-ParmVarDecl 0x247c960 col:25 'C &&'\n"; + ASSERT_EQUALS("class C { int foo ( ) { return 0 ; } C ( ) { } C ( const C & ) = default ; C ( C && ) = default ; } ;", parse(clang)); + } + + void conditionalExpr() { + const char clang[] = "`-VarDecl 0x257cc88 col:5 x 'int' cinit\n" + " `-ConditionalOperator 0x257cda8 'int'\n" + " |-ImplicitCastExpr 0x257cd60 'int' \n" + " | `-DeclRefExpr 0x257cce8 'int' lvalue Var 0x257cae0 'a' 'int'\n" + " |-ImplicitCastExpr 0x257cd78 'int' \n" + " | `-DeclRefExpr 0x257cd10 'int' lvalue Var 0x257cb98 'b' 'int'\n" + " `-ImplicitCastExpr 0x257cd90 'int' \n" + " `-DeclRefExpr 0x257cd38 'int' lvalue Var 0x257cc10 'c' 'int'"; + ASSERT_EQUALS("int x@1 = a ? b : c ;", parse(clang)); + } + + void compoundAssignOperator() { + const char clang[] = "`-FunctionDecl 0x3570690 <1.cpp:2:1, col:25> col:6 f 'void ()'\n" + " `-CompoundStmt 0x3570880 \n" + " `-CompoundAssignOperator 0x3570848 'int' lvalue '+=' ComputeLHSTy='int' ComputeResultTy='int'\n" + " |-DeclRefExpr 0x3570800 'int' lvalue Var 0x3570788 'x' 'int'\n" + " `-IntegerLiteral 0x3570828 'int' 1"; + ASSERT_EQUALS("void f ( ) { x += 1 ; }", parse(clang)); + } + + void continueStmt() { + const char clang[] = "`-FunctionDecl 0x2c31b18 <1.c:1:1, col:34> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x2c31c40 \n" + " `-WhileStmt 0x2c31c20 \n" + " |-<<>>\n" + " |-IntegerLiteral 0x2c31bf8 'int' 0\n" + " `-ContinueStmt 0x2c31c18 "; + ASSERT_EQUALS("void foo ( ) { while ( 0 ) { continue ; } }", parse(clang)); + } + + void cstyleCastExpr() { + const char clang[] = "`-VarDecl 0x2336aa0 <1.c:1:1, col:14> col:5 x 'int' cinit\n" + " `-CStyleCastExpr 0x2336b70 'int' \n" + " `-CharacterLiteral 0x2336b40 'int' 97"; + ASSERT_EQUALS("int x@1 = ( int ) 'a' ;", parse(clang)); + } + + void cxxBoolLiteralExpr() { + const char clang[] = "`-VarDecl 0x3940608 col:6 x 'bool' cinit\n" + " `-CXXBoolLiteralExpr 0x39406a8 'bool' true"; + ASSERT_EQUALS("bool x@1 = true ;", parse(clang)); + } + + void cxxConstructorDecl1() { + const char clang[] = "|-CXXConstructorDecl 0x428e890 col:11 C 'void ()'\n" + "| `-CompoundStmt 0x428ea58 \n" + "| `-BinaryOperator 0x428ea30 'int' lvalue '='\n" + "| |-MemberExpr 0x428e9d8 'int' lvalue ->x 0x428e958\n" + "| | `-CXXThisExpr 0x428e9c0 'C *' this\n" + "| `-IntegerLiteral 0x428ea10 'int' 0\n" + "`-FieldDecl 0x428e958 col:30 referenced x 'int'"; + ASSERT_EQUALS("C ( ) { this . x@1 = 0 ; } int x@1", parse(clang)); + } + + void cxxConstructorDecl2() { + const char clang[] = "`-CXXConstructorDecl 0x1c208c0 col:11 implicit constexpr basic_string 'void (std::basic_string &&)' inline default trivial noexcept-unevaluated 0x1c208c0\n" + " `-ParmVarDecl 0x1c209f0 col:11 'std::basic_string &&'"; + ASSERT_EQUALS("basic_string ( std::basic_string && ) = default ;", parse(clang)); + } + + void cxxConstructExpr1() { + const char clang[] = "`-FunctionDecl 0x2dd7940 col:5 f 'Foo (Foo)'\n" + " |-ParmVarDecl 0x2dd7880 col:11 used foo 'Foo'\n" + " `-CompoundStmt 0x2dd80c0 \n" + " `-ReturnStmt 0x2dd80a8 \n" + " `-CXXConstructExpr 0x2dd8070 'Foo' 'void (Foo &&) noexcept'\n" + " `-ImplicitCastExpr 0x2dd7f28 'Foo' xvalue \n" + " `-DeclRefExpr 0x2dd7a28 'Foo' lvalue ParmVar 0x2dd7880 'foo' 'Foo'"; + ASSERT_EQUALS("Foo f ( Foo foo@1 ) { return foo@1 ; }", parse(clang)); + } + + void cxxConstructExpr2() { + const char clang[] = "`-FunctionDecl 0x3e44180 <1.cpp:2:1, col:30> col:13 f 'std::string ()'\n" + " `-CompoundStmt 0x3e4cb80 \n" + " `-ReturnStmt 0x3e4cb68 \n" + " `-CXXConstructExpr 0x3e4cb38 'std::string':'std::__cxx11::basic_string' '....' list"; + ASSERT_EQUALS("std :: string f ( ) { return std :: string ( ) ; }", parse(clang)); + } + + void cxxConstructExpr3() { + const char clang[] = "`-FunctionDecl 0x2c585b8 <1.cpp:4:1, col:39> col:6 f 'void ()'\n" + " `-CompoundStmt 0x2c589d0 \n" + " |-DeclStmt 0x2c586d0 \n" + " | `-VarDecl 0x2c58670 col:18 used p 'char *'\n" + " `-DeclStmt 0x2c589b8 \n" + " `-VarDecl 0x2c58798 col:33 s 'std::string':'std::__cxx11::basic_string' callinit\n" + " `-ExprWithCleanups 0x2c589a0 'std::string':'std::__cxx11::basic_string'\n" + " `-CXXConstructExpr 0x2c58960 'std::string':'std::__cxx11::basic_string' 'void (const char *, const std::allocator &)'\n" + " |-ImplicitCastExpr 0x2c58870 'const char *' \n" + " | `-ImplicitCastExpr 0x2c58858 'char *' \n" + " | `-DeclRefExpr 0x2c58750 'char *' lvalue Var 0x2c58670 'p' 'char *'\n" + " `-CXXDefaultArgExpr 0x2c58940 <> 'const std::allocator':'const std::allocator' lvalue\n"; + ASSERT_EQUALS("void f ( ) { char * p@1 ; std :: string s@2 ( p@1 ) ; }", parse(clang)); + } + + void cxxDeleteExpr() { + const char clang[] = "|-FunctionDecl 0x2e0e740 <1.cpp:1:1, col:28> col:6 f 'void (int *)'\n" + "| |-ParmVarDecl 0x2e0e680 col:13 used p 'int *'\n" + "| `-CompoundStmt 0x2e0ee70 \n" + "| `-CXXDeleteExpr 0x2e0ee48 'void' Function 0x2e0ebb8 'operator delete' 'void (void *) noexcept'\n" + "| `-ImplicitCastExpr 0x2e0e850 'int *' \n" + "| `-DeclRefExpr 0x2e0e828 'int *' lvalue ParmVar 0x2e0e680 'p' 'int *'"; + ASSERT_EQUALS("void f ( int * p@1 ) { delete p@1 ; }", parse(clang)); + } + + void cxxDestructorDecl() { + const char clang[] = "`-CXXRecordDecl 0x8ecd60 <1.cpp:1:1, line:4:1> line:1:8 struct S definition\n" + " `-CXXDestructorDecl 0x8ed088 col:3 ~S 'void () noexcept'\n" + " `-CompoundStmt 0x8ed1a8 "; + ASSERT_EQUALS("struct S { ~S ( ) { } } ;", parse(clang)); + } + + void cxxForRangeStmt1() { + const char clang[] = "`-FunctionDecl 0x4280820 line:4:6 foo 'void ()'\n" + " `-CompoundStmt 0x42810f0 \n" + " `-CXXForRangeStmt 0x4281090 \n" + " |-DeclStmt 0x4280c30 \n" + " | `-VarDecl 0x42809c8 col:17 implicit referenced __range1 'char const (&)[6]' cinit\n" + " | `-DeclRefExpr 0x42808c0 'const char [6]' lvalue Var 0x4280678 'hello' 'const char [6]'\n" + " |-DeclStmt 0x4280ef8 \n" + " | `-VarDecl 0x4280ca8 col:15 implicit used __begin1 'const char *':'const char *' cinit\n" + " | `-ImplicitCastExpr 0x4280e10 'const char *' \n" + " | `-DeclRefExpr 0x4280c48 'char const[6]' lvalue Var 0x42809c8 '__range1' 'char const (&)[6]'\n" + " |-DeclStmt 0x4280f10 \n" + " | `-VarDecl 0x4280d18 col:15 implicit used __end1 'const char *':'const char *' cinit\n" + " | `-BinaryOperator 0x4280e60 'const char *' '+'\n" + " | |-ImplicitCastExpr 0x4280e48 'const char *' \n" + " | | `-DeclRefExpr 0x4280c70 'char const[6]' lvalue Var 0x42809c8 '__range1' 'char const (&)[6]'\n" + " | `-IntegerLiteral 0x4280e28 'long' 6\n" + " |-BinaryOperator 0x4280fa8 'bool' '!='\n" + " | |-ImplicitCastExpr 0x4280f78 'const char *':'const char *' \n" + " | | `-DeclRefExpr 0x4280f28 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" + " | `-ImplicitCastExpr 0x4280f90 'const char *':'const char *' \n" + " | `-DeclRefExpr 0x4280f50 'const char *':'const char *' lvalue Var 0x4280d18 '__end1' 'const char *':'const char *'\n" + " |-UnaryOperator 0x4280ff8 'const char *':'const char *' lvalue prefix '++'\n" + " | `-DeclRefExpr 0x4280fd0 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" + " |-DeclStmt 0x4280958 \n" + " | `-VarDecl 0x42808f8 col:13 c1 'char' cinit\n" + " | `-ImplicitCastExpr 0x4281078 'char' \n" + " | `-UnaryOperator 0x4281058 'const char' lvalue prefix '*' cannot overflow\n" + " | `-ImplicitCastExpr 0x4281040 'const char *':'const char *' \n" + " | `-DeclRefExpr 0x4281018 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" + " `-CompoundStmt 0x42810e0 "; + ASSERT_EQUALS("void foo ( ) { for ( char c1@1 : hello ) { } }", + parse(clang)); + } + + void cxxForRangeStmt2() { + // clang 9 + const char clang[] = "`-FunctionDecl 0xc15d98 col:6 foo 'void ()'\n" + " `-CompoundStmt 0xc16668 \n" + " `-CXXForRangeStmt 0xc165f8 \n" + " |-<<>>\n" + " |-DeclStmt 0xc161c0 \n" + " | `-VarDecl 0xc15f48 col:25 implicit referenced __range1 'int const (&)[4]' cinit\n" + " | `-DeclRefExpr 0xc15e38 'const int [4]' lvalue Var 0xc15ac0 'values' 'const int [4]'\n" + " |-DeclStmt 0xc16498 \n" + " | `-VarDecl 0xc16228 col:24 implicit used __begin1 'const int *':'const int *' cinit\n" + " | `-ImplicitCastExpr 0xc163b0 'const int *' \n" + " | `-DeclRefExpr 0xc161d8 'int const[4]' lvalue Var 0xc15f48 '__range1' 'int const (&)[4]' non_odr_use_constant\n" + " |-DeclStmt 0xc164b0 \n" + " | `-VarDecl 0xc162a0 col:24 implicit used __end1 'const int *':'const int *' cinit\n" + " | `-BinaryOperator 0xc16400 'const int *' '+'\n" + " | |-ImplicitCastExpr 0xc163e8 'const int *' \n" + " | | `-DeclRefExpr 0xc161f8 'int const[4]' lvalue Var 0xc15f48 '__range1' 'int const (&)[4]' non_odr_use_constant\n" + " | `-IntegerLiteral 0xc163c8 'long' 4\n" + " |-BinaryOperator 0xc16538 'bool' '!='\n" + " | |-ImplicitCastExpr 0xc16508 'const int *':'const int *' \n" + " | | `-DeclRefExpr 0xc164c8 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" + " | `-ImplicitCastExpr 0xc16520 'const int *':'const int *' \n" + " | `-DeclRefExpr 0xc164e8 'const int *':'const int *' lvalue Var 0xc162a0 '__end1' 'const int *':'const int *'\n" + " |-UnaryOperator 0xc16578 'const int *':'const int *' lvalue prefix '++'\n" + " | `-DeclRefExpr 0xc16558 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" + " |-DeclStmt 0xc15ed8 \n" + " | `-VarDecl 0xc15e70 col:23 v 'int' cinit\n" + " | `-ImplicitCastExpr 0xc165e0 'int' \n" + " | `-UnaryOperator 0xc165c8 'const int' lvalue prefix '*' cannot overflow\n" + " | `-ImplicitCastExpr 0xc165b0 'const int *':'const int *' \n" + " | `-DeclRefExpr 0xc16590 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" + " `-CompoundStmt 0xc16658 "; + ASSERT_EQUALS("void foo ( ) { for ( int v@1 : values ) { } }", + parse(clang)); + } + + void cxxFunctionalCastExpr() { + const char clang[] = "`-FunctionDecl 0x156fe98 line:1:5 main 'int (int, char **)'\n" + " |-ParmVarDecl 0x156fd00 col:14 argc 'int'\n" + " |-ParmVarDecl 0x156fdb8 col:27 argv 'char **'\n" + " `-CompoundStmt 0x1596410 \n" + " |-DeclStmt 0x15946a8 \n" + " | `-VarDecl 0x1570118 col:11 used setCode 'MyVar':'MyVar' cinit\n" + " | `-ExprWithCleanups 0x1594690 'MyVar':'MyVar'\n" + " | `-CXXConstructExpr 0x1594660 'MyVar':'MyVar' 'void (MyVar &&) noexcept' elidable\n" + " | `-MaterializeTemporaryExpr 0x1592b68 'MyVar':'MyVar' xvalue\n" + " | `-CXXFunctionalCastExpr 0x1592b40 'MyVar':'MyVar' functional cast to MyVar \n" + " | `-CXXConstructExpr 0x15929f0 'MyVar':'MyVar' 'void (int)'\n" + " | `-IntegerLiteral 0x1570248 'int' 5\n"; + ASSERT_EQUALS("int main ( int argc@1 , char * * argv@2 ) { MyVar setCode@3 = MyVar ( 5 ) ; }", + parse(clang)); + } + + void cxxMemberCall() { + const char clang[] = "`-FunctionDecl 0x320dc80 col:6 bar 'void ()'\n" + " `-CompoundStmt 0x323bb08 \n" + " |-DeclStmt 0x323ba40 \n" + " | `-VarDecl 0x320df28 col:21 used c 'C':'C' callinit\n" + " | `-CXXConstructExpr 0x323ba10 'C':'C' 'void () noexcept'\n" + " `-CXXMemberCallExpr 0x323bab8 'int':'int'\n" + " `-MemberExpr 0x323ba80 '' .foo 0x320e160\n" + " `-DeclRefExpr 0x323ba58 'C':'C' lvalue Var 0x320df28 'c' 'C':'C'"; + ASSERT_EQUALS("void bar ( ) { C c@1 ( C ( ) ) ; c@1 . foo ( ) ; }", parse(clang)); + } + + void cxxMethodDecl1() { + const char clang[] = "|-CXXMethodDecl 0x55c786f5ad60 col:10 analyzeFile '_Bool (const std::string &, const std::string &, const std::string &, unsigned long long, std::list *)'\n" + "| |-ParmVarDecl 0x55c786f5a4c8 col:41 buildDir 'const std::string &'\n" + "| |-ParmVarDecl 0x55c786f5a580 col:70 sourcefile 'const std::string &'\n" + "| |-ParmVarDecl 0x55c786f5a638 col:101 cfg 'const std::string &'\n" + "| |-ParmVarDecl 0x55c786f5a6a8 col:125 checksum 'unsigned long long'\n" + "| |-ParmVarDecl 0x55c786f5ac00 col:173 errors 'std::list *'\n" + " `-CompoundStmt 0x0 <>"; + ASSERT_EQUALS("_Bool analyzeFile ( const std :: string & buildDir@1 , const std :: string & sourcefile@2 , const std :: string & cfg@3 , unsigned long long checksum@4 , std::list * errors@5 ) { }", parse(clang)); + } + + void cxxMethodDecl2() { // "unexpanded" template method + const char clang[] = "`-CXXMethodDecl 0x220ecb0 parent 0x21e4c28 prev 0x21e5338 line:14:1 find 'const typename char_traits<_CharT>::char_type *(const char_traits::char_type *, int, const char_traits::char_type &)'\n" + " `-CompoundStmt 0x220ede0 \n" + " `-ReturnStmt 0x220edd0 \n" + " `-IntegerLiteral 0x220edb0 'int' 0"; + ASSERT_EQUALS("", parse(clang)); + } + + void cxxMethodDecl3() { + const char clang[] = "|-CXXRecordDecl 0x21cca40 <2.cpp:2:1, line:4:1> line:2:7 class Fred definition\n" + "| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + "| | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + "| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveConstructor exists simple trivial needs_implicit\n" + "| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | `-Destructor simple irrelevant trivial needs_implicit\n" + "| |-CXXRecordDecl 0x21ccb58 col:7 implicit class Fred\n" + "| `-CXXMethodDecl 0x21ccc68 col:6 foo 'void ()'\n" + "`-CXXMethodDecl 0x21ccd60 parent 0x21cca40 prev 0x21ccc68 col:12 foo 'void ()'\n" + " `-CompoundStmt 0x21cce50 "; + ASSERT_EQUALS("class Fred { void foo ( ) ; } ; void Fred :: foo ( ) { }", parse(clang)); + } + + void cxxMethodDecl4() { + const char clang[] = "|-ClassTemplateSpecializationDecl 0x15d82f8 line:8:12 struct char_traits definition\n" + "| |-TemplateArgument type 'char'\n" + "| | `-BuiltinType 0x15984c0 'char'\n" + "| |-CXXRecordDecl 0x15d8520 col:12 implicit struct char_traits\n" + "| |-CXXMethodDecl 0x15d8738 line:13:7 move 'char *(char *)' static\n" + "| | |-ParmVarDecl 0x15d8630 col:18 used __s1 'char *'\n" + "| | `-CompoundStmt 0x15d88e8 \n"; + ASSERT_EQUALS("struct char_traits { static char * move ( char * __s1@1 ) { } } ;", + parse(clang)); + } + + void cxxNewExpr1() { + const char clang[] = "|-VarDecl 0x3a97680 <1.cpp:2:1, col:14> col:6 i 'int *' cinit\n" + "| `-CXXNewExpr 0x3a97d18 'int *' Function 0x3a97778 'operator new' 'void *(unsigned long)'\n" + "`-VarDecl 0x3a97d80 col:6 j 'int *' cinit\n" + " `-CXXNewExpr 0x3a97e68 'int *' array Function 0x3a978c0 'operator new[]' 'void *(unsigned long)'\n" + " `-ImplicitCastExpr 0x3a97e18 'unsigned long' \n" + " `-IntegerLiteral 0x3a97de0 'int' 100"; + ASSERT_EQUALS("int * i@1 = new int ; int * j@2 = new int [ 100 ] ;", + parse(clang)); + } + + void cxxNewExpr2() { + const char clang[] = "|-FunctionDecl 0x59a188 line:7:11 f 'struct S *()'\n" + "| `-CompoundStmt 0x5c4318 \n" + "| `-ReturnStmt 0x5c4308 \n" + "| `-CXXNewExpr 0x5c42c8 'S *' Function 0x59a378 'operator new' 'void *(unsigned long)'\n" + "| `-CXXConstructExpr 0x5c42a0 'S' 'void () noexcept'"; + ASSERT_EQUALS("struct S * f ( ) { return new S ( ) ; }", + parse(clang)); + } + + void cxxNullPtrLiteralExpr() { + const char clang[] = "`-VarDecl 0x2a7d650 <1.cpp:1:1, col:17> col:13 p 'const char *' cinit\n" + " `-ImplicitCastExpr 0x2a7d708 'const char *' \n" + " `-CXXNullPtrLiteralExpr 0x2a7d6f0 'nullptr_t'"; + ASSERT_EQUALS("const char * p@1 = nullptr ;", parse(clang)); + } + + void cxxOperatorCallExpr() { + const char clang[] = "`-FunctionDecl 0x3c099f0 col:6 foo 'void ()'\n" + " `-CompoundStmt 0x3c37308 \n" + " |-DeclStmt 0x3c0a060 \n" + " | `-VarDecl 0x3c09ae0 col:16 used c 'C' callinit\n" + " | `-CXXConstructExpr 0x3c0a030 'C' 'void () noexcept'\n" + " `-CXXOperatorCallExpr 0x3c372c0 'void'\n" + " |-ImplicitCastExpr 0x3c372a8 'void (*)(int)' \n" + " | `-DeclRefExpr 0x3c37250 'void (int)' lvalue CXXMethod 0x3c098c0 'operator=' 'void (int)'\n" + " |-DeclRefExpr 0x3c0a078 'C' lvalue Var 0x3c09ae0 'c' 'C'\n" + " `-IntegerLiteral 0x3c0a0a0 'int' 4"; + ASSERT_EQUALS("void foo ( ) { C c@1 ( C ( ) ) ; c@1 . operator= ( 4 ) ; }", parse(clang)); + } + + void cxxRecordDecl1() { + const char clang[] = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 class Foo"; + ASSERT_EQUALS("class Foo ;", parse(clang)); + } + + void cxxRecordDecl2() { + const char clang[] = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 struct Foo definition"; + ASSERT_EQUALS("struct Foo { } ;", parse(clang)); + } + + void cxxRecordDeclDerived() { + const char clang[] = "|-CXXRecordDecl 0x19ccd38 line:4:8 referenced struct base definition\n" + "| `-VarDecl 0x19ccf00 col:27 value 'const bool' static constexpr cinit\n" + "| |-value: Int 0\n" + "| `-CXXBoolLiteralExpr 0x19ccf68 'bool' false\n" + "`-CXXRecordDecl 0x19ccfe8 col:8 struct derived definition\n" + " |-public 'base'\n" + " `-CXXRecordDecl 0x19cd150 col:8 implicit struct derived"; + + ASSERT_EQUALS("struct base { static const bool value@1 = false ; } ; struct derived : public base { } ;", parse(clang)); + } + + void cxxStaticCastExpr1() { + const char clang[] = "`-VarDecl 0x2e0e650 col:5 a 'int' cinit\n" + " `-CXXStaticCastExpr 0x2e0e728 'int' static_cast \n" + " `-IntegerLiteral 0x2e0e6f0 'int' 0"; + ASSERT_EQUALS("int a@1 = static_cast ( 0 ) ;", parse(clang)); + } + + void cxxStaticCastExpr2() { + const char clang[] = "`-VarDecl 0x2e0e650 col:5 a 'int' cinit\n" + " `-CXXStaticCastExpr 0x3e453e8 'std::_Rb_tree_iterator, Library::AllocFunc> >' xvalue static_cast, struct Library::AllocFunc> > &&> \n" + " `-DeclRefExpr 0x3e453b0 'std::_Rb_tree_iterator, Library::AllocFunc> >' lvalue ParmVar 0x3e45250 '' 'std::_Rb_tree_iterator, Library::AllocFunc> > &&'"; + ASSERT_EQUALS("int a@1 = static_cast,structLibrary::AllocFunc>>&&> ( ) ;", parse(clang)); + } + + void cxxStaticCastExpr3() { + const char clang[] = "`-ClassTemplateSpecializationDecl 0xd842d8 line:4:21 struct char_traits definition\n" + " |-TemplateArgument type 'char'\n" + " | `-BuiltinType 0xd444c0 'char'\n" + " |-CXXRecordDecl 0xd84500 col:21 implicit struct char_traits\n" + " |-TypedefDecl 0xd845a0 col:20 referenced char_type 'char'\n" + " | `-BuiltinType 0xd444c0 'char'\n" + " `-CXXMethodDecl 0xd847b0 col:18 assign 'char_traits::char_type *(char_traits::char_type *)'\n" + " |-ParmVarDecl 0xd84670 col:36 used __s 'char_traits::char_type *'\n" + " `-CompoundStmt 0xd848f8 \n" + " `-ReturnStmt 0xd848e8 \n" + " `-CXXStaticCastExpr 0xd848b8 'char_traits::char_type *' static_cast::char_type *> \n" + " `-ImplicitCastExpr 0xd848a0 'char_traits::char_type *' part_of_explicit_cast\n" + " `-DeclRefExpr 0xd84870 'char_traits::char_type *' lvalue ParmVar 0xd84670 '__s' 'char_traits::char_type *'\n"; + + ASSERT_EQUALS("struct char_traits { typedef char char_type ; char_traits::char_type * assign ( char_traits::char_type * __s@1 ) { return static_cast::char_type*> ( __s@1 ) ; } } ;", parse(clang)); + } + + void cxxStdInitializerListExpr() { + const char clang[] = "`-VarDecl 0x2f92060 <1.cpp:3:1, col:25> col:18 x 'std::vector':'std::vector >' listinit\n" + " `-ExprWithCleanups 0x2fb0b40 'std::vector':'std::vector >'\n" + " `-CXXConstructExpr 0x2fb0b00 'std::vector':'std::vector >' 'void (initializer_list >::value_type>, const std::vector >::allocator_type &)' list std::initializer_list\n" + " |-CXXStdInitializerListExpr 0x2fb0928 'initializer_list >::value_type>':'std::initializer_list'\n" + " | `-MaterializeTemporaryExpr 0x2fb0910 'const int [3]' xvalue\n" + " | `-InitListExpr 0x2fb08b8 'const int [3]'\n" + " | |-IntegerLiteral 0x2f920c0 'int' 1\n" + " | |-IntegerLiteral 0x2f920e0 'int' 2\n" + " | `-IntegerLiteral 0x2f92100 'int' 3\n" + " `-CXXDefaultArgExpr 0x2fb0ae0 <> 'const std::vector >::allocator_type':'const std::allocator' lvalue"; + ASSERT_EQUALS("std :: vector x@1 { 1 , 2 , 3 } ;", parse(clang)); + } + + void cxxThrowExpr() { + const char clang[] = "`-FunctionDecl 0x3701690 <1.cpp:2:1, col:23> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x37017b0 \n" + " `-CXXThrowExpr 0x3701790 'void'\n" + " `-IntegerLiteral 0x3701770 'int' 1"; + ASSERT_EQUALS("void foo ( ) { throw 1 ; }", parse(clang)); + } + + void defaultStmt() { + const char clang[] = "`-FunctionDecl 0x18476b8 <1.c:3:1, line:9:1> line:3:5 foo 'int (int)'\n" + " |-ParmVarDecl 0x18475e0 col:13 used rc 'int'\n" + " `-CompoundStmt 0x1847868 \n" + " `-SwitchStmt 0x18477e0 \n" + " |-ImplicitCastExpr 0x18477c8 'int' \n" + " | `-DeclRefExpr 0x18477a8 'int' lvalue ParmVar 0x18475e0 'rc' 'int'\n" + " `-CompoundStmt 0x1847850 \n" + " `-DefaultStmt 0x1847830 \n" + " `-ReturnStmt 0x1847820 \n" + " `-IntegerLiteral 0x1847800 'int' 1"; + ASSERT_EQUALS("int foo ( int rc@1 ) { switch ( rc@1 ) { default : return 1 ; } }", parse(clang)); + } + + void doStmt() { + const char clang[] = "`-FunctionDecl 0x27fbbc8 col:6 foo 'void ()'\n" + " `-CompoundStmt 0x27fbd08 \n" + " `-DoStmt 0x27fbce8 \n" + " |-CompoundStmt 0x27fbcb0 \n" + " | `-UnaryOperator 0x27fbc90 'int' postfix '++'\n" + " | `-DeclRefExpr 0x27fbc68 'int' lvalue Var 0x27fbae0 'x' 'int'\n" + " `-IntegerLiteral 0x27fbcc8 'int' 1"; + ASSERT_EQUALS("void foo ( ) { do { ++ x ; } while ( 1 ) ; }", parse(clang)); + } + + void enumDecl1() { + const char clang[] = "`-EnumDecl 0x2660660 col:6 referenced abc\n" + " |-EnumConstantDecl 0x2660720 col:11 referenced a 'abc'\n" + " |-EnumConstantDecl 0x2660768 col:13 b 'abc'\n" + " `-EnumConstantDecl 0x26607b0 col:15 c 'abc'"; + ASSERT_EQUALS("enum abc { a , b , c }", parse(clang)); + } + + void enumDecl2() { + const char clang[] = "`-EnumDecl 0xb55d50 <2.cpp:4:3, col:44> col:8 syntax_option_type 'unsigned int'"; + ASSERT_EQUALS("enum syntax_option_type : unsigned int { }", parse(clang)); + } + + void enumDecl3() { + const char clang[] = "|-EnumDecl 0x1586e48 <2.cpp:1:3, line:5:3> line:1:8 __syntax_option\n" + "| |-EnumConstantDecl 0x1586f18 col:5 referenced _S_polynomial '__syntax_option'\n" + "| `-EnumConstantDecl 0x1586f68 col:5 _S_syntax_last '__syntax_option'"; + ASSERT_EQUALS("enum __syntax_option { _S_polynomial , _S_syntax_last }", parse(clang)); + } + + void enumDecl4() { + const char clang[] = "|-EnumDecl 0xace1f8 col:1\n" + "| |-EnumConstantDecl 0xace2c8 col:7 A '(anonymous enum at e1.cpp:3:1)'\n" + "| |-EnumConstantDecl 0xace318 col:16 B '(anonymous enum at e1.cpp:3:1)'\n" + "| `-EnumConstantDecl 0xace3b8 col:46 referenced C '(anonymous enum at e1.cpp:3:1)'\n" + "`-VarDecl 0xace470 col:53 x 'enum (anonymous enum at e1.cpp:3:1)':'(anonymous enum at e1.cpp:3:1)' cinit\n" + " `-DeclRefExpr 0xace520 '(anonymous enum at e1.cpp:3:1)' EnumConstant 0xace3b8 'C' '(anonymous enum at e1.cpp:3:1)'"; + ASSERT_EQUALS("enum { A , B , C } x@1 = C ;", parse(clang)); + } + + void forStmt() { + const char clang[] = "`-FunctionDecl 0x2f93ae0 <1.c:1:1, col:56> col:5 main 'int ()'\n" + " `-CompoundStmt 0x2f93dc0 \n" + " |-ForStmt 0x2f93d50 \n" + " | |-DeclStmt 0x2f93c58 \n" + " | | `-VarDecl 0x2f93bd8 col:23 used i 'int' cinit\n" + " | | `-IntegerLiteral 0x2f93c38 'int' 0\n" + " | |-<<>>\n" + " | |-BinaryOperator 0x2f93cd0 'int' '<'\n" + " | | |-ImplicitCastExpr 0x2f93cb8 'int' \n" + " | | | `-DeclRefExpr 0x2f93c70 'int' lvalue Var 0x2f93bd8 'i' 'int'\n" + " | | `-IntegerLiteral 0x2f93c98 'int' 10\n" + " | |-UnaryOperator 0x2f93d20 'int' postfix '++'\n" + " | | `-DeclRefExpr 0x2f93cf8 'int' lvalue Var 0x2f93bd8 'i' 'int'\n" + " | `-CompoundStmt 0x2f93d40 \n" + " `-ReturnStmt 0x2f93da8 \n" + " `-IntegerLiteral 0x2f93d88 'int' 0"; + ASSERT_EQUALS("int main ( ) { for ( int i@1 = 0 ; i@1 < 10 ; ++ i@1 ) { } return 0 ; }", parse(clang)); + } + + void funcdecl1() { + const char clang[] = "`-FunctionDecl 0x3122c30 <1.c:1:1, col:22> col:6 foo 'void (int, int)'\n" + " |-ParmVarDecl 0x3122ae0 col:14 x 'int'\n" + " `-ParmVarDecl 0x3122b58 col:21 y 'int'"; + ASSERT_EQUALS("void foo ( int x@1 , int y@2 ) ;", parse(clang)); + } + + void funcdecl2() { + const char clang[] = "`-FunctionDecl 0x24b2c38 <1.c:1:1, line:4:1> line:1:5 foo 'int (int, int)'\n" + " |-ParmVarDecl 0x24b2ae0 col:13 used x 'int'\n" + " |-ParmVarDecl 0x24b2b58 col:20 used y 'int'\n" + " `-CompoundStmt 0x24b2de8 \n" + " `-ReturnStmt 0x24b2dd0 \n" + " `-BinaryOperator 0x24b2da8 'int' '/'\n" + " |-ImplicitCastExpr 0x24b2d78 'int' \n" + " | `-DeclRefExpr 0x24b2d28 'int' lvalue ParmVar 0x24b2ae0 'x' 'int'\n" + " `-ImplicitCastExpr 0x24b2d90 'int' \n" + " `-DeclRefExpr 0x24b2d50 'int' lvalue ParmVar 0x24b2b58 'y' 'int'"; + ASSERT_EQUALS("int foo ( int x@1 , int y@2 ) { return x@1 / y@2 ; }", parse(clang)); + } + + void funcdecl3() { + const char clang[] = "|-FunctionDecl 0x27cb6b8 col:12 __overflow 'int (FILE *, int)' extern\n" + "| |-ParmVarDecl 0x27cb528 col:30 'FILE *'\n" + "| `-ParmVarDecl 0x27cb5a0 col:35 'int'"; + ASSERT_EQUALS("int __overflow ( FILE * , int ) ;", parse(clang)); + } + + void funcdecl4() { + const char clang[] = "|-FunctionDecl 0x272bb60 col:15 implicit fwrite 'unsigned long (const void *, unsigned long, unsigned long, FILE *)' extern\n" + "| |-ParmVarDecl 0x272cc40 <> 'const void *'\n" + "| |-ParmVarDecl 0x272cca0 <> 'unsigned long'\n" + "| |-ParmVarDecl 0x272cd00 <> 'unsigned long'\n" + "| `-ParmVarDecl 0x272cd60 <> 'FILE *'"; + ASSERT_EQUALS("unsigned long fwrite ( const void * , unsigned long , unsigned long , FILE * ) ;", parse(clang)); + } + + void funcdecl5() { + const char clang[] = "`-FunctionDecl 0x59d670 <1.c:1:1, col:28> col:20 foo 'void (void)' static inline"; + ASSERT_EQUALS("static inline void foo ( ) ;", parse(clang)); + } + + void funcdecl6() { + const char clang[] = "`-FunctionDecl 0x196eea8 <1.cpp:3:5, col:27> col:12 foo 'void **(int)'\n" + " `-ParmVarDecl 0x196eda0 col:21 count 'int'"; + ASSERT_EQUALS("void * * foo ( int count@1 ) ;", parse(clang)); + } + + void functionTemplateDecl1() { + const char clang[] = "`-FunctionTemplateDecl 0x3242860 col:21 foo"; + ASSERT_EQUALS("", parse(clang)); + } + + void functionTemplateDecl2() { + const char clang[] = "|-FunctionTemplateDecl 0x333a860 col:21 foo\n" + "| |-TemplateTypeParmDecl 0x333a5f8 col:16 referenced class depth 0 index 0 T\n" + "| |-FunctionDecl 0x333a7c0 col:21 foo 'T (T)'\n" + "| | |-ParmVarDecl 0x333a6c0 col:27 referenced t 'T'\n" + "| | `-CompoundStmt 0x333a980 \n" + "| | `-ReturnStmt 0x333a968 \n" + "| | `-BinaryOperator 0x333a940 '' '+'\n" + "| | |-DeclRefExpr 0x333a8f8 'T' lvalue ParmVar 0x333a6c0 't' 'T'\n" + "| | `-IntegerLiteral 0x333a920 'int' 1\n" + "| `-FunctionDecl 0x333ae00 col:21 used foo 'int (int)'\n" + "| |-TemplateArgument type 'int'\n" + "| |-ParmVarDecl 0x333ad00 col:27 used t 'int':'int'\n" + "| `-CompoundStmt 0x333b0a8 \n" + "| `-ReturnStmt 0x333b090 \n" + "| `-BinaryOperator 0x333b068 'int' '+'\n" + "| |-ImplicitCastExpr 0x333b050 'int':'int' \n" + "| | `-DeclRefExpr 0x333b028 'int':'int' lvalue ParmVar 0x333ad00 't' 'int':'int'\n" + "| `-IntegerLiteral 0x333a920 'int' 1\n" + "`-FunctionDecl 0x333a9f8 col:1 invalid bar 'int ()'\n" + " `-CompoundStmt 0x333b010 \n" + " `-CallExpr 0x333afe0 'int':'int'\n" + " |-ImplicitCastExpr 0x333afc8 'int (*)(int)' \n" + " | `-DeclRefExpr 0x333af00 'int (int)' lvalue Function 0x333ae00 'foo' 'int (int)' (FunctionTemplate 0x333a860 'foo')\n" + " `-IntegerLiteral 0x333ab48 'int' 1"; + ASSERT_EQUALS("int foo ( int t@1 ) { return t@1 + 1 ; } int bar ( ) { foo ( 1 ) ; }", parse(clang)); + } + + void ifelse() { + const char clang[] = "`-FunctionDecl 0x2637ba8 <1.c:1:1, line:4:1> line:1:5 foo 'int (int)'\n" + " |-ParmVarDecl 0x2637ae0 col:13 used x 'int'\n" + " `-CompoundStmt 0x2637d70 \n" + " `-IfStmt 0x2637d38 \n" + " |-<<>>\n" + " |-<<>>\n" + " |-BinaryOperator 0x2637cf0 'int' '>'\n" + " | |-ImplicitCastExpr 0x2637cd8 'int' \n" + " | | `-DeclRefExpr 0x2637c90 'int' lvalue ParmVar 0x2637ae0 'x' 'int'\n" + " | `-IntegerLiteral 0x2637cb8 'int' 10\n" + " |-CompoundStmt 0x2637d18 \n" + " `-CompoundStmt 0x2637d28 "; + ASSERT_EQUALS("int foo ( int x@1 ) { if ( x@1 > 10 ) { } else { } }", parse(clang)); + } + + void ifStmt() { + // Clang 8 in cygwin + const char clang[] = "`-FunctionDecl 0x41d0690 <2.cpp:1:1, col:24> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x41d07f0 \n" + " `-IfStmt 0x41d07b8 \n" + " |-ImplicitCastExpr 0x41d0790 'bool' \n" + " | `-IntegerLiteral 0x41d0770 'int' 1\n" + " |-CompoundStmt 0x41d07a8 \n"; + ASSERT_EQUALS("void foo ( ) { if ( 1 ) { } }", parse(clang)); + } + + void initListExpr() { + const char clang[] = "|-VarDecl 0x397c680 <1.cpp:2:1, col:26> col:11 used ints 'const int [3]' cinit\n" + "| `-InitListExpr 0x397c7d8 'const int [3]'\n" + "| |-IntegerLiteral 0x397c720 'int' 1\n" + "| |-IntegerLiteral 0x397c740 'int' 2\n" + "| `-IntegerLiteral 0x397c760 'int' 3"; + ASSERT_EQUALS("const int [3] ints@1 = { 1 , 2 , 3 } ;", parse(clang)); + } + + void labelStmt() { + const char clang[] = "`-FunctionDecl 0x2ed1ba0 <1.c:1:1, col:36> col:6 foo 'void (int)'\n" + " `-CompoundStmt 0x2ed1d00 \n" + " `-LabelStmt 0x2ed1ce8 'loop'\n" + " `-GotoStmt 0x2ed1cd0 'loop' 0x2ed1c88"; + ASSERT_EQUALS("void foo ( ) { loop : goto loop ; }", parse(clang)); + } + + void memberExpr() { + // C code: + // struct S { int x }; + // int foo(struct S s) { return s.x; } + const char clang[] = "|-RecordDecl 0x2441a88 <1.c:1:1, col:18> col:8 struct S definition\n" + "| `-FieldDecl 0x2441b48 col:16 referenced x 'int'\n" + "`-FunctionDecl 0x2441cf8 col:5 foo 'int (struct S)'\n" + " |-ParmVarDecl 0x2441be8 col:18 used s 'struct S':'struct S'\n" + " `-CompoundStmt 0x2441e70 \n" + " `-ReturnStmt 0x2441e58 \n" + " `-ImplicitCastExpr 0x2441e40 'int' \n" + " `-MemberExpr 0x2441e08 'int' lvalue .x 0x2441b48\n" + " `-DeclRefExpr 0x2441de0 'struct S':'struct S' lvalue ParmVar 0x2441be8 's' 'struct S':'struct S'"; + ASSERT_EQUALS("struct S { int x@1 ; } ; int foo ( struct S s@2 ) { return s@2 . x@1 ; }", + parse(clang)); + } + + void namespaceDecl1() { + const char clang[] = "`-NamespaceDecl 0x2e5f658 col:11 x\n" + " `-VarDecl 0x2e5f6d8 col:19 var 'int'"; + ASSERT_EQUALS("namespace x { int var@1 ; }", + parse(clang)); + } + + void namespaceDecl2() { + const char clang[] = "`-NamespaceDecl 0x1753e60 <1.cpp:1:1, line:4:1> line:1:11 std\n" + " |-VisibilityAttr 0x1753ed0 Default\n" + " `-VarDecl 0x1753f40 col:9 x 'int'"; + ASSERT_EQUALS("namespace std { int x@1 ; }", + parse(clang)); + } + + void recordDecl() { + const char clang[] = "`-RecordDecl 0x354eac8 <1.c:1:1, line:4:1> line:1:8 struct S definition\n" + " |-FieldDecl 0x354eb88 col:7 x 'int'\n" + " `-FieldDecl 0x354ebe8 col:7 y 'int'"; + ASSERT_EQUALS("struct S { int x@1 ; int y@2 ; } ;", + parse(clang)); + } + + void switchStmt() { + const char clang[] = "`-FunctionDecl 0x2796ba0 <1.c:1:1, col:35> col:6 foo 'void (int)'\n" + " |-ParmVarDecl 0x2796ae0 col:14 used x 'int'\n" + " `-CompoundStmt 0x2796d18 \n" + " |-SwitchStmt 0x2796cc8 \n" + " | |-<<>>\n" + " | |-<<>>\n" + " | |-ImplicitCastExpr 0x2796cb0 'int' \n" + " | | `-DeclRefExpr 0x2796c88 'int' lvalue ParmVar 0x2796ae0 'x' 'int'\n" + " | `-CompoundStmt 0x2796cf8 \n" + " `-NullStmt 0x2796d08 "; + ASSERT_EQUALS("void foo ( int x@1 ) { switch ( x@1 ) { } ; }", + parse(clang)); + } + + void typedefDecl1() { + const char clang[] = "|-TypedefDecl 0x2d60180 <> implicit __int128_t '__int128'\n" + "| `-BuiltinType 0x2d5fe80 '__int128'"; + ASSERT_EQUALS("typedef __int128 __int128_t ;", parse(clang)); + } + + void typedefDecl2() { + const char clang[] = "|-TypedefDecl 0x2d604a8 <> implicit __NSConstantString 'struct __NSConstantString_tag'\n" + "| `-RecordType 0x2d602c0 'struct __NSConstantString_tag'\n" + "| `-Record 0x2d60238 '__NSConstantString_tag'"; + ASSERT_EQUALS("typedef struct __NSConstantString_tag __NSConstantString ;", parse(clang)); + } + + void typedefDecl3() { + const char clang[] = "|-TypedefDecl 0x2d60540 <> implicit __builtin_ms_va_list 'char *'\n" + "| `-PointerType 0x2d60500 'char *'\n" + "| `-BuiltinType 0x2d5f980 'char'"; + ASSERT_EQUALS("typedef char * __builtin_ms_va_list ;", parse(clang)); + } + + void unaryExprOrTypeTraitExpr1() { + const char clang[] = "`-VarDecl 0x24cc610 col:5 x 'int' cinit\n" + " `-ImplicitCastExpr 0x24cc6e8 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x24cc6c8 'unsigned long' sizeof 'int'\n"; + ASSERT_EQUALS("int x@1 = sizeof ( int ) ;", parse(clang)); + } + + void unaryExprOrTypeTraitExpr2() { + const char clang[] = "`-VarDecl 0x27c6c00 col:9 x 'int' cinit\n" + " `-ImplicitCastExpr 0x27c6cc8 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x27c6ca8 'unsigned long' sizeof\n" + " `-ParenExpr 0x27c6c88 'char [10]' lvalue\n" + " `-DeclRefExpr 0x27c6c60 'char [10]' lvalue Var 0x27c6b48 'buf' 'char [10]'"; + ASSERT_EQUALS("int x@1 = sizeof ( buf ) ;", parse(clang)); + } + + void unaryOperator() { + const char clang[] = "`-FunctionDecl 0x2dd9748 <1.cpp:2:1, col:44> col:5 foo 'int (int *)'\n" + " |-ParmVarDecl 0x2dd9680 col:19 used p 'int *'\n" + " `-CompoundStmt 0x2dd9908 \n" + " `-ReturnStmt 0x2dd98f0 \n" + " `-BinaryOperator 0x2dd98c8 'int' '/'\n" + " |-IntegerLiteral 0x2dd9830 'int' 100000\n" + " `-ImplicitCastExpr 0x2dd98b0 'int' \n" + " `-UnaryOperator 0x2dd9890 'int' lvalue prefix '*' cannot overflow\n" + " `-ImplicitCastExpr 0x2dd9878 'int *' \n" + " `-DeclRefExpr 0x2dd9850 'int *' lvalue ParmVar 0x2dd9680 'p' 'int *'"; + ASSERT_EQUALS("int foo ( int * p@1 ) { return 100000 / * p@1 ; }", parse(clang)); + } + + void vardecl1() { + const char clang[] = "|-VarDecl 0x32b8aa0 <1.c:1:1, col:9> col:5 used a 'int' cinit\n" + "| `-IntegerLiteral 0x32b8b40 'int' 1\n" + "`-VarDecl 0x32b8b78 col:5 b 'int' cinit\n" + " `-ImplicitCastExpr 0x32b8c00 'int' \n" + " `-DeclRefExpr 0x32b8bd8 'int' lvalue Var 0x32b8aa0 'a' 'int'"; + + ASSERT_EQUALS("int a@1 = 1 ; int b@2 = a@1 ;", + parse(clang)); + } + + void vardecl2() { + const char clang[] = "|-VarDecl 0x3873b50 <1.c:1:1, col:9> col:5 used a 'int [10]'\n" + "`-FunctionDecl 0x3873c38 line:3:6 foo 'void ()'\n" + " `-CompoundStmt 0x3873dd0 \n" + " `-BinaryOperator 0x3873da8 'int' '='\n" + " |-ArraySubscriptExpr 0x3873d60 'int' lvalue\n" + " | |-ImplicitCastExpr 0x3873d48 'int *' \n" + " | | `-DeclRefExpr 0x3873cd8 'int [10]' lvalue Var 0x3873b50 'a' 'int [10]'\n" + " | `-IntegerLiteral 0x3873d00 'int' 0\n" + " `-IntegerLiteral 0x3873d88 'int' 0\n"; + + ASSERT_EQUALS("int [10] a@1 ; void foo ( ) { a@1 [ 0 ] = 0 ; }", + parse(clang)); + } + + void vardecl3() { + const char clang[] = "`-VarDecl 0x25a8aa0 <1.c:1:1, col:12> col:12 p 'const int *'"; + ASSERT_EQUALS("const int * p@1 ;", parse(clang)); + } + + void vardecl4() { + const char clang[] = "|-VarDecl 0x23d6c78 col:14 stdin 'FILE *' extern"; + ASSERT_EQUALS("FILE * stdin@1 ;", parse(clang)); + } + + void vardecl5() { + const char clang[] = "|-VarDecl 0x2e31fc0 col:26 sys_errlist 'const char *const []' extern"; + ASSERT_EQUALS("const char * const [] sys_errlist@1 ;", parse(clang)); + } + + void vardecl6() { + const char clang[] = "`-VarDecl 0x278e170 <1.c:1:1, col:16> col:12 x 'int' static cinit\n" + " `-IntegerLiteral 0x278e220 'int' 3"; + ASSERT_EQUALS("static int x@1 = 3 ;", parse(clang)); + } + + void vardecl7() { + const char clang[] = "`-VarDecl 0x2071f20 <1.cpp:2:1, col:23> col:9 start 'void *(*)(void *)'"; + ASSERT_EQUALS("void * * start@1 ;", parse(clang)); + } + + void whileStmt1() { + const char clang[] = "`-FunctionDecl 0x3d45b18 <1.c:1:1, line:3:1> line:1:6 foo 'void ()'\n" + " `-CompoundStmt 0x3d45c48 \n" + " `-WhileStmt 0x3d45c28 \n" + " |-<<>>\n" + " |-IntegerLiteral 0x3d45bf8 'int' 0\n" + " `-NullStmt 0x3d45c18 "; + ASSERT_EQUALS("void foo ( ) { while ( 0 ) { ; } }", + parse(clang)); + } + + void whileStmt2() { + // clang 9 + const char clang[] = "`-FunctionDecl 0x1c99ac8 <1.cpp:1:1, col:27> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x1c99c10 \n" + " `-WhileStmt 0x1c99bf8 \n" + " |-ImplicitCastExpr 0x1c99bd0 'bool' \n" + " | `-IntegerLiteral 0x1c99bb0 'int' 1\n" + " `-CompoundStmt 0x1c99be8 "; + ASSERT_EQUALS("void foo ( ) { while ( 1 ) { } }", parse(clang)); + } + + +#define GET_SYMBOL_DB(AST) \ + Settings settings; \ + settings.clang = true; \ + settings.platform(cppcheck::Platform::PlatformType::Unix64); \ + Tokenizer tokenizer(&settings, this); \ + std::istringstream istr(AST); \ + clangimport::parseClangAstDump(&tokenizer, istr); \ + const SymbolDatabase *db = tokenizer.getSymbolDatabase(); \ + ASSERT(db) + + void tokenIndex() { + const char clang[] = "`-FunctionDecl 0x1e07dd0 <67.cpp:1:1, col:13> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x1e07eb8 "; + ASSERT_EQUALS("void foo ( ) { }", parse(clang)); + + GET_SYMBOL_DB(clang); + const Token *tok = tokenizer.tokens(); + ASSERT_EQUALS(tok->index() + 1, tok->next()->index()); + } + + void symbolDatabaseEnum1() { + const char clang[] = "|-NamespaceDecl 0x29ad5f8 <1.cpp:1:1, line:3:1> line:1:11 ns\n" + "| `-EnumDecl 0x29ad660 col:6 referenced abc\n" + "| |-EnumConstantDecl 0x29ad720 col:11 a 'ns::abc'\n" + "| |-EnumConstantDecl 0x29ad768 col:13 b 'ns::abc'\n" + "| `-EnumConstantDecl 0x29ad7b0 col:15 referenced c 'ns::abc'\n" + "`-VarDecl 0x29ad898 col:9 x 'ns::abc':'ns::abc' cinit\n" + " `-DeclRefExpr 0x29ad998 'ns::abc' EnumConstant 0x29ad7b0 'c' 'ns::abc'\n"; + + ASSERT_EQUALS("namespace ns { enum abc { a , b , c } } ns :: abc x@1 = c ;", parse(clang)); + + GET_SYMBOL_DB(clang); + + // Enum scope and type + ASSERT_EQUALS(3, db->scopeList.size()); + const Scope &enumScope = db->scopeList.back(); + ASSERT_EQUALS(Scope::ScopeType::eEnum, enumScope.type); + ASSERT_EQUALS("abc", enumScope.className); + const Type *enumType = enumScope.definedType; + ASSERT_EQUALS("abc", enumType->name()); + + // Variable + const Token *vartok = Token::findsimplematch(tokenizer.tokens(), "x"); + ASSERT(vartok); + ASSERT(vartok->variable()); + ASSERT(vartok->variable()->valueType()); + ASSERT_EQUALS(uintptr_t(&enumScope), uintptr_t(vartok->variable()->valueType()->typeScope)); + } + + void symbolDatabaseFunction1() { + const char clang[] = "|-FunctionDecl 0x3aea7a0 <1.cpp:2:1, col:22> col:6 used foo 'void (int, int)'\n" + " |-ParmVarDecl 0x3aea650 col:14 x 'int'\n" + " |-ParmVarDecl 0x3aea6c8 col:21 y 'int'\n" + " `-CompoundStmt 0x3d45c48 \n"; + + GET_SYMBOL_DB(clang); + + // There is a function foo that has 2 arguments + ASSERT_EQUALS(1, db->functionScopes.size()); + const Scope *scope = db->functionScopes[0]; + const Function *func = scope->function; + ASSERT_EQUALS(2, func->argCount()); + ASSERT_EQUALS("x", func->getArgumentVar(0)->name()); + ASSERT_EQUALS("y", func->getArgumentVar(1)->name()); + ASSERT_EQUALS(ValueType::Type::INT, func->getArgumentVar(0)->valueType()->type); + ASSERT_EQUALS(ValueType::Type::INT, func->getArgumentVar(1)->valueType()->type); + } + + void symbolDatabaseFunction2() { + const char clang[] = "|-FunctionDecl 0x3aea7a0 <1.cpp:2:1, col:22> col:6 used foo 'void (int, int)'\n" + "| |-ParmVarDecl 0x3aea650 col:14 'int'\n" + "| |-ParmVarDecl 0x3aea6c8 col:21 'int'\n" + " `-CompoundStmt 0x3d45c48 \n"; + + GET_SYMBOL_DB(clang); + + // There is a function foo that has 2 arguments + ASSERT_EQUALS(1, db->functionScopes.size()); + const Scope *scope = db->functionScopes[0]; + const Function *func = scope->function; + ASSERT_EQUALS(2, func->argCount()); + ASSERT_EQUALS(0, (long long)func->getArgumentVar(0)->nameToken()); + ASSERT_EQUALS(0, (long long)func->getArgumentVar(1)->nameToken()); + } + + void symbolDatabaseFunction3() { // #9640 + const char clang[] = "`-FunctionDecl 0x238fcd8 <9640.cpp:1:1, col:26> col:6 used bar 'bool (const char, int &)'\n" + " |-ParmVarDecl 0x238fb10 col:20 'const char'\n" + " |-ParmVarDecl 0x238fbc0 col:26 'int &'\n" + " `-CompoundStmt 0x3d45c48 \n"; + + GET_SYMBOL_DB(clang); + + // There is a function foo that has 2 arguments + ASSERT_EQUALS(1, db->functionScopes.size()); + const Scope *scope = db->functionScopes[0]; + const Function *func = scope->function; + ASSERT_EQUALS(2, func->argCount()); + ASSERT_EQUALS(false, func->getArgumentVar(0)->isReference()); + ASSERT_EQUALS(true, func->getArgumentVar(1)->isReference()); + } + + void symbolDatabaseFunctionConst() { + const char clang[] = "`-CXXRecordDecl 0x7e2d98 <1.cpp:2:1, line:5:1> line:2:7 class foo definition\n" + " `-CXXMethodDecl 0x7e3000 col:8 f 'void () const'"; + + GET_SYMBOL_DB(clang); + + // There is a function f that is const + ASSERT_EQUALS(2, db->scopeList.size()); + ASSERT_EQUALS(1, db->scopeList.back().functionList.size()); + const Function &func = db->scopeList.back().functionList.back(); + ASSERT(func.isConst()); + } + + void symbolDatabaseVariableRef() { + const char clang[] = "`-FunctionDecl 0x1593df0 <3.cpp:1:1, line:4:1> line:1:6 foo 'void ()'\n" + " `-CompoundStmt 0x15940b0 \n" + " |-DeclStmt 0x1593f58 \n" + " | `-VarDecl 0x1593ef0 col:7 used x 'int'\n" + " `-DeclStmt 0x1594098 \n" + " `-VarDecl 0x1593fb8 col:8 ref 'int &' cinit\n" + " `-DeclRefExpr 0x1594020 'int' lvalue Var 0x1593ef0 'x' 'int'"; + GET_SYMBOL_DB(clang); + const Variable *refVar = db->variableList().back(); + ASSERT(refVar->isReference()); + } + + void symbolDatabaseVariableRRef() { + const char clang[] = "`-FunctionDecl 0x1a40df0 <3.cpp:1:1, line:4:1> line:1:6 foo 'void ()'\n" + " `-CompoundStmt 0x1a41180 \n" + " |-DeclStmt 0x1a40f58 \n" + " | `-VarDecl 0x1a40ef0 col:7 used x 'int'\n" + " `-DeclStmt 0x1a41168 \n" + " `-VarDecl 0x1a40fb8 col:9 ref 'int &&' cinit\n" + " `-ExprWithCleanups 0x1a410f8 'int' xvalue\n" + " `-MaterializeTemporaryExpr 0x1a41098 'int' xvalue extended by Var 0x1a40fb8 'ref' 'int &&'\n" + " `-BinaryOperator 0x1a41078 'int' '+'\n" + " |-ImplicitCastExpr 0x1a41060 'int' \n" + " | `-DeclRefExpr 0x1a41020 'int' lvalue Var 0x1a40ef0 'x' 'int'\n" + " `-IntegerLiteral 0x1a41040 'int' 1\n"; + + ASSERT_EQUALS("void foo ( ) { int x@1 ; int && ref@2 = x@1 + 1 ; }", parse(clang)); + + GET_SYMBOL_DB(clang); + const Variable *refVar = db->variableList().back(); + ASSERT(refVar->isReference()); + ASSERT(refVar->isRValueReference()); + } + + void symbolDatabaseVariablePointerRef() { + const char clang[] = "`-FunctionDecl 0x9b4f10 <3.cpp:1:1, col:17> col:6 used foo 'void (int *&)'\n" + " `-ParmVarDecl 0x9b4e40 col:16 p 'int *&'\n"; + + ASSERT_EQUALS("void foo ( int * & p@1 ) ;", parse(clang)); + + GET_SYMBOL_DB(clang); + const Variable *p = db->variableList().back(); + ASSERT(p->isPointer()); + ASSERT(p->isReference()); + } + + void symbolDatabaseNodeType1() { + const char clang[] = "`-FunctionDecl 0x32438c0 line:5:6 foo 'a::b (a::b)'\n" + " |-ParmVarDecl 0x32437b0 col:15 used i 'a::b':'long'\n" + " `-CompoundStmt 0x3243a60 \n" + " `-ReturnStmt 0x3243a48 \n" + " `-BinaryOperator 0x3243a20 'long' '+'\n" + " |-ImplicitCastExpr 0x32439f0 'a::b':'long' \n" + " | `-DeclRefExpr 0x32439a8 'a::b':'long' lvalue ParmVar 0x32437b0 'i' 'a::b':'long'\n" + " `-ImplicitCastExpr 0x3243a08 'long' \n" + " `-IntegerLiteral 0x32439d0 'int' 1\n"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "i + 1"); + ASSERT(!!tok); + ASSERT(!!tok->valueType()); + ASSERT_EQUALS("signed long", tok->valueType()->str()); + } + + void symbolDatabaseForVariable() { + const char clang[] = "`-FunctionDecl 0x38f8bb0 <10100.c:2:1, line:4:1> line:2:6 f 'void (void)'\n" + " `-CompoundStmt 0x38f8d88 \n" + " `-ForStmt 0x38f8d50 \n" + " |-DeclStmt 0x38f8d28 \n" + " | `-VarDecl 0x38f8ca8 col:14 i 'int' cinit\n" + " | `-IntegerLiteral 0x38f8d08 'int' 0\n" + " |-<<>>\n" + " |-<<>>\n" + " |-<<>>\n" + " `-CompoundStmt 0x38f8d40 "; + + ASSERT_EQUALS("void f ( ) { for ( int i@1 = 0 ; ; ) { } }", parse(clang)); + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "i"); + ASSERT(!!tok); + ASSERT(!!tok->variable()); + ASSERT_EQUALS(Scope::ScopeType::eFor, tok->variable()->scope()->type); + } + + void valueFlow1() { + // struct S { int x; int buf[10]; } ; int sz = sizeof(struct S); + const char clang[] = "|-RecordDecl 0x2fc5a88 <1.c:1:1, line:4:1> line:1:8 struct S definition\n" + "| |-FieldDecl 0x2fc5b48 col:7 x 'int'\n" + "| `-FieldDecl 0x2fc5c10 col:7 buf 'int [10]'\n" + "`-VarDecl 0x2fc5c70 col:5 sz 'int' cinit\n" + " `-ImplicitCastExpr 0x2fc5d88 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x2fc5d68 'unsigned long' sizeof 'struct S':'struct S'"; + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "sizeof ("); + ASSERT(!!tok); + tok = tok->next(); + ASSERT(tok->hasKnownIntValue()); + ASSERT_EQUALS(44, tok->getKnownIntValue()); + } + + void valueFlow2() { + // int buf[42]; + // int x = sizeof(buf); + const char clang[] = "|-VarDecl 0x10f6de8 <66.cpp:3:1, col:11> col:5 referenced buf 'int [42]'\n" + "`-VarDecl 0x10f6eb0 col:5 x 'int' cinit\n" + " `-ImplicitCastExpr 0x10f6f78 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x10f6f58 'unsigned long' sizeof\n" + " `-ParenExpr 0x10f6f38 'int [42]' lvalue\n" + " `-DeclRefExpr 0x10f6f18 'int [42]' lvalue Var 0x10f6de8 'buf' 'int [42]' non_odr_use_unevaluated"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "sizeof ("); + ASSERT(!!tok); + tok = tok->next(); + // TODO ASSERT(tok->hasKnownIntValue()); + // TODO ASSERT_EQUALS(10, tok->getKnownIntValue()); + } + + void valueType1() { + const char clang[] = "`-FunctionDecl 0x32438c0 line:5:6 foo 'a::b (a::b)'\n" + " |-ParmVarDecl 0x32437b0 col:15 used i 'a::b':'long'\n" + " `-CompoundStmt 0x3243a60 \n" + " `-ReturnStmt 0x3243a48 \n" + " `-ImplicitCastExpr 0x2176ca8 'int' \n" + " `-ImplicitCastExpr 0x2176c90 'bool' \n" + " `-DeclRefExpr 0x2176c60 'bool' lvalue Var 0x2176bd0 'e' 'bool'\n"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "e"); + ASSERT(!!tok); + ASSERT(!!tok->valueType()); + ASSERT_EQUALS("bool", tok->valueType()->str()); + } + + void valueType2() { + const char clang[] = "`-VarDecl 0xc9eda0 <1.cpp:2:1, col:17> col:13 s 'const char *' cinit\n" + " `-ImplicitCastExpr 0xc9eef0 'const char *' \n" + " `-StringLiteral 0xc9eed0 'const char [6]' lvalue \"hello\"\n"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "\"hello\""); + ASSERT(!!tok); + ASSERT(!!tok->valueType()); + ASSERT_EQUALS("const signed char *", tok->valueType()->str()); + } +}; + +REGISTER_TEST(TestClangImport)