diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index ccc6c7a0995..da761030ec7 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1213,7 +1213,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str if (!hasValidConfig && currCfg == *configurations.rbegin()) { // If there is no valid configuration then report error.. - preprocessor.error(o.location.file(), o.location.line, o.msg); + preprocessor.error(o.location.file(), o.location.line, o.msg, o.type); } continue; diff --git a/lib/cppcheck.h b/lib/cppcheck.h index 07df37cd697..c139df08ce1 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -203,7 +203,7 @@ class CPPCHECKLIB CppCheck { * @brief Check a file using stream * @param file the file * @param cfgname cfg name - * @param createTokenList a function to create the simplecpp::TokenList with + * @param createTokenList a function to create the simplecpp::TokenList with - throws simplecpp::Output * @return number of errors found */ unsigned int checkInternal(const FileWithDetails& file, const std::string &cfgname, int fileIndex, const CreateTokenListFn& createTokenList); diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index c8d7b02dd34..e62ed6b806d 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -45,14 +45,17 @@ const std::set ErrorLogger::mCriticalErrorIds{ "cppcheckError", "cppcheckLimit", + "includeNestedTooDeeply", "internalAstError", "instantiationError", "internalError", + "missingFile", "premium-internalError", "premium-invalidArgument", "premium-invalidLicense", "preprocessorErrorDirective", "syntaxError", + "unhandledChar", "unknownMacro" }; diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index cb1c93aaf7f..91b2e1f9ebb 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -312,7 +312,7 @@ void Preprocessor::inlineSuppressions(SuppressionList &suppressions) ::addInlineSuppressions(filedata->tokens, mSettings, suppressions, err); } for (const BadInlineSuppression &bad : err) { - error(bad.file, bad.line, bad.errmsg); + error(bad.file, bad.line, bad.errmsg, simplecpp::Output::ERROR); // TODO: use individual (non-fatal) ID } } @@ -860,7 +860,7 @@ bool Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool sh case simplecpp::Output::ERROR: hasError = true; if (!startsWith(out.msg,"#error") || showerror) - error(out.location.file(), out.location.line, out.msg); + error(out.location.file(), out.location.line, out.msg, out.type); break; case simplecpp::Output::WARNING: case simplecpp::Output::PORTABILITY_BACKSLASH: @@ -877,13 +877,13 @@ bool Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool sh case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: hasError = true; - error(out.location.file(), out.location.line, out.msg); + error(out.location.file(), out.location.line, out.msg, out.type); break; case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: case simplecpp::Output::FILE_NOT_FOUND: case simplecpp::Output::DUI_ERROR: hasError = true; - error("", 0, out.msg); + error("", 0, out.msg, out.type); break; } } @@ -891,7 +891,34 @@ bool Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool sh return hasError; } -void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg) +static std::string simplecppErrToId(simplecpp::Output::Type type) +{ + switch (type) { + case simplecpp::Output::ERROR: + return "preprocessorErrorDirective"; + case simplecpp::Output::SYNTAX_ERROR: + return "syntaxError"; + case simplecpp::Output::UNHANDLED_CHAR_ERROR: + return "unhandledChar"; + case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: + return "includeNestedTooDeeply"; + case simplecpp::Output::FILE_NOT_FOUND: + return "missingFile"; + // should never occur + case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: + case simplecpp::Output::DUI_ERROR: + // handled separately + case simplecpp::Output::MISSING_HEADER: + // no handled at all (warnings) + case simplecpp::Output::WARNING: + case simplecpp::Output::PORTABILITY_BACKSLASH: + throw std::runtime_error("unexpected simplecpp::Output type " + std::to_string(type)); + } + + cppcheck::unreachable(); +} + +void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg, simplecpp::Output::Type type) { std::list locationList; if (!filename.empty()) { @@ -905,7 +932,7 @@ void Preprocessor::error(const std::string &filename, unsigned int linenr, const mFile0, Severity::error, msg, - "preprocessorErrorDirective", + simplecppErrToId(type), Certainty::normal)); } @@ -935,7 +962,11 @@ void Preprocessor::getErrorMessages(ErrorLogger &errorLogger, const Settings &se Preprocessor preprocessor(tokens, settings, errorLogger, Standards::Language::CPP); preprocessor.missingInclude("", 1, 2, "", UserHeader); preprocessor.missingInclude("", 1, 2, "", SystemHeader); - preprocessor.error("", 1, "#error message"); // #error .. + preprocessor.error("", 1, "message", simplecpp::Output::ERROR); + preprocessor.error("", 1, "message", simplecpp::Output::SYNTAX_ERROR); + preprocessor.error("", 1, "message", simplecpp::Output::UNHANDLED_CHAR_ERROR); + preprocessor.error("", 1, "message", simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY); + preprocessor.error("", 1, "message", simplecpp::Output::FILE_NOT_FOUND); } void Preprocessor::dump(std::ostream &out) const diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 0547213ddc5..4f64cc19100 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -142,7 +142,7 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { bool reportOutput(const simplecpp::OutputList &outputList, bool showerror); - void error(const std::string &filename, unsigned int linenr, const std::string &msg); + void error(const std::string &filename, unsigned int linenr, const std::string &msg, simplecpp::Output::Type type); private: static bool hasErrors(const simplecpp::Output &output); diff --git a/releasenotes.txt b/releasenotes.txt index 53917b6e318..ece1223afeb 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -13,6 +13,7 @@ GUI: - Changed interface: +- some `preprocessorErrorDirective` and `syntaxError` errors got more specific error IDs. - Deprecations: diff --git a/test/cli/other_test.py b/test/cli/other_test.py index b6c8f2392eb..81bef0bbf1e 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -3890,8 +3890,7 @@ def test_simplecpp_unhandled_char(tmp_path): assert stdout.splitlines() == [] assert stderr.splitlines() == [ # TODO: lacks column information - # TODO: should report another ID - '{}:2:0: error: The code contains unhandled character(s) (character code=228). Neither unicode nor extended ascii is supported. [preprocessorErrorDirective]'.format(test_file) + '{}:2:0: error: The code contains unhandled character(s) (character code=228). Neither unicode nor extended ascii is supported. [unhandledChar]'.format(test_file) ] @@ -3922,10 +3921,9 @@ def test_simplecpp_include_nested_too_deeply(tmp_path): test_h = tmp_path / 'test_398.h' assert stderr.splitlines() == [ # TODO: should only report the error once - # TODO: should report another ID # TODO: lacks column information - '{}:1:0: error: #include nested too deeply [preprocessorErrorDirective]'.format(test_h), - '{}:1:0: error: #include nested too deeply [preprocessorErrorDirective]'.format(test_h) + '{}:1:0: error: #include nested too deeply [includeNestedTooDeeply]'.format(test_h), + '{}:1:0: error: #include nested too deeply [includeNestedTooDeeply]'.format(test_h) ] @@ -3946,8 +3944,7 @@ def test_simplecpp_syntax_error(tmp_path): assert stdout.splitlines() == [] assert stderr.splitlines() == [ # TODO: should only report the error once - # TODO: should report another ID # TODO: lacks column information - '{}:1:0: error: No header in #include [preprocessorErrorDirective]'.format(test_file), - '{}:1:0: error: No header in #include [preprocessorErrorDirective]'.format(test_file) + '{}:1:0: error: No header in #include [syntaxError]'.format(test_file), + '{}:1:0: error: No header in #include [syntaxError]'.format(test_file) ] diff --git a/test/cli/project_test.py b/test/cli/project_test.py index 25fcd587b73..ec1ef56d7d1 100644 --- a/test/cli/project_test.py +++ b/test/cli/project_test.py @@ -64,7 +64,7 @@ def test_json_entry_file_not_found(tmpdir): "--project=" + str(project_file) ]) assert 0 == ret - assert stderr == f"nofile:0:0: error: File is missing: {missing_file_posix} [preprocessorErrorDirective]\n" + assert stderr == f"nofile:0:0: error: File is missing: {missing_file_posix} [missingFile]\n" def test_json_no_arguments(tmpdir): diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 1c8f570750e..ea0863eff99 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -1529,7 +1529,7 @@ class TestPreprocessor : public TestFixture { const std::map actual = getcode(settings0, *this, filedata); ASSERT_EQUALS(0, actual.size()); - ASSERT_EQUALS("[file.c:2:0]: (error) No pair for character ('). Can't process file. File is either invalid or unicode, which is currently not supported. [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.c:2:0]: (error) No pair for character ('). Can't process file. File is either invalid or unicode, which is currently not supported. [syntaxError]\n", errout_str()); } } @@ -1544,7 +1544,7 @@ class TestPreprocessor : public TestFixture { const std::string actual(expandMacros(filedata, *this)); ASSERT_EQUALS("", actual); - ASSERT_EQUALS("[file.cpp:3:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.cpp:3:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [syntaxError]\n", errout_str()); } { @@ -1557,7 +1557,7 @@ class TestPreprocessor : public TestFixture { const std::string actual(expandMacros(filedata, *this)); ASSERT_EQUALS("", actual); - ASSERT_EQUALS("[abc.h:2:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[abc.h:2:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [syntaxError]\n", errout_str()); } { @@ -1570,7 +1570,7 @@ class TestPreprocessor : public TestFixture { const std::string actual(expandMacros(filedata, *this)); ASSERT_EQUALS("", actual); - ASSERT_EQUALS("[file.cpp:2:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.cpp:2:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [syntaxError]\n", errout_str()); } { @@ -1582,7 +1582,7 @@ class TestPreprocessor : public TestFixture { const std::string actual(expandMacros(filedata, *this)); ASSERT_EQUALS("", actual); - ASSERT_EQUALS("[file.cpp:2:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.cpp:2:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [syntaxError]\n", errout_str()); } { @@ -1598,7 +1598,7 @@ class TestPreprocessor : public TestFixture { // expand macros.. (void)expandMacros(filedata, *this); - ASSERT_EQUALS("[file.cpp:7:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.cpp:7:0]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported. [syntaxError]\n", errout_str()); } } @@ -1651,7 +1651,7 @@ class TestPreprocessor : public TestFixture { // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("", actual.at("")); - ASSERT_EQUALS("[file.c:6:0]: (error) failed to expand 'BC', Wrong number of parameters for macro 'BC'. [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.c:6:0]: (error) failed to expand 'BC', Wrong number of parameters for macro 'BC'. [syntaxError]\n", errout_str()); } void newline_in_macro() { @@ -1968,12 +1968,12 @@ class TestPreprocessor : public TestFixture { void invalid_define_1() { (void)getcode(settings0, *this, "#define =\n"); - ASSERT_EQUALS("[file.c:1:0]: (error) Failed to parse #define [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.c:1:0]: (error) Failed to parse #define [syntaxError]\n", errout_str()); } void invalid_define_2() { // #4036 (void)getcode(settings0, *this, "#define () {(int f(x) }\n"); - ASSERT_EQUALS("[file.c:1:0]: (error) Failed to parse #define [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[file.c:1:0]: (error) Failed to parse #define [syntaxError]\n", errout_str()); } void inline_suppressions() { @@ -2119,7 +2119,7 @@ class TestPreprocessor : public TestFixture { const char code[] = "#elif (){\n"; const std::string actual = getcodeforcfg(settings0, *this, code, "TEST", "test.c"); ASSERT_EQUALS("", actual); - ASSERT_EQUALS("[test.c:1:0]: (error) #elif without #if [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[test.c:1:0]: (error) #elif without #if [syntaxError]\n", errout_str()); } void getConfigs1() { @@ -2368,8 +2368,8 @@ class TestPreprocessor : public TestFixture { // Preprocess => don't crash.. (void)getcode(settings0, *this, filedata); ASSERT_EQUALS( - "[file.c:1:0]: (error) Syntax error in #ifdef [preprocessorErrorDirective]\n" - "[file.c:1:0]: (error) Syntax error in #ifdef [preprocessorErrorDirective]\n", errout_str()); + "[file.c:1:0]: (error) Syntax error in #ifdef [syntaxError]\n" + "[file.c:1:0]: (error) Syntax error in #ifdef [syntaxError]\n", errout_str()); } void garbage() { @@ -2631,7 +2631,7 @@ class TestPreprocessor : public TestFixture { settings.standards.setStd("c++11"); ASSERT_EQUALS("", getcodeforcfg(settings, *this, code, "", "test.cpp")); - ASSERT_EQUALS("[test.cpp:1:0]: (error) failed to evaluate #if condition, undefined function-like macro invocation: __has_include( ... ) [preprocessorErrorDirective]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:1:0]: (error) failed to evaluate #if condition, undefined function-like macro invocation: __has_include( ... ) [syntaxError]\n", errout_str()); // TODO: use individual ID settings.standards.setStd("c++17"); ASSERT_EQUALS("", getcodeforcfg(settings, *this, code, "", "test.cpp")); diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index a1abdf1fb58..90bd5bac31f 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1355,7 +1355,7 @@ class TestSuppressions : public TestFixture { "[!VAR \"BC\" = \"$BC + 1\"!][!//\n" "[!ENDIF!][!//\n" "};"; - ASSERT_EQUALS(0, (this->*check)(code, "preprocessorErrorDirective:test.cpp:4")); + ASSERT_EQUALS(0, (this->*check)(code, "syntaxError:test.cpp:4")); ASSERT_EQUALS("", errout_str()); }