diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index d8be7be309d..5dd0576db71 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -917,7 +917,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string std::vector files; simplecpp::TokenList tokens(*fileStream, files, file.spath()); if (analyzerInformation) { - const Preprocessor preprocessor(mSettings, mErrorLogger); + const Preprocessor preprocessor(mSettings, mErrorLogger, Standards::Language::C); hash = calculateHash(preprocessor, tokens, mSettings, mSuppressions); } tokenlist.createTokens(std::move(tokens)); @@ -926,7 +926,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string std::vector files; simplecpp::TokenList tokens(file.spath(), files); if (analyzerInformation) { - const Preprocessor preprocessor(mSettings, mErrorLogger); + const Preprocessor preprocessor(mSettings, mErrorLogger, file.lang()); hash = calculateHash(preprocessor, tokens, mSettings, mSuppressions); } tokenlist.createTokens(std::move(tokens)); @@ -973,7 +973,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string return mLogger->exitcode(); } - Preprocessor preprocessor(mSettings, mErrorLogger); + Preprocessor preprocessor(mSettings, mErrorLogger, file.lang()); if (!preprocessor.loadFiles(tokens1, files)) return mLogger->exitcode(); diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index db00e18a8d6..901dc57cffe 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -63,8 +64,13 @@ Directive::DirectiveToken::DirectiveToken(const simplecpp::Token & _tok) : char Preprocessor::macroChar = char(1); -Preprocessor::Preprocessor(const Settings& settings, ErrorLogger &errorLogger) : mSettings(settings), mErrorLogger(errorLogger) -{} +Preprocessor::Preprocessor(const Settings& settings, ErrorLogger &errorLogger, Standards::Language lang) + : mSettings(settings) + , mErrorLogger(errorLogger) + , mLang(lang) +{ + assert(mLang != Standards::Language::None); +} Preprocessor::~Preprocessor() { @@ -688,7 +694,7 @@ static void splitcfg(const std::string &cfg, std::list &defines, co } } -static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename) +static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, Standards::Language lang) { // TODO: make it possible to specify platform-dependent sizes simplecpp::DUI dui; @@ -716,8 +722,6 @@ static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cf dui.includePaths = mSettings.includePaths; // -I dui.includes = mSettings.userIncludes; // --include // TODO: use mSettings.standards.stdValue instead - // TODO: error out on unknown language? - const Standards::Language lang = Path::identify(filename, mSettings.cppHeaderProbe); if (lang == Standards::Language::CPP) { dui.std = mSettings.standards.getCPP(); splitcfg(mSettings.platform.getLimitsDefines(Standards::getCPP(dui.std)), dui.defines, ""); @@ -773,7 +777,7 @@ void Preprocessor::handleErrors(const simplecpp::OutputList& outputList, bool th bool Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files) { - const simplecpp::DUI dui = createDUI(mSettings, "", files[0]); + const simplecpp::DUI dui = createDUI(mSettings, "", mLang); simplecpp::OutputList outputList; mTokenLists = simplecpp::load(rawtokens, files, dui, &outputList); @@ -812,7 +816,7 @@ void Preprocessor::setPlatformInfo(simplecpp::TokenList &tokens, const Settings& simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError) { - const simplecpp::DUI dui = createDUI(mSettings, cfg, files[0]); + const simplecpp::DUI dui = createDUI(mSettings, cfg, mLang); simplecpp::OutputList outputList; std::list macroUsage; @@ -927,7 +931,7 @@ void Preprocessor::missingInclude(const std::string &filename, unsigned int line void Preprocessor::getErrorMessages(ErrorLogger &errorLogger, const Settings &settings) { - Preprocessor preprocessor(settings, errorLogger); + Preprocessor preprocessor(settings, errorLogger, Standards::Language::CPP); preprocessor.missingInclude("", 1, "", UserHeader); preprocessor.missingInclude("", 1, "", SystemHeader); preprocessor.error("", 1, "#error message"); // #error .. diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 3086ca4cb0a..8659cc36fb7 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -22,6 +22,7 @@ //--------------------------------------------------------------------------- #include "config.h" +#include "standards.h" #include #include @@ -108,7 +109,7 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { /** character that is inserted in expanded macros */ static char macroChar; - explicit Preprocessor(const Settings& settings, ErrorLogger &errorLogger); + explicit Preprocessor(const Settings& settings, ErrorLogger &errorLogger, Standards::Language lang); virtual ~Preprocessor(); void inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions); @@ -180,6 +181,7 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { /** filename for cpp/c file - useful when reporting errors */ std::string mFile0; + Standards::Language mLang{Standards::Language::None}; /** simplecpp tracking info */ std::list mMacroUsage; diff --git a/test/cli/other_test.py b/test/cli/other_test.py index dd0f803f8e5..ac1c303a804 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -3563,4 +3563,48 @@ def test_suppress_unmatched_wildcard_unchecked(tmp_path): 'tes?.c:-1:0: information: Unmatched suppression: id [unmatchedSuppression]', '*:-1:0: information: Unmatched suppression: id2 [unmatchedSuppression]', 'test*.c:-1:0: information: Unmatched suppression: * [unmatchedSuppression]' - ] \ No newline at end of file + ] + + +def test_preprocess_enforced_c(tmp_path): # #10989 + test_file = tmp_path / 'test.cpp' + with open(test_file, 'wt') as f: + f.write( +"""#ifdef __cplusplus +#error "err" +#endif""") + + args = [ + '-q', + '--template=simple', + '--language=c', + str(test_file) + ] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0, stdout if stdout else stderr + assert stdout.splitlines() == [] + assert stderr.splitlines() == [] + + +def test_preprocess_enforced_cpp(tmp_path): # #10989 + test_file = tmp_path / 'test.c' + with open(test_file, 'wt') as f: + f.write( +"""#ifdef __cplusplus +#error "err" +#endif""") + + args = [ + '-q', + '--template=simple', + '--language=c++', + str(test_file) + ] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0, stdout if stdout else stderr + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + '{}:2:2: error: #error "err" [preprocessorErrorDirective]'.format(test_file) + ] diff --git a/test/helpers.cpp b/test/helpers.cpp index b91187b40ca..8f9d83dd0d1 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -136,7 +136,7 @@ std::map PreprocessorHelper::getcode(const Settings& s std::istringstream istr(code); simplecpp::TokenList tokens(istr, files, Path::simplifyPath(filename), &outputList); - Preprocessor preprocessor(settings, errorlogger); + Preprocessor preprocessor(settings, errorlogger, Path::identify(tokens.getFiles()[0], false)); if (inlineSuppression) preprocessor.inlineSuppressions(tokens, *inlineSuppression); preprocessor.removeComments(tokens); @@ -168,7 +168,7 @@ void SimpleTokenizer2::preprocess(const char code[], std::vector &f std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, file0); - Preprocessor preprocessor(tokenizer.getSettings(), errorlogger); + Preprocessor preprocessor(tokenizer.getSettings(), errorlogger, Path::identify(tokens1.getFiles()[0], false)); simplecpp::TokenList tokens2 = preprocessor.preprocess(tokens1, "", files, true); // Tokenizer.. diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 2303e8ed944..73dbff0c4aa 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -25,6 +25,7 @@ #include "platform.h" #include "preprocessor.h" #include "settings.h" +#include "standards.h" #include "suppressions.h" #include "tokenlist.h" #include "fixture.h" @@ -54,7 +55,7 @@ class TestPreprocessor : public TestFixture { simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1 = simplecpp::TokenList(istr, files, "file.cpp", &outputList); - Preprocessor p(settingsDefault, errorLogger); + Preprocessor p(settingsDefault, errorLogger, Path::identify(tokens1.getFiles()[0], false)); simplecpp::TokenList tokens2 = p.preprocess(tokens1, "", files, true); p.reportOutput(outputList, true); return tokens2.stringify(); @@ -87,7 +88,7 @@ class TestPreprocessor : public TestFixture { std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, "test.cpp"); - const Preprocessor preprocessor(settingsDefault, errorLogger); + const Preprocessor preprocessor(settingsDefault, errorLogger, Path::identify(tokens1.getFiles()[0], false)); return preprocessor.getRemarkComments(tokens1); } @@ -301,11 +302,12 @@ class TestPreprocessor : public TestFixture { settings.userDefines = arg + 2; if (arg && std::strncmp(arg,"-U",2)==0) settings.userUndefs.insert(arg+2); - Preprocessor preprocessor(settings, *this); std::vector files; std::istringstream istr(filedata); + // TODO: this adds an empty filename simplecpp::TokenList tokens(istr,files); tokens.removeComments(); + Preprocessor preprocessor(settings, *this, Standards::Language::C); // TODO: do we need to consider #file? const std::set configs = preprocessor.getConfigs(tokens); std::string ret; for (const std::string & config : configs) @@ -314,11 +316,12 @@ class TestPreprocessor : public TestFixture { } std::size_t getHash(const char filedata[]) { - Preprocessor preprocessor(settingsDefault, *this); std::vector files; std::istringstream istr(filedata); + // TODO: this adds an empty filename simplecpp::TokenList tokens(istr,files); tokens.removeComments(); + Preprocessor preprocessor(settingsDefault, *this, Standards::Language::C); // TODO: do we need to consider #file? return preprocessor.calculateHash(tokens, ""); } @@ -478,7 +481,7 @@ class TestPreprocessor : public TestFixture { { const Settings settings = settingsBuilder().platform(Platform::Type::Unix32).build(); Preprocessor::setPlatformInfo(tokens, settings); - Preprocessor preprocessor(settings, *this); + Preprocessor preprocessor(settings, *this, Path::identify(tokens.getFiles()[0], false)); ASSERT_EQUALS("\n1", preprocessor.getcode(tokens, "", files, false)); } @@ -486,7 +489,7 @@ class TestPreprocessor : public TestFixture { { const Settings settings = settingsBuilder().platform(Platform::Type::Unix64).build(); Preprocessor::setPlatformInfo(tokens, settings); - Preprocessor preprocessor(settings, *this); + Preprocessor preprocessor(settings, *this, Path::identify(tokens.getFiles()[0], false)); ASSERT_EQUALS("\n\n\n2", preprocessor.getcode(tokens, "", files, false)); } } @@ -1211,7 +1214,7 @@ class TestPreprocessor : public TestFixture { "#undef z\n" "int z;\n" "z = 0;\n"; - ASSERT_EQUALS("\n\nint z ;\nz = 0 ;", PreprocessorHelper::getcode(settings0, *this, filedata, "", "")); + ASSERT_EQUALS("\n\nint z ;\nz = 0 ;", PreprocessorHelper::getcode(settings0, *this, filedata, "", "test.c")); } } @@ -1629,14 +1632,14 @@ class TestPreprocessor : public TestFixture { "#if A\n" "FOO\n" "#endif"; - ASSERT_EQUALS("", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } { const char filedata[] = "#define A 1\n" "#if A==1\n" "FOO\n" "#endif"; - ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } } @@ -1646,7 +1649,7 @@ class TestPreprocessor : public TestFixture { "#if (B==A) || (B==C)\n" "FOO\n" "#endif"; - ASSERT_EQUALS("\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } void define_if3() { @@ -1654,7 +1657,7 @@ class TestPreprocessor : public TestFixture { "#if (A==0)\n" "FOO\n" "#endif"; - ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } void define_if4() { @@ -1662,7 +1665,7 @@ class TestPreprocessor : public TestFixture { "#if X==123\n" "FOO\n" "#endif"; - ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } void define_if5() { // #4516 - #define B (A & 0x00f0) @@ -1672,7 +1675,7 @@ class TestPreprocessor : public TestFixture { "#if B==0x0010\n" "FOO\n" "#endif"; - ASSERT_EQUALS("\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } { const char filedata[] = "#define A 0x00f0\n" @@ -1681,14 +1684,14 @@ class TestPreprocessor : public TestFixture { "#if C==0x0010\n" "FOO\n" "#endif"; - ASSERT_EQUALS("\n\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("\n\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } { const char filedata[] = "#define A (1+A)\n" // don't hang for recursive macros "#if A==1\n" "FOO\n" "#endif"; - ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","test.c")); } } diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 3126867e4dd..2870feb4cae 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -550,11 +550,11 @@ class TestTokenizer : public TestFixture { } void directiveDump(const char filedata[], const char filename[], const Settings& settings, std::ostream& ostr) { - Preprocessor preprocessor(settings, *this); std::istringstream istr(filedata); simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1(istr, files, filename, &outputList); + Preprocessor preprocessor(settings, *this, Path::identify(tokens1.getFiles()[0], false)); std::list directives = preprocessor.createDirectives(tokens1); TokenList tokenlist{&settings}; @@ -8573,7 +8573,7 @@ class TestTokenizerCompileLimits : public TestFixture std::vector files; const simplecpp::TokenList tokens1(fin, files, "", &outputList); const std::string filedata = tokens1.stringify(); - const std::string code = PreprocessorHelper::getcode(settingsDefault, *this, filedata, "", ""); + const std::string code = PreprocessorHelper::getcode(settingsDefault, *this, filedata, "", "test.c"); ASSERT_THROW_INTERNAL_EQUALS(tokenizeAndStringify(code), AST, "maximum AST depth exceeded"); } diff --git a/test/testtokenlist.cpp b/test/testtokenlist.cpp index 1d58c94da47..8c86bb3db31 100644 --- a/test/testtokenlist.cpp +++ b/test/testtokenlist.cpp @@ -165,7 +165,7 @@ class TestTokenList : public TestFixture { std::istringstream istr(code); std::vector files; simplecpp::TokenList tokens1(istr, files, "poll.h", nullptr); - Preprocessor preprocessor(settingsDefault, *this); + Preprocessor preprocessor(settingsDefault, *this, Path::identify(tokens1.getFiles()[0], false)); simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, "", files, true); TokenList tokenlist(&settingsDefault); tokenlist.createTokens(std::move(tokensP)); // do not assert