@@ -490,6 +490,7 @@ bool ImportProject::importSln(std::istream &istr, const std::string &path, const
490490
491491namespace {
492492 struct ProjectConfiguration {
493+ ProjectConfiguration () = default ;
493494 explicit ProjectConfiguration (const tinyxml2::XMLElement *cfg) {
494495 const char *a = cfg->Attribute (" Include" );
495496 if (a)
@@ -535,10 +536,14 @@ namespace {
535536
536537 // see https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
537538 // properties are .NET String objects and you can call any of its members on them
538- bool conditionIsTrue (const ProjectConfiguration &p) const {
539- if (mCondition .empty ())
539+ bool conditionIsTrue (const ProjectConfiguration &p, const std::string &filename, std::vector<std::string> &errors) const {
540+ return conditionIsTrue (mCondition , p, filename, errors);
541+ }
542+
543+ static bool conditionIsTrue (const std::string& condition, const ProjectConfiguration &p, const std::string &filename, std::vector<std::string> &errors) {
544+ if (condition.empty ())
540545 return true ;
541- std::string c = ' (' + mCondition + " );" ;
546+ std::string c = ' (' + condition + " );" ;
542547 replaceAll (c, " $(Configuration)" , p.configuration );
543548 replaceAll (c, " $(Platform)" , p.platformStr );
544549
@@ -561,19 +566,83 @@ namespace {
561566 }
562567 }
563568 }
569+
570+ // Replace "And" and "Or" with "&&" and "||"
571+ for (Token *tok = tokenlist.front (); tok; tok = tok->next ()) {
572+ if (tok->str () == " And" )
573+ tok->str (" &&" );
574+ else if (tok->str () == " Or" )
575+ tok->str (" ||" );
576+ }
577+
564578 tokenlist.createAst ();
579+
580+ // Locate ast top and execute the condition
565581 for (const Token *tok = tokenlist.front (); tok; tok = tok->next ()) {
566- if (tok->str () == " (" && tok->astOperand1 () && tok->astOperand2 ()) {
567- // TODO: this is wrong - it is Contains() not Equals()
568- if (tok->astOperand1 ()->expressionString () == " Configuration.Contains" )
569- return (' \' ' + p.configuration + ' \' ' ) == tok->astOperand2 ()->str ();
582+ if (tok->astParent ()) {
583+ return execute (tok->astTop (), p) == " True" ;
570584 }
571- if (tok->str () == " ==" && tok->astOperand1 () && tok->astOperand2 () && tok->astOperand1 ()->str () == tok->astOperand2 ()->str ())
572- return true ;
573585 }
574- return false ;
586+
587+ throw std::runtime_error (" Invalid condition: '" + condition + " '" );
575588 }
576589 private:
590+
591+ static std::string executeOp1 (const Token* tok, const ProjectConfiguration &p, bool b=false ) {
592+ const std::string result = execute (tok->astOperand1 (), p);
593+ if (b)
594+ return (result != " False" && !result.empty ()) ? " True" : " False" ;
595+ return result;
596+ }
597+
598+ static std::string executeOp2 (const Token* tok, const ProjectConfiguration &p, bool b=false ) {
599+ const std::string result = execute (tok->astOperand2 (), p);
600+ if (b)
601+ return (result != " False" && !result.empty ()) ? " True" : " False" ;
602+ return result;
603+ }
604+
605+ static std::string execute (const Token* tok, const ProjectConfiguration &p) {
606+ if (!tok)
607+ throw std::runtime_error (" Missing operator" );
608+ auto boolResult = [](bool b) -> std::string { return b ? " True" : " False" ; };
609+ if (tok->isUnaryOp (" !" ))
610+ return boolResult (executeOp1 (tok, p, true ) == " False" );
611+ if (tok->str () == " ==" )
612+ return boolResult (executeOp1 (tok, p) == executeOp2 (tok, p));
613+ if (tok->str () == " !=" )
614+ return boolResult (executeOp1 (tok, p) != executeOp2 (tok, p));
615+ if (tok->str () == " &&" )
616+ return boolResult (executeOp1 (tok, p, true ) == " True" && executeOp2 (tok, p, true ) == " True" );
617+ if (tok->str () == " ||" )
618+ return boolResult (executeOp1 (tok, p, true ) == " True" || executeOp2 (tok, p, true ) == " True" );
619+ if (tok->str () == " (" && Token::Match (tok->previous (), " $ ( %name% . %name% (" )) {
620+ const std::string propertyName = tok->next ()->str ();
621+ std::string propertyValue;
622+ if (propertyName == " Configuration" )
623+ propertyValue = p.configuration ;
624+ else if (propertyName == " Platform" )
625+ propertyValue = p.platform ;
626+ else
627+ throw std::runtime_error (" Unhandled property '" + propertyName + " '" );
628+ const std::string method = tok->strAt (3 );
629+ std::string arg = executeOp2 (tok->tokAt (4 ), p);
630+ if (arg.size () >= 2 && arg[0 ] == ' \' ' )
631+ arg = arg.substr (1 , arg.size () - 2 );
632+ if (method == " Contains" )
633+ return boolResult (propertyValue.find (arg) != std::string::npos);
634+ if (method == " EndsWith" )
635+ return boolResult (endsWith (propertyValue,arg.c_str (),arg.size ()));
636+ if (method == " StartsWith" )
637+ return boolResult (startsWith (propertyValue,arg));
638+ throw std::runtime_error (" Unhandled method '" + method + " '" );
639+ }
640+ if (tok->str ().size () >= 2 && tok->str ()[0 ] == ' \' ' )
641+ return tok->str ();
642+
643+ throw std::runtime_error (" Unknown/unhandled operator/operand '" + tok->str () + " '" );
644+ }
645+
577646 std::string mCondition ;
578647 };
579648
@@ -879,7 +948,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
879948 }
880949 std::string additionalIncludePaths;
881950 for (const ItemDefinitionGroup &i : itemDefinitionGroupList) {
882- if (!i.conditionIsTrue (p))
951+ if (!i.conditionIsTrue (p, cfilename, errors ))
883952 continue ;
884953 fs.standard = Standards::getCPP (i.cppstd );
885954 fs.defines += ' ;' + i.preprocessorDefinitions ;
@@ -897,7 +966,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
897966 }
898967 bool useUnicode = false ;
899968 for (const ConfigurationPropertyGroup &c : configurationPropertyGroups) {
900- if (!c.conditionIsTrue (p))
969+ if (!c.conditionIsTrue (p, cfilename, errors ))
901970 continue ;
902971 // in msbuild the last definition wins
903972 useUnicode = c.useUnicode ;
@@ -1554,3 +1623,14 @@ void ImportProject::setRelativePaths(const std::string &filename)
15541623 }
15551624}
15561625
1626+ // only used by tests (testimportproject.cpp::testVcxprojConditions):
1627+ // cppcheck-suppress unusedFunction
1628+ bool cppcheck::testing::evaluateVcxprojCondition (const std::string& condition, const std::string& configuration,
1629+ const std::string& platform)
1630+ {
1631+ ProjectConfiguration p;
1632+ p.configuration = configuration;
1633+ p.platformStr = platform;
1634+ std::vector<std::string> errors;
1635+ return ConditionalGroup::conditionIsTrue (condition, p, " file.vcxproj" , errors) && errors.empty ();
1636+ }
0 commit comments