@@ -558,6 +558,7 @@ bool ImportProject::importSlnx(const std::string& filename, const std::vector<st
558558
559559namespace {
560560 struct ProjectConfiguration {
561+ ProjectConfiguration () = default ;
561562 explicit ProjectConfiguration (const tinyxml2::XMLElement *cfg) {
562563 const char *a = cfg->Attribute (" Include" );
563564 if (a)
@@ -603,10 +604,14 @@ namespace {
603604
604605 // see https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
605606 // properties are .NET String objects and you can call any of its members on them
606- bool conditionIsTrue (const ProjectConfiguration &p) const {
607- if (mCondition .empty ())
607+ bool conditionIsTrue (const ProjectConfiguration &p, const std::string &filename, std::vector<std::string> &errors) const {
608+ return conditionIsTrue (mCondition , p, filename, errors);
609+ }
610+
611+ static bool conditionIsTrue (const std::string& condition, const ProjectConfiguration &p, const std::string &filename, std::vector<std::string> &errors) {
612+ if (condition.empty ())
608613 return true ;
609- std::string c = ' (' + mCondition + " );" ;
614+ std::string c = ' (' + condition + " );" ;
610615 replaceAll (c, " $(Configuration)" , p.configuration );
611616 replaceAll (c, " $(Platform)" , p.platformStr );
612617
@@ -629,19 +634,83 @@ namespace {
629634 }
630635 }
631636 }
637+
638+ // Replace "And" and "Or" with "&&" and "||"
639+ for (Token *tok = tokenlist.front (); tok; tok = tok->next ()) {
640+ if (tok->str () == " And" )
641+ tok->str (" &&" );
642+ else if (tok->str () == " Or" )
643+ tok->str (" ||" );
644+ }
645+
632646 tokenlist.createAst ();
647+
648+ // Locate ast top and execute the condition
633649 for (const Token *tok = tokenlist.front (); tok; tok = tok->next ()) {
634- if (tok->str () == " (" && tok->astOperand1 () && tok->astOperand2 ()) {
635- // TODO: this is wrong - it is Contains() not Equals()
636- if (tok->astOperand1 ()->expressionString () == " Configuration.Contains" )
637- return (' \' ' + p.configuration + ' \' ' ) == tok->astOperand2 ()->str ();
650+ if (tok->astParent ()) {
651+ return execute (tok->astTop (), p) == " True" ;
638652 }
639- if (tok->str () == " ==" && tok->astOperand1 () && tok->astOperand2 () && tok->astOperand1 ()->str () == tok->astOperand2 ()->str ())
640- return true ;
641653 }
642- return false ;
654+
655+ throw std::runtime_error (" Invalid condition: '" + condition + " '" );
643656 }
644657 private:
658+
659+ static std::string executeOp1 (const Token* tok, const ProjectConfiguration &p, bool b=false ) {
660+ const std::string result = execute (tok->astOperand1 (), p);
661+ if (b)
662+ return (result != " False" && !result.empty ()) ? " True" : " False" ;
663+ return result;
664+ }
665+
666+ static std::string executeOp2 (const Token* tok, const ProjectConfiguration &p, bool b=false ) {
667+ const std::string result = execute (tok->astOperand2 (), p);
668+ if (b)
669+ return (result != " False" && !result.empty ()) ? " True" : " False" ;
670+ return result;
671+ }
672+
673+ static std::string execute (const Token* tok, const ProjectConfiguration &p) {
674+ if (!tok)
675+ throw std::runtime_error (" Missing operator" );
676+ auto boolResult = [](bool b) -> std::string { return b ? " True" : " False" ; };
677+ if (tok->isUnaryOp (" !" ))
678+ return boolResult (executeOp1 (tok, p, true ) == " False" );
679+ if (tok->str () == " ==" )
680+ return boolResult (executeOp1 (tok, p) == executeOp2 (tok, p));
681+ if (tok->str () == " !=" )
682+ return boolResult (executeOp1 (tok, p) != executeOp2 (tok, p));
683+ if (tok->str () == " &&" )
684+ return boolResult (executeOp1 (tok, p, true ) == " True" && executeOp2 (tok, p, true ) == " True" );
685+ if (tok->str () == " ||" )
686+ return boolResult (executeOp1 (tok, p, true ) == " True" || executeOp2 (tok, p, true ) == " True" );
687+ if (tok->str () == " (" && Token::Match (tok->previous (), " $ ( %name% . %name% (" )) {
688+ const std::string propertyName = tok->next ()->str ();
689+ std::string propertyValue;
690+ if (propertyName == " Configuration" )
691+ propertyValue = p.configuration ;
692+ else if (propertyName == " Platform" )
693+ propertyValue = p.platform ;
694+ else
695+ throw std::runtime_error (" Unhandled property '" + propertyName + " '" );
696+ const std::string method = tok->strAt (3 );
697+ std::string arg = executeOp2 (tok->tokAt (4 ), p);
698+ if (arg.size () >= 2 && arg[0 ] == ' \' ' )
699+ arg = arg.substr (1 , arg.size () - 2 );
700+ if (method == " Contains" )
701+ return boolResult (propertyValue.find (arg) != std::string::npos);
702+ if (method == " EndsWith" )
703+ return boolResult (endsWith (propertyValue,arg.c_str (),arg.size ()));
704+ if (method == " StartsWith" )
705+ return boolResult (startsWith (propertyValue,arg));
706+ throw std::runtime_error (" Unhandled method '" + method + " '" );
707+ }
708+ if (tok->str ().size () >= 2 && tok->str ()[0 ] == ' \' ' )
709+ return tok->str ();
710+
711+ throw std::runtime_error (" Unknown/unhandled operator/operand '" + tok->str () + " '" );
712+ }
713+
645714 std::string mCondition ;
646715 };
647716
@@ -947,7 +1016,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
9471016 }
9481017 std::string additionalIncludePaths;
9491018 for (const ItemDefinitionGroup &i : itemDefinitionGroupList) {
950- if (!i.conditionIsTrue (p))
1019+ if (!i.conditionIsTrue (p, cfilename, errors ))
9511020 continue ;
9521021 fs.standard = Standards::getCPP (i.cppstd );
9531022 fs.defines += ' ;' + i.preprocessorDefinitions ;
@@ -965,7 +1034,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
9651034 }
9661035 bool useUnicode = false ;
9671036 for (const ConfigurationPropertyGroup &c : configurationPropertyGroups) {
968- if (!c.conditionIsTrue (p))
1037+ if (!c.conditionIsTrue (p, cfilename, errors ))
9691038 continue ;
9701039 // in msbuild the last definition wins
9711040 useUnicode = c.useUnicode ;
@@ -1622,3 +1691,14 @@ void ImportProject::setRelativePaths(const std::string &filename)
16221691 }
16231692}
16241693
1694+ // only used by tests (testimportproject.cpp::testVcxprojConditions):
1695+ // cppcheck-suppress unusedFunction
1696+ bool cppcheck::testing::evaluateVcxprojCondition (const std::string& condition, const std::string& configuration,
1697+ const std::string& platform)
1698+ {
1699+ ProjectConfiguration p;
1700+ p.configuration = configuration;
1701+ p.platformStr = platform;
1702+ std::vector<std::string> errors;
1703+ return ConditionalGroup::conditionIsTrue (condition, p, " file.vcxproj" , errors) && errors.empty ();
1704+ }
0 commit comments