@@ -101,6 +101,13 @@ class TestCppcheck : public TestFixture {
101101 TEST_CASE (checkPlistOutput);
102102 TEST_CASE (premiumResultsCache);
103103 TEST_CASE (purgedConfiguration);
104+ TEST_CASE (checkConfigurationInvalid);
105+ TEST_CASE (checkConfigurationValid);
106+ TEST_CASE (checkConfigurationSuppression);
107+ TEST_CASE (checkConfigurationExplicitError);
108+ TEST_CASE (checkConfigurationCombinedDefines);
109+ TEST_CASE (checkConfigurationNormalAnalysis);
110+ TEST_CASE (checkConfigurationHeader);
104111 TEST_CASE (recheckInclude);
105112 }
106113
@@ -126,6 +133,7 @@ class TestCppcheck : public TestFixture {
126133 bool foundTooManyConfigs = false ;
127134 bool foundMissingInclude = false ; // #11984
128135 bool foundMissingIncludeSystem = false ; // #11984
136+ bool foundInvalidConfiguration = false ;
129137 for (const std::string & it : errorLogger.ids ) {
130138 if (it == " purgedConfiguration" )
131139 foundPurgedConfiguration = true ;
@@ -135,11 +143,14 @@ class TestCppcheck : public TestFixture {
135143 foundMissingInclude = true ;
136144 else if (it == " missingIncludeSystem" )
137145 foundMissingIncludeSystem = true ;
146+ else if (it == " invalidConfiguration" )
147+ foundInvalidConfiguration = true ;
138148 }
139149 ASSERT (foundPurgedConfiguration);
140150 ASSERT (foundTooManyConfigs);
141151 ASSERT (foundMissingInclude);
142152 ASSERT (foundMissingIncludeSystem);
153+ ASSERT (foundInvalidConfiguration);
143154 }
144155
145156 static std::string exename_ (const std::string& exe)
@@ -639,6 +650,100 @@ class TestCppcheck : public TestFixture {
639650 it->toString (false , templateFormat, " " ));
640651 }
641652
653+ static const char * invalidConfigurationCode () {
654+ return " #ifndef PLATFORM\n "
655+ " #error Select PLATFORM\n "
656+ " #endif\n "
657+ " int base;\n "
658+ " #ifdef FEATURE\n "
659+ " int feature;\n "
660+ " #endif\n " ;
661+ }
662+
663+ std::list<ErrorMessage> configurationMessages (const char * code, const Settings& settings, const char * suppression = nullptr ) const {
664+ const ScopedFile source (" check-config.c" , code);
665+ Settings configuredSettings = settings;
666+ configuredSettings.templateFormat = templateFormat;
667+ Suppressions supprs;
668+ if (suppression)
669+ ASSERT_EQUALS (" " , supprs.nomsg .addSuppressionLine (suppression));
670+ ErrorLogger2 errorLogger;
671+ CppCheck cppcheck (configuredSettings, supprs, errorLogger, nullptr , true , {});
672+ cppcheck.check (FileWithDetails (source.path (), Path::identify (source.path (), false ), 0 ));
673+ errorLogger.errmsgs .remove_if ([](const ErrorMessage& msg) {
674+ return msg.id == " logChecker" ;
675+ });
676+ return errorLogger.errmsgs ;
677+ }
678+
679+ void checkConfigurationInvalid () const {
680+ // Trac #6672: FEATURE is considered independently of the required PLATFORM.
681+ const auto settings = dinit (Settings, $.checkConfiguration = true , $.templateFormat = templateFormat);
682+ const auto messages = configurationMessages (invalidConfigurationCode (), settings);
683+ ASSERT_EQUALS (1 , messages.size ());
684+ const ErrorMessage& msg = messages.front ();
685+ ASSERT_EQUALS (" invalidConfiguration" , msg.id );
686+ ASSERT (msg.severity == Severity::information);
687+ ASSERT_EQUALS (" check-config.c" , msg.file0 );
688+ ASSERT_EQUALS (1 , msg.callStack .size ());
689+ ASSERT_EQUALS (" check-config.c" , msg.callStack .back ().getfile (false ));
690+ ASSERT_EQUALS (2 , msg.callStack .back ().line );
691+ ASSERT (msg.shortMessage ().find (" FEATURE" ) != std::string::npos);
692+ ASSERT (msg.shortMessage ().find (" #error Select PLATFORM" ) != std::string::npos);
693+ }
694+
695+ void checkConfigurationValid () const {
696+ const auto settings = dinit (Settings, $.checkConfiguration = true , $.userDefines = " PLATFORM=1" );
697+ ASSERT (configurationMessages (invalidConfigurationCode (), settings).empty ());
698+
699+ const auto automaticSettings = dinit (Settings, $.checkConfiguration = true );
700+ ASSERT (configurationMessages (" #if 0\n #error inactive\n #endif\n int value;\n " , automaticSettings).empty ());
701+ ASSERT (configurationMessages (" #ifdef FEATURE\n int feature;\n #endif\n int value;\n " , automaticSettings).empty ());
702+ }
703+
704+ void checkConfigurationSuppression () const {
705+ const auto settings = dinit (Settings, $.checkConfiguration = true );
706+ ASSERT (configurationMessages (invalidConfigurationCode (), settings, " invalidConfiguration" ).empty ());
707+ }
708+
709+ void checkConfigurationExplicitError () const {
710+ const auto settings = dinit (Settings, $.checkConfiguration = true , $.userDefines = " FEATURE=1" );
711+ const auto messages = configurationMessages (invalidConfigurationCode (), settings);
712+ ASSERT_EQUALS (1 , messages.size ());
713+ ASSERT_EQUALS (" preprocessorErrorDirective" , messages.front ().id );
714+ ASSERT (messages.front ().severity == Severity::error);
715+ }
716+
717+ void checkConfigurationCombinedDefines () const {
718+ const auto settings = dinit (Settings, $.checkConfiguration = true , $.force = true , $.userDefines = " EXTRA=7" );
719+ const auto messages = configurationMessages (invalidConfigurationCode (), settings);
720+ ASSERT_EQUALS (1 , messages.size ());
721+ ASSERT_EQUALS (" invalidConfiguration" , messages.front ().id );
722+ const std::string message = messages.front ().shortMessage ();
723+ ASSERT (message.find (" EXTRA=7;FEATURE" ) != std::string::npos);
724+ ASSERT (message.find (" EXTRA=7" , message.find (" EXTRA=7" ) + 1 ) == std::string::npos);
725+ }
726+
727+ void checkConfigurationNormalAnalysis () const {
728+ const auto settings = dinit (Settings, $.force = true , $.severity .enable (Severity::information));
729+ ASSERT (configurationMessages (invalidConfigurationCode (), settings).empty ());
730+ }
731+
732+ void checkConfigurationHeader () const {
733+ const ScopedFile header (" config-error.h" , " #ifndef PLATFORM\n #error Select PLATFORM\n #endif\n " );
734+ const auto settings = dinit (Settings, $.checkConfiguration = true );
735+ const auto messages = configurationMessages (" #include \" config-error.h\"\n #ifdef FEATURE\n int feature;\n #endif\n " , settings);
736+ // Both the empty and FEATURE configurations fail in the included header.
737+ ASSERT_EQUALS (2 , messages.size ());
738+ ASSERT (messages.front ().shortMessage () != messages.back ().shortMessage ());
739+ for (const ErrorMessage& message : messages) {
740+ ASSERT_EQUALS (" invalidConfiguration" , message.id );
741+ ASSERT_EQUALS (" check-config.c" , message.file0 );
742+ ASSERT_EQUALS (" config-error.h" , message.callStack .back ().getfile (false ));
743+ ASSERT_EQUALS (2 , message.callStack .back ().line );
744+ }
745+ }
746+
642747 void recheckInclude () const
643748 {
644749 const auto settings = dinit (Settings,
0 commit comments