@@ -505,6 +505,7 @@ bool ImportProject::importSln(std::istream &istr, const std::string &path, const
505505
506506namespace {
507507 struct ProjectConfiguration {
508+ ProjectConfiguration () = default ;
508509 explicit ProjectConfiguration (const tinyxml2::XMLElement *cfg) {
509510 const char *a = cfg->Attribute (" Include" );
510511 if (a)
@@ -550,10 +551,14 @@ namespace {
550551
551552 // see https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
552553 // properties are .NET String objects and you can call any of its members on them
553- bool conditionIsTrue (const ProjectConfiguration &p) const {
554- if (mCondition .empty ())
554+ bool conditionIsTrue (const ProjectConfiguration &p, const std::string &filename, std::vector<std::string> &errors) const {
555+ return conditionIsTrue (mCondition , p, filename, errors);
556+ }
557+
558+ static bool conditionIsTrue (const std::string& condition, const ProjectConfiguration &p, const std::string &filename, std::vector<std::string> &errors) {
559+ if (condition.empty ())
555560 return true ;
556- std::string c = ' (' + mCondition + " );" ;
561+ std::string c = ' (' + condition + " );" ;
557562 replaceAll (c, " $(Configuration)" , p.configuration );
558563 replaceAll (c, " $(Platform)" , p.platformStr );
559564
@@ -576,19 +581,83 @@ namespace {
576581 }
577582 }
578583 }
584+
585+ // Replace "And" and "Or" with "&&" and "||"
586+ for (Token *tok = tokenlist.front (); tok; tok = tok->next ()) {
587+ if (tok->str () == " And" )
588+ tok->str (" &&" );
589+ else if (tok->str () == " Or" )
590+ tok->str (" ||" );
591+ }
592+
579593 tokenlist.createAst ();
594+
595+ // Locate ast top and execute the condition
580596 for (const Token *tok = tokenlist.front (); tok; tok = tok->next ()) {
581- if (tok->str () == " (" && tok->astOperand1 () && tok->astOperand2 ()) {
582- // TODO: this is wrong - it is Contains() not Equals()
583- if (tok->astOperand1 ()->expressionString () == " Configuration.Contains" )
584- return (' \' ' + p.configuration + ' \' ' ) == tok->astOperand2 ()->str ();
597+ if (tok->astParent ()) {
598+ return execute (tok->astTop (), p) == " True" ;
585599 }
586- if (tok->str () == " ==" && tok->astOperand1 () && tok->astOperand2 () && tok->astOperand1 ()->str () == tok->astOperand2 ()->str ())
587- return true ;
588600 }
589- return false ;
601+
602+ throw std::runtime_error (" Invalid condition: '" + condition + " '" );
590603 }
591604 private:
605+
606+ static std::string executeOp1 (const Token* tok, const ProjectConfiguration &p, bool b=false ) {
607+ const std::string result = execute (tok->astOperand1 (), p);
608+ if (b)
609+ return (result != " False" && !result.empty ()) ? " True" : " False" ;
610+ return result;
611+ }
612+
613+ static std::string executeOp2 (const Token* tok, const ProjectConfiguration &p, bool b=false ) {
614+ const std::string result = execute (tok->astOperand2 (), p);
615+ if (b)
616+ return (result != " False" && !result.empty ()) ? " True" : " False" ;
617+ return result;
618+ }
619+
620+ static std::string execute (const Token* tok, const ProjectConfiguration &p) {
621+ if (!tok)
622+ throw std::runtime_error (" Missing operator" );
623+ auto boolResult = [](bool b) -> std::string { return b ? " True" : " False" ; };
624+ if (tok->isUnaryOp (" !" ))
625+ return boolResult (executeOp1 (tok, p, true ) == " False" );
626+ if (tok->str () == " ==" )
627+ return boolResult (executeOp1 (tok, p) == executeOp2 (tok, p));
628+ if (tok->str () == " !=" )
629+ return boolResult (executeOp1 (tok, p) != executeOp2 (tok, p));
630+ if (tok->str () == " &&" )
631+ return boolResult (executeOp1 (tok, p, true ) == " True" && executeOp2 (tok, p, true ) == " True" );
632+ if (tok->str () == " ||" )
633+ return boolResult (executeOp1 (tok, p, true ) == " True" || executeOp2 (tok, p, true ) == " True" );
634+ if (tok->str () == " (" && Token::Match (tok->previous (), " $ ( %name% . %name% (" )) {
635+ const std::string propertyName = tok->next ()->str ();
636+ std::string propertyValue;
637+ if (propertyName == " Configuration" )
638+ propertyValue = p.configuration ;
639+ else if (propertyName == " Platform" )
640+ propertyValue = p.platform ;
641+ else
642+ throw std::runtime_error (" Unhandled property '" + propertyName + " '" );
643+ const std::string method = tok->strAt (3 );
644+ std::string arg = executeOp2 (tok->tokAt (4 ), p);
645+ if (arg.size () >= 2 && arg[0 ] == ' \' ' )
646+ arg = arg.substr (1 , arg.size () - 2 );
647+ if (method == " Contains" )
648+ return boolResult (propertyValue.find (arg) != std::string::npos);
649+ if (method == " EndsWith" )
650+ return boolResult (endsWith (propertyValue,arg.c_str (),arg.size ()));
651+ if (method == " StartsWith" )
652+ return boolResult (startsWith (propertyValue,arg));
653+ throw std::runtime_error (" Unhandled method '" + method + " '" );
654+ }
655+ if (tok->str ().size () >= 2 && tok->str ()[0 ] == ' \' ' )
656+ return tok->str ();
657+
658+ throw std::runtime_error (" Unknown/unhandled operator/operand '" + tok->str () + " '" );
659+ }
660+
592661 std::string mCondition ;
593662 };
594663
@@ -894,7 +963,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
894963 }
895964 std::string additionalIncludePaths;
896965 for (const ItemDefinitionGroup &i : itemDefinitionGroupList) {
897- if (!i.conditionIsTrue (p))
966+ if (!i.conditionIsTrue (p, cfilename, errors ))
898967 continue ;
899968 fs.standard = Standards::getCPP (i.cppstd );
900969 fs.defines += ' ;' + i.preprocessorDefinitions ;
@@ -912,7 +981,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
912981 }
913982 bool useUnicode = false ;
914983 for (const ConfigurationPropertyGroup &c : configurationPropertyGroups) {
915- if (!c.conditionIsTrue (p))
984+ if (!c.conditionIsTrue (p, cfilename, errors ))
916985 continue ;
917986 // in msbuild the last definition wins
918987 useUnicode = c.useUnicode ;
@@ -1569,3 +1638,14 @@ void ImportProject::setRelativePaths(const std::string &filename)
15691638 }
15701639}
15711640
1641+ // only used by tests (testimportproject.cpp::testVcxprojConditions):
1642+ // cppcheck-suppress unusedFunction
1643+ bool cppcheck::testing::evaluateVcxprojCondition (const std::string& condition, const std::string& configuration,
1644+ const std::string& platform)
1645+ {
1646+ ProjectConfiguration p;
1647+ p.configuration = configuration;
1648+ p.platformStr = platform;
1649+ std::vector<std::string> errors;
1650+ return ConditionalGroup::conditionIsTrue (condition, p, " file.vcxproj" , errors) && errors.empty ();
1651+ }
0 commit comments