Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions lib/importproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include <algorithm>
#include <cctype>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <cstring>
Expand Down Expand Up @@ -848,6 +849,22 @@ static std::string safeFormat(const char *fmt, Args... args) {
#pragma clang diagnostic pop
#endif

// MSBuild evaluates the [MSBuild]::Add/Subtract/Multiply intrinsics in .NET's
// default unchecked context, so on overflow the result wraps (two's complement).
// Do the arithmetic on unsigned operands to get the same result without
// signed-overflow UB.
static long long wrapAdd(long long a, long long b) {
return static_cast<long long>(static_cast<unsigned long long>(a) + static_cast<unsigned long long>(b));
}

static long long wrapSub(long long a, long long b) {
return static_cast<long long>(static_cast<unsigned long long>(a) - static_cast<unsigned long long>(b));
}

static long long wrapMul(long long a, long long b) {
return static_cast<long long>(static_cast<unsigned long long>(a) * static_cast<unsigned long long>(b));
}

// Evaluate a $([ClassName]::Method(args)) static property function.
// Returns an empty string for unknown or unimplementable functions rather
// than throwing, so import can continue gracefully.
Expand Down Expand Up @@ -894,23 +911,31 @@ std::string ImportProject::applyMSBuildStaticFunction(const std::string &classNa
long long a = 0, b = 0;
if (toInt(args[0], a) && toInt(args[1], b)) {
if (caseInsensitiveStringCompare(member, "Add") == 0)
return std::to_string(a + b);
return std::to_string(wrapAdd(a, b));
if (caseInsensitiveStringCompare(member, "Subtract") == 0)
return std::to_string(a - b);
return std::to_string(wrapSub(a, b));
if (caseInsensitiveStringCompare(member, "Multiply") == 0)
return std::to_string(a * b);
return std::to_string(wrapMul(a, b));
if (caseInsensitiveStringCompare(member, "Divide") == 0) {
if (b == 0) {
addDebug("MSBuild::Divide: division by zero");
return std::string();
}
if (a == LLONG_MIN && b == -1) {
addDebug("MSBuild::Divide: integer overflow");
return std::string();
}
return std::to_string(a / b);
}
if (caseInsensitiveStringCompare(member, "Modulo") == 0) {
if (b == 0) {
addDebug("MSBuild::Modulo: division by zero");
return std::string();
}
if (a == LLONG_MIN && b == -1) {
addDebug("MSBuild::Modulo: integer overflow");
return std::string();
}
return std::to_string(a % b);
}
}
Expand Down
8 changes: 8 additions & 0 deletions test/testimportproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,14 @@ class TestImportProject : public TestFixture {
ASSERT_EQUALS("3", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::Divide(9, 3))"));
ASSERT_EQUALS("2", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::Modulo(5, 3))"));

// Overflow wraps around like MSBuild (.NET unchecked arithmetic).
ASSERT_EQUALS("-9223372036854775808", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::Add(9223372036854775807, 1))"));
ASSERT_EQUALS("9223372036854775807", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::Subtract(-9223372036854775808, 1))"));
ASSERT_EQUALS("7766279611452241921", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::Multiply(9999999999, 9999999999))"));
// .NET throws OverflowException for LLONG_MIN / -1 and LLONG_MIN % -1.
ASSERT_EQUALS("", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::Divide(-9223372036854775808, -1))"));
ASSERT_EQUALS("", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::Modulo(-9223372036854775808, -1))"));

// --- $([MSBuild]::...) path helpers ---
ASSERT_EQUALS("foo/", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::EnsureTrailingSlash('foo'))"));
ASSERT_EQUALS("foo/", cppcheck::testing::expandMSBuildExpression("$([MSBuild]::EnsureTrailingSlash('foo/'))"));
Expand Down
Loading