diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index 0340d7277..a9bd31b62 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -42,6 +42,7 @@ #endif #include +#include #include #include // EXIT_SUCCESS and EXIT_FAILURE #include @@ -217,21 +218,29 @@ int CppCheckExecutor::check_wrapper(CppCheck& cppcheck) return check_internal(cppcheck); } -bool CppCheckExecutor::reportSuppressions(const Settings &settings, bool unusedFunctionCheckEnabled, const std::list> &files, ErrorLogger& errorLogger) { - const auto& suppressions = settings.nomsg.getSuppressions(); - if (std::any_of(suppressions.begin(), suppressions.end(), [](const Suppressions::Suppression& s) { +bool CppCheckExecutor::reportSuppressions(const Settings &settings, const Suppressions& suppressions, bool unusedFunctionCheckEnabled, const std::list> &files, const std::list& fileSettings, ErrorLogger& errorLogger) { + const auto& suppr = suppressions.getSuppressions(); + if (std::any_of(suppr.begin(), suppr.end(), [](const Suppressions::Suppression& s) { return s.errorId == "unmatchedSuppression" && s.fileName.empty() && s.lineNumber == Suppressions::Suppression::NO_LINE; })) return false; bool err = false; if (settings.useSingleJob()) { + // the two inputs may only be used exclusively + assert(!(!files.empty() && !fileSettings.empty())); + for (std::list>::const_iterator i = files.cbegin(); i != files.cend(); ++i) { err |= Suppressions::reportUnmatchedSuppressions( - settings.nomsg.getUnmatchedLocalSuppressions(i->first, unusedFunctionCheckEnabled), errorLogger); + suppressions.getUnmatchedLocalSuppressions(i->first, unusedFunctionCheckEnabled), errorLogger); + } + + for (std::list::const_iterator i = fileSettings.cbegin(); i != fileSettings.cend(); ++i) { + err |= Suppressions::reportUnmatchedSuppressions( + suppressions.getUnmatchedLocalSuppressions(i->filename, unusedFunctionCheckEnabled), errorLogger); } } - err |= Suppressions::reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions(unusedFunctionCheckEnabled), errorLogger); + err |= Suppressions::reportUnmatchedSuppressions(suppressions.getUnmatchedGlobalSuppressions(unusedFunctionCheckEnabled), errorLogger); return err; } @@ -277,7 +286,7 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck) const cppcheck.analyseWholeProgram(settings.buildDir, mFiles, mFileSettings); if (settings.severity.isEnabled(Severity::information) || settings.checkConfiguration) { - const bool err = reportSuppressions(settings, cppcheck.isUnusedFunctionCheckEnabled(), mFiles, *mStdLogger); + const bool err = reportSuppressions(settings, settings.nomsg, cppcheck.isUnusedFunctionCheckEnabled(), mFiles, mFileSettings, *mStdLogger); if (err && returnValue == 0) returnValue = settings.exitCode; } diff --git a/cli/cppcheckexecutor.h b/cli/cppcheckexecutor.h index f54e49dee..8c26234b9 100644 --- a/cli/cppcheckexecutor.h +++ b/cli/cppcheckexecutor.h @@ -31,6 +31,7 @@ class CppCheck; class Settings; class ErrorLogger; +class Suppressions; /** * This class works as an example of how CppCheck can be used in external @@ -81,7 +82,7 @@ private: protected: - static bool reportSuppressions(const Settings &settings, bool unusedFunctionCheckEnabled, const std::list> &files, ErrorLogger& errorLogger); + static bool reportSuppressions(const Settings &settings, const Suppressions& suppressions, bool unusedFunctionCheckEnabled, const std::list> &files, const std::list& fileSettings, ErrorLogger& errorLogger); /** * Wrapper around check_internal diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 2f91cbdde..85432331a 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -53,18 +53,23 @@ private: TEST_CASE(suppressionsGlob); TEST_CASE(suppressionsGlobId); TEST_CASE(suppressionsFileNameWithExtraPath); - TEST_CASE(suppressionsSettings); - TEST_CASE(suppressionsSettingsThreads); + TEST_CASE(suppressionsSettingsFiles); + TEST_CASE(suppressionsSettingsFS); + TEST_CASE(suppressionsSettingsThreadsFiles); + TEST_CASE(suppressionsSettingsThreadsFS); #if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) - TEST_CASE(suppressionsSettingsProcesses); + TEST_CASE(suppressionsSettingsProcessesFiles); + TEST_CASE(suppressionsSettingsProcessesFS); #endif - TEST_CASE(suppressionsMultiFile); + TEST_CASE(suppressionsMultiFileFiles); + TEST_CASE(suppressionsMultiFileFS); TEST_CASE(suppressionsPathSeparator); TEST_CASE(suppressionsLine0); TEST_CASE(suppressionsFileComment); TEST_CASE(inlinesuppress); - TEST_CASE(inlinesuppress_symbolname); + TEST_CASE(inlinesuppress_symbolname_Files); + TEST_CASE(inlinesuppress_symbolname_FS); TEST_CASE(inlinesuppress_comment); TEST_CASE(multi_inlinesuppress); @@ -75,14 +80,21 @@ private: TEST_CASE(inlinesuppress_unusedFunction); // #4210 - unusedFunction TEST_CASE(globalsuppress_unusedFunction); // #4946 TEST_CASE(suppressionWithRelativePaths); // #4733 - TEST_CASE(suppressingSyntaxErrors); // #7076 - TEST_CASE(suppressingSyntaxErrorsInline); // #5917 - TEST_CASE(suppressingSyntaxErrorsWhileFileRead); // PR #1333 + TEST_CASE(suppressingSyntaxErrorsFiles); // #7076 + TEST_CASE(suppressingSyntaxErrorsFS); // #7076 + TEST_CASE(suppressingSyntaxErrorsInlineFiles); // #5917 + TEST_CASE(suppressingSyntaxErrorsInlineFS); // #5917 + TEST_CASE(suppressingSyntaxErrorsWhileFileReadFiles); // PR #1333 + TEST_CASE(suppressingSyntaxErrorsWhileFileReadFS); // PR #1333 TEST_CASE(symbol); - TEST_CASE(unusedFunction); + TEST_CASE(unusedFunctionFiles); + TEST_CASE(unusedFunctionFS); - TEST_CASE(suppressingSyntaxErrorAndExitCode); + TEST_CASE(suppressingSyntaxErrorAndExitCodeFiles); + TEST_CASE(suppressingSyntaxErrorAndExitCodeFS); + TEST_CASE(suppressingSyntaxErrorAndExitCodeMultiFileFiles); + TEST_CASE(suppressingSyntaxErrorAndExitCodeMultiFileFS); TEST_CASE(suppressLocal); TEST_CASE(suppressUnmatchedSuppressions); @@ -188,22 +200,45 @@ private: ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "x/../a.c", 123))); } + unsigned int checkSuppressionFiles(const char code[], const std::string &suppression = emptyString) { + return _checkSuppression(code, false, suppression); + } + + unsigned int checkSuppressionFS(const char code[], const std::string &suppression = emptyString) { + return _checkSuppression(code, true, suppression); + } + // Check the suppression - unsigned int checkSuppression(const char code[], const std::string &suppression = emptyString) { + unsigned int _checkSuppression(const char code[], bool useFS, const std::string &suppression = emptyString) { std::map files; files["test.cpp"] = code; - return checkSuppression(files, suppression); + return _checkSuppression(files, useFS, suppression); + } + + unsigned int checkSuppressionFiles(std::map &f, const std::string &suppression = emptyString) { + return _checkSuppression(f, false, suppression); + } + + unsigned int checkSuppressionFS(std::map &f, const std::string &suppression = emptyString) { + return _checkSuppression(f, true, suppression); } // Check the suppression for multiple files - unsigned int checkSuppression(std::map &f, const std::string &suppression = emptyString) { + unsigned int _checkSuppression(std::map &f, bool useFS, const std::string &suppression = emptyString) { // Clear the error log errout.str(""); - std::list> files; + std::list fileSettings; + + std::list> filelist; for (std::map::const_iterator i = f.cbegin(); i != f.cend(); ++i) { - files.emplace_back(i->first, i->second.size()); + filelist.emplace_back(i->first, i->second.size()); + if (useFS) { + FileSettings fs; + fs.filename = i->first; + fileSettings.emplace_back(std::move(fs)); + } } CppCheck cppCheck(*this, true, nullptr); @@ -217,26 +252,44 @@ private: if (!suppression.empty()) { EXPECT_EQ("", settings.nomsg.addSuppressionLine(suppression)); } - // TODO: test with FS - std::list fileSettings; - SingleExecutor executor(cppCheck, files, fileSettings, settings, settings.nomsg, *this); + std::vector> scopedfiles; - scopedfiles.reserve(files.size()); + scopedfiles.reserve(filelist.size()); for (std::map::const_iterator i = f.cbegin(); i != f.cend(); ++i) scopedfiles.emplace_back(new ScopedFile(i->first, i->second)); + // clear files list so only fileSettings are used + if (useFS) + filelist.clear(); + + SingleExecutor executor(cppCheck, filelist, fileSettings, settings, settings.nomsg, *this); const unsigned int exitCode = executor.check(); - CppCheckExecutor::reportSuppressions(settings, false, files, *this); + CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); return exitCode; } - unsigned int checkSuppressionThreads(const char code[], const std::string &suppression = emptyString) { + unsigned int checkSuppressionThreadsFiles(const char code[], const std::string &suppression = emptyString) { + return _checkSuppressionThreads(code, false, suppression); + } + + unsigned int checkSuppressionThreadsFS(const char code[], const std::string &suppression = emptyString) { + return _checkSuppressionThreads(code, true, suppression); + } + + unsigned int _checkSuppressionThreads(const char code[], bool useFS, const std::string &suppression = emptyString) { errout.str(""); - std::list> files; - files.emplace_back("test.cpp", strlen(code)); + std::list fileSettings; + + std::list> filelist; + filelist.emplace_back("test.cpp", strlen(code)); + if (useFS) { + FileSettings fs; + fs.filename = "test.cpp"; + fileSettings.emplace_back(std::move(fs)); + } Settings settings; settings.jobs = 2; @@ -246,27 +299,45 @@ private: if (!suppression.empty()) { EXPECT_EQ("", settings.nomsg.addSuppressionLine(suppression)); } - // TODO: test with FS - std::list fileSettings; - ThreadExecutor executor(files, fileSettings, settings, settings.nomsg, *this, CppCheckExecutor::executeCommand); + std::vector> scopedfiles; - scopedfiles.reserve(files.size()); - for (std::list>::const_iterator i = files.cbegin(); i != files.cend(); ++i) + scopedfiles.reserve(filelist.size()); + for (std::list>::const_iterator i = filelist.cbegin(); i != filelist.cend(); ++i) scopedfiles.emplace_back(new ScopedFile(i->first, code)); + // clear files list so only fileSettings are used + if (useFS) + filelist.clear(); + + ThreadExecutor executor(filelist, fileSettings, settings, settings.nomsg, *this, CppCheckExecutor::executeCommand); const unsigned int exitCode = executor.check(); - CppCheckExecutor::reportSuppressions(settings, false, files, *this); + CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); return exitCode; } #if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) - unsigned int checkSuppressionProcesses(const char code[], const std::string &suppression = emptyString) { + unsigned int checkSuppressionProcessesFiles(const char code[], const std::string &suppression = emptyString) { + return _checkSuppressionProcesses(code, false, suppression); + } + + unsigned int checkSuppressionProcessesFS(const char code[], const std::string &suppression = emptyString) { + return _checkSuppressionProcesses(code, true, suppression); + } + + unsigned int _checkSuppressionProcesses(const char code[], bool useFS, const std::string &suppression = emptyString) { errout.str(""); - std::list> files; - files.emplace_back("test.cpp", strlen(code)); + std::list fileSettings; + + std::list> filelist; + filelist.emplace_back("test.cpp", strlen(code)); + if (useFS) { + FileSettings fs; + fs.filename = "test.cpp"; + fileSettings.emplace_back(std::move(fs)); + } Settings settings; settings.jobs = 2; @@ -276,17 +347,20 @@ private: if (!suppression.empty()) { EXPECT_EQ("", settings.nomsg.addSuppressionLine(suppression)); } - // TODO: test with FS - std::list fileSettings; - ProcessExecutor executor(files, fileSettings, settings, settings.nomsg, *this, CppCheckExecutor::executeCommand); + std::vector> scopedfiles; - scopedfiles.reserve(files.size()); - for (std::list>::const_iterator i = files.cbegin(); i != files.cend(); ++i) + scopedfiles.reserve(filelist.size()); + for (std::list>::const_iterator i = filelist.cbegin(); i != filelist.cend(); ++i) scopedfiles.emplace_back(new ScopedFile(i->first, code)); + // clear files list so only fileSettings are used + if (useFS) + filelist.clear(); + + ProcessExecutor executor(filelist, fileSettings, settings, settings.nomsg, *this, CppCheckExecutor::executeCommand); const unsigned int exitCode = executor.check(); - CppCheckExecutor::reportSuppressions(settings, false, files, *this); + CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); return exitCode; } @@ -808,21 +882,34 @@ private: ASSERT_EQUALS("", errout.str()); // <- no unmatched suppression reported for macro suppression } - void suppressionsSettings() { - runChecks(&TestSuppressions::checkSuppression); + void suppressionsSettingsFiles() { + runChecks(&TestSuppressions::checkSuppressionFiles); } - void suppressionsSettingsThreads() { - runChecks(&TestSuppressions::checkSuppressionThreads); + static void suppressionsSettingsFS() { + // TODO + // runChecks(&TestSuppressions::checkSuppressionFS); + } + + void suppressionsSettingsThreadsFiles() { + runChecks(&TestSuppressions::checkSuppressionThreadsFiles); + } + + void suppressionsSettingsThreadsFS() { + runChecks(&TestSuppressions::checkSuppressionThreadsFS); } #if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) - void suppressionsSettingsProcesses() { - runChecks(&TestSuppressions::checkSuppressionProcesses); + void suppressionsSettingsProcessesFiles() { + runChecks(&TestSuppressions::checkSuppressionProcessesFiles); + } + + void suppressionsSettingsProcessesFS() { + runChecks(&TestSuppressions::checkSuppressionProcessesFS); } #endif - void suppressionsMultiFile() { + void suppressionsMultiFileInternal(unsigned int (TestSuppressions::*check)(std::map &f, const std::string &)) { std::map files; files["abc.cpp"] = "void f() {\n" "}\n"; @@ -832,10 +919,18 @@ private: "}\n"; // suppress uninitvar for this file and line - ASSERT_EQUALS(0, checkSuppression(files, "uninitvar:xyz.cpp:3")); + ASSERT_EQUALS(0, (this->*check)(files, "uninitvar:xyz.cpp:3")); ASSERT_EQUALS("", errout.str()); } + void suppressionsMultiFileFiles() { + suppressionsMultiFileInternal(&TestSuppressions::checkSuppressionFiles); + } + + void suppressionsMultiFileFS() { + suppressionsMultiFileInternal(&TestSuppressions::checkSuppressionFS); + } + void suppressionsPathSeparator() const { const Suppressions::Suppression s1("*", "test/foo/*"); ASSERT_EQUALS(true, s1.isSuppressed(errorMessage("someid", "test/foo/bar.cpp", 142))); @@ -928,24 +1023,32 @@ private: ASSERT_EQUALS(badSuppressionAttribute, msg); } - void inlinesuppress_symbolname() { - ASSERT_EQUALS(0, checkSuppression("void f() {\n" - " int a;\n" - " /* cppcheck-suppress uninitvar symbolName=a */\n" - " a++;\n" - "}\n", - "")); + void inlinesuppress_symbolname_Internal(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { + ASSERT_EQUALS(0, (this->*check)("void f() {\n" + " int a;\n" + " /* cppcheck-suppress uninitvar symbolName=a */\n" + " a++;\n" + "}\n", + "")); ASSERT_EQUALS("", errout.str()); - ASSERT_EQUALS(1, checkSuppression("void f() {\n" - " int a,b;\n" - " /* cppcheck-suppress uninitvar symbolName=b */\n" - " a++; b++;\n" - "}\n", - "")); + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " int a,b;\n" + " /* cppcheck-suppress uninitvar symbolName=b */\n" + " a++; b++;\n" + "}\n", + "")); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); } + void inlinesuppress_symbolname_Files() { + inlinesuppress_symbolname_Internal(&TestSuppressions::checkSuppressionFiles); + } + + void inlinesuppress_symbolname_FS() { + inlinesuppress_symbolname_Internal(&TestSuppressions::checkSuppressionFS); + } + void inlinesuppress_comment() const { Suppressions::Suppression s; std::string errMsg; @@ -1141,14 +1244,22 @@ private: ASSERT_EQUALS("",errout.str()); } - void suppressingSyntaxErrors() { // syntaxErrors should be suppressible (#7076) + void suppressingSyntaxErrorsInternal(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { // syntaxErrors should be suppressible (#7076) const char code[] = "if if\n"; - ASSERT_EQUALS(0, checkSuppression(code, "syntaxError:test.cpp:1")); + ASSERT_EQUALS(0, (this->*check)(code, "syntaxError:test.cpp:1")); ASSERT_EQUALS("", errout.str()); } - void suppressingSyntaxErrorsInline() { // syntaxErrors should be suppressible (#5917) + void suppressingSyntaxErrorsFiles() { + suppressingSyntaxErrorsInternal(&TestSuppressions::checkSuppressionFiles); + } + + void suppressingSyntaxErrorsFS() { + suppressingSyntaxErrorsInternal(&TestSuppressions::checkSuppressionFiles); + } + + void suppressingSyntaxErrorsInlineInternal(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { // syntaxErrors should be suppressible (#5917) const char code[] = "double result(0.0);\n" "_asm\n" "{\n" @@ -1159,11 +1270,19 @@ private: " fstp QWORD PTR result ; store a double (8 bytes)\n" " pop EAX ; restore EAX\n" "}"; - ASSERT_EQUALS(0, checkSuppression(code, "")); + ASSERT_EQUALS(0, (this->*check)(code, "")); ASSERT_EQUALS("", errout.str()); } - void suppressingSyntaxErrorsWhileFileRead() { // syntaxError while file read should be suppressible (PR #1333) + void suppressingSyntaxErrorsInlineFiles() { + suppressingSyntaxErrorsInlineInternal(&TestSuppressions::checkSuppressionFiles); + } + + void suppressingSyntaxErrorsInlineFS() { + suppressingSyntaxErrorsInlineInternal(&TestSuppressions::checkSuppressionFS); + } + + void suppressingSyntaxErrorsWhileFileReadInternal(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { // syntaxError while file read should be suppressible (PR #1333) const char code[] = "CONST (genType, KS_CONST) genService[KS_CFG_NR_OF_NVM_BLOCKS] =\n" "{\n" "[!VAR \"BC\" = \"$BC + 1\"!][!//\n" @@ -1177,10 +1296,18 @@ private: "[!VAR \"BC\" = \"$BC + 1\"!][!//\n" "[!ENDIF!][!//\n" "};"; - ASSERT_EQUALS(0, checkSuppression(code, "syntaxError:test.cpp:4")); + ASSERT_EQUALS(0, (this->*check)(code, "syntaxError:test.cpp:4")); ASSERT_EQUALS("", errout.str()); } + void suppressingSyntaxErrorsWhileFileReadFiles() { + suppressingSyntaxErrorsWhileFileReadInternal(&TestSuppressions::checkSuppressionFiles); + } + + void suppressingSyntaxErrorsWhileFileReadFS() { + suppressingSyntaxErrorsWhileFileReadInternal(&TestSuppressions::checkSuppressionFiles); + } + void symbol() const { Suppressions::Suppression s; s.errorId = "foo"; @@ -1204,27 +1331,28 @@ private: ASSERT_EQUALS(true, s.isSuppressed(errorMsg)); } - void unusedFunction() { - ASSERT_EQUALS(0, checkSuppression("void f() {}", "unusedFunction")); + void unusedFunctionInternal(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { + ASSERT_EQUALS(0, (this->*check)("void f() {}", "unusedFunction")); } - void suppressingSyntaxErrorAndExitCode() { + void unusedFunctionFiles() { + unusedFunctionInternal(&TestSuppressions::checkSuppressionFiles); + } + + void unusedFunctionFS() { + unusedFunctionInternal(&TestSuppressions::checkSuppressionFS); + } + + void suppressingSyntaxErrorAndExitCodeInternal(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { const char code[] = "fi if;"; - ASSERT_EQUALS(0, checkSuppression(code, "*:test.cpp")); + ASSERT_EQUALS(0, (this->*check)(code, "*:test.cpp")); ASSERT_EQUALS("", errout.str()); - // multi files, but only suppression one - std::map mfiles; - mfiles["test.cpp"] = "fi if;"; - mfiles["test2.cpp"] = "fi if"; - ASSERT_EQUALS(2, checkSuppression(mfiles, "*:test.cpp")); - ASSERT_EQUALS("[test2.cpp:1]: (error) syntax error\n", errout.str()); - // multi error in file, but only suppression one error const char code2[] = "fi fi\n" "if if;"; - ASSERT_EQUALS(2, checkSuppression(code2, "*:test.cpp:1")); // suppress all error at line 1 of test.cpp + ASSERT_EQUALS(2, (this->*check)(code2, "*:test.cpp:1")); // suppress all error at line 1 of test.cpp ASSERT_EQUALS("[test.cpp:2]: (error) syntax error\n", errout.str()); // multi error in file, but only suppression one error (2) @@ -1233,7 +1361,34 @@ private: " int b = y/0;\n" "}\n" "f(0, 1);\n"; - ASSERT_EQUALS(2, checkSuppression(code3, "zerodiv:test.cpp:3")); // suppress 'errordiv' at line 3 of test.cpp + ASSERT_EQUALS(2, (this->*check)(code3, "zerodiv:test.cpp:3")); // suppress 'errordiv' at line 3 of test.cpp + } + + void suppressingSyntaxErrorAndExitCodeFiles() { + suppressingSyntaxErrorAndExitCodeInternal(&TestSuppressions::checkSuppressionFiles); + } + + static void suppressingSyntaxErrorAndExitCodeFS() { + // TODO + // suppressingSyntaxErrorAndExitCodeInternal(&TestSuppressions::checkSuppressionFS); + } + + void suppressingSyntaxErrorAndExitCodeMultiFileInternal(unsigned int (TestSuppressions::*check)(std::map &f, const std::string &)) { + // multi files, but only suppression one + std::map mfiles; + mfiles["test.cpp"] = "fi if;"; + mfiles["test2.cpp"] = "fi if"; + ASSERT_EQUALS(2, (this->*check)(mfiles, "*:test.cpp")); + ASSERT_EQUALS("[test2.cpp:1]: (error) syntax error\n", errout.str()); + } + + void suppressingSyntaxErrorAndExitCodeMultiFileFiles() { + suppressingSyntaxErrorAndExitCodeMultiFileInternal(&TestSuppressions::checkSuppressionFiles); + } + + static void suppressingSyntaxErrorAndExitCodeMultiFileFS() { + // TODO + // suppressingSyntaxErrorAndExitCodeMultiFileInternal(&TestSuppressions::checkSuppressionFS); } void suppressLocal() const {