diff --git a/src/Build/Evaluation/Conditionals/AndExpressionNode.cs b/src/Build/Evaluation/Conditionals/AndExpressionNode.cs index 7b3e099fb4c..31d790c5b3a 100644 --- a/src/Build/Evaluation/Conditionals/AndExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/AndExpressionNode.cs @@ -19,14 +19,14 @@ internal sealed class AndExpressionNode : OperatorExpressionNode internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) { ProjectErrorUtilities.VerifyThrowInvalidProject - (LeftChild.CanBoolEvaluate(state), + (LeftChild.TryBoolEvaluate(state, out bool leftBool), state.ElementLocation, "ExpressionDoesNotEvaluateToBoolean", LeftChild.GetUnexpandedValue(state), LeftChild.GetExpandedValue(state), state.Condition); - if (!LeftChild.BoolEvaluate(state)) + if (!leftBool) { // Short circuit return false; @@ -34,14 +34,14 @@ internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState else { ProjectErrorUtilities.VerifyThrowInvalidProject - (RightChild.CanBoolEvaluate(state), + (RightChild.TryBoolEvaluate(state, out bool rightBool), state.ElementLocation, "ExpressionDoesNotEvaluateToBoolean", RightChild.GetUnexpandedValue(state), RightChild.GetExpandedValue(state), state.Condition); - return RightChild.BoolEvaluate(state); + return rightBool; } } diff --git a/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs b/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs index d4862fec251..5963a4a61e7 100644 --- a/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs @@ -12,12 +12,9 @@ namespace Microsoft.Build.Evaluation /// internal abstract class GenericExpressionNode { - internal abstract bool CanBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state); - internal abstract bool CanNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state); - internal abstract bool CanVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state); - internal abstract bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state); - internal abstract double NumericEvaluate(ConditionEvaluator.IConditionEvaluationState state); - internal abstract Version VersionEvaluate(ConditionEvaluator.IConditionEvaluationState state); + internal abstract bool TryBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state, out bool result); + internal abstract bool TryNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state, out double result); + internal abstract bool TryVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state, out Version result); /// /// Returns true if this node evaluates to an empty string, @@ -57,13 +54,13 @@ internal virtual bool EvaluatesToEmpty(ConditionEvaluator.IConditionEvaluationSt internal bool Evaluate(ConditionEvaluator.IConditionEvaluationState state) { ProjectErrorUtilities.VerifyThrowInvalidProject( - CanBoolEvaluate(state), + TryBoolEvaluate(state, out bool boolValue), state.ElementLocation, "ConditionNotBooleanDetail", state.Condition, GetExpandedValue(state)); - return BoolEvaluate(state); + return boolValue; } /// diff --git a/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs b/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs index 6a457794da3..18e1caad924 100644 --- a/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs @@ -48,36 +48,41 @@ internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState // and we know which do, then we already have enough information to evaluate this expression. // That means we don't have to fully expand a condition like " '@(X)' == '' " // which is a performance advantage if @(X) is a huge item list. - if (LeftChild.EvaluatesToEmpty(state) || RightChild.EvaluatesToEmpty(state)) + bool leftEmpty = LeftChild.EvaluatesToEmpty(state); + bool rightEmpty = RightChild.EvaluatesToEmpty(state); + if (leftEmpty || rightEmpty) { UpdateConditionedProperties(state); - return Compare(LeftChild.EvaluatesToEmpty(state), RightChild.EvaluatesToEmpty(state)); + return Compare(leftEmpty, rightEmpty); } - - if (LeftChild.CanNumericEvaluate(state) && RightChild.CanNumericEvaluate(state)) + else if (LeftChild.TryNumericEvaluate(state, out double leftNumericValue) && RightChild.TryNumericEvaluate(state, out double rightNumericValue)) { - return Compare(LeftChild.NumericEvaluate(state), RightChild.NumericEvaluate(state)); + // The left child evaluating to a number and the right child not evaluating to a number + // is insufficient to say they are not equal because $(MSBuildToolsVersion) evaluates to + // the string "Current" most of the time but when doing numeric comparisons, is treated + // as a version and returns "17.0" (or whatever the current tools version is). This means + // that if '$(MSBuildToolsVersion)' is "equal" to BOTH '17.0' and 'Current' (if 'Current' + // is 17.0). + return Compare(leftNumericValue, rightNumericValue); } - else if (LeftChild.CanBoolEvaluate(state) && RightChild.CanBoolEvaluate(state)) + else if (LeftChild.TryBoolEvaluate(state, out bool leftBoolValue) && RightChild.TryBoolEvaluate(state, out bool rightBoolValue)) { - return Compare(LeftChild.BoolEvaluate(state), RightChild.BoolEvaluate(state)); + return Compare(leftBoolValue, rightBoolValue); } - else // string comparison - { - string leftExpandedValue = LeftChild.GetExpandedValue(state); - string rightExpandedValue = RightChild.GetExpandedValue(state); - ProjectErrorUtilities.VerifyThrowInvalidProject - (leftExpandedValue != null && rightExpandedValue != null, - state.ElementLocation, - "IllFormedCondition", - state.Condition); + string leftExpandedValue = LeftChild.GetExpandedValue(state); + string rightExpandedValue = RightChild.GetExpandedValue(state); - UpdateConditionedProperties(state); + ProjectErrorUtilities.VerifyThrowInvalidProject + (leftExpandedValue != null && rightExpandedValue != null, + state.ElementLocation, + "IllFormedCondition", + state.Condition); - return Compare(leftExpandedValue, rightExpandedValue); - } + UpdateConditionedProperties(state); + + return Compare(leftExpandedValue, rightExpandedValue); } /// diff --git a/src/Build/Evaluation/Conditionals/NotExpressionNode.cs b/src/Build/Evaluation/Conditionals/NotExpressionNode.cs index 0a2521feda7..de73b72e736 100644 --- a/src/Build/Evaluation/Conditionals/NotExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/NotExpressionNode.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.Build.Shared; using System.Diagnostics; namespace Microsoft.Build.Evaluation @@ -17,12 +18,14 @@ internal sealed class NotExpressionNode : OperatorExpressionNode /// internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) { - return !LeftChild.BoolEvaluate(state); - } - - internal override bool CanBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - return LeftChild.CanBoolEvaluate(state); + ProjectErrorUtilities.VerifyThrowInvalidProject + (LeftChild.TryBoolEvaluate(state, out bool boolValue), + state.ElementLocation, + "ExpressionDoesNotEvaluateToBoolean", + LeftChild.GetUnexpandedValue(state), + LeftChild.GetExpandedValue(state), + state.Condition); + return !boolValue; } /// diff --git a/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs b/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs index 0bd30600cbf..dec7c6e2fe2 100644 --- a/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs @@ -38,46 +38,29 @@ internal abstract class NumericComparisonExpressionNode : OperatorExpressionNode /// internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) { - bool isLeftNum = LeftChild.CanNumericEvaluate(state); - bool isLeftVersion = LeftChild.CanVersionEvaluate(state); - bool isRightNum = RightChild.CanNumericEvaluate(state); - bool isRightVersion = RightChild.CanVersionEvaluate(state); - bool isNumeric = isLeftNum && isRightNum; - bool isVersion = isLeftVersion && isRightVersion; - bool isValidComparison = isNumeric || isVersion || (isLeftNum && isRightVersion) || (isLeftVersion && isRightNum); + bool isLeftNum = LeftChild.TryNumericEvaluate(state, out double leftNum); + bool isLeftVersion = LeftChild.TryVersionEvaluate(state, out Version leftVersion); + bool isRightNum = RightChild.TryNumericEvaluate(state, out double rightNum); + bool isRightVersion = RightChild.TryVersionEvaluate(state, out Version rightVersion); ProjectErrorUtilities.VerifyThrowInvalidProject - (isValidComparison, + ((isLeftNum || isLeftVersion) && (isRightNum || isRightVersion), state.ElementLocation, "ComparisonOnNonNumericExpression", state.Condition, /* helpfully display unexpanded token and expanded result in error message */ - LeftChild.CanNumericEvaluate(state) ? RightChild.GetUnexpandedValue(state) : LeftChild.GetUnexpandedValue(state), - LeftChild.CanNumericEvaluate(state) ? RightChild.GetExpandedValue(state) : LeftChild.GetExpandedValue(state)); + isLeftNum ? RightChild.GetUnexpandedValue(state) : LeftChild.GetUnexpandedValue(state), + isLeftNum ? RightChild.GetExpandedValue(state) : LeftChild.GetExpandedValue(state)); - // If the values identify as numeric, make that comparison instead of the Version comparison since numeric has a stricter definition - if (isNumeric) + return (isLeftNum, isLeftVersion, isRightNum, isRightVersion) switch { - return Compare(LeftChild.NumericEvaluate(state), RightChild.NumericEvaluate(state)); - } - else if (isVersion) - { - return Compare(LeftChild.VersionEvaluate(state), RightChild.VersionEvaluate(state)); - } - - // If the numbers are of a mixed type, call that specific Compare method - if (isLeftNum && isRightVersion) - { - return Compare(LeftChild.NumericEvaluate(state), RightChild.VersionEvaluate(state)); - } - else if (isLeftVersion && isRightNum) - { - return Compare(LeftChild.VersionEvaluate(state), RightChild.NumericEvaluate(state)); - } + (true, _, true, _) => Compare(leftNum, rightNum), + (_, true, _, true) => Compare(leftVersion, rightVersion), + (true, _, _, true) => Compare(leftNum, rightVersion), + (_, true, true, _) => Compare(leftVersion, rightNum), - // Throw error here as this code should be unreachable - ErrorUtilities.ThrowInternalErrorUnreachable(); - return false; + _ => false + }; } } } diff --git a/src/Build/Evaluation/Conditionals/NumericExpressionNode.cs b/src/Build/Evaluation/Conditionals/NumericExpressionNode.cs index 306db6d802b..fe21a15a1ad 100644 --- a/src/Build/Evaluation/Conditionals/NumericExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/NumericExpressionNode.cs @@ -21,60 +21,20 @@ internal NumericExpressionNode(string value) _value = value; } - /// - /// Evaluate as boolean - /// - internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - // Should be unreachable: all calls check CanBoolEvaluate() first - ErrorUtilities.VerifyThrow(false, "Can't evaluate a numeric expression as boolean."); - return false; - } - - /// - /// Evaluate as numeric - /// - internal override double NumericEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - return ConversionUtilities.ConvertDecimalOrHexToDouble(_value); - } - - /// - /// Evaluate as a Version - /// - internal override Version VersionEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state, out bool result) { - return Version.Parse(_value); - } - - /// - /// Whether it can be evaluated as a boolean: never allowed for numerics - /// - internal override bool CanBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - // Numeric expressions are never allowed to be treated as booleans. + result = default; return false; } - /// - /// Whether it can be evaluated as numeric - /// - internal override bool CanNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state, out double result) { - // It is not always possible to numerically evaluate even a numerical expression - - // for example, it may overflow a double. So check here. - return ConversionUtilities.ValidDecimalOrHexNumber(_value); + return ConversionUtilities.TryConvertDecimalOrHexToDouble(_value, out result); } - /// - /// Whether it can be evaluated as a Version - /// - internal override bool CanVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state, out Version result) { - // Check if the value can be formatted as a Version number - // This is needed for nodes that identify as Numeric but can't be parsed as numbers (e.g. 8.1.1.0 vs 8.1) - Version unused; - return Version.TryParse(_value, out unused); + return Version.TryParse(_value, out result); } /// diff --git a/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs b/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs index 918e188ff85..d5520e01bd7 100644 --- a/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs @@ -11,48 +11,23 @@ namespace Microsoft.Build.Evaluation /// internal abstract class OperatorExpressionNode : GenericExpressionNode { - /// - /// Numeric evaluation is never allowed for operators - /// - internal override double NumericEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - // Should be unreachable: all calls check CanNumericEvaluate() first - ErrorUtilities.VerifyThrow(false, "Cannot numeric evaluate an operator"); - return 0.0D; - } - - /// - /// Version evaluation is never allowed for operators - /// - internal override Version VersionEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - ErrorUtilities.VerifyThrow(false, "Cannot version evaluate an operator"); - return null; - } - - /// - /// Whether boolean evaluation is allowed: always allowed for operators - /// - internal override bool CanBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state, out bool result) { + result = BoolEvaluate(state); return true; } - /// - /// Whether the node can be evaluated as a numeric: by default, - /// this is not allowed - /// - internal override bool CanNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal abstract bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state); + + internal override bool TryNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state, out double result) { + result = default; return false; } - /// - /// Whether the node can be evaluated as a version: by default, - /// this is not allowed - /// - internal override bool CanVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state, out Version result) { + result = default; return false; } diff --git a/src/Build/Evaluation/Conditionals/OrExpressionNode.cs b/src/Build/Evaluation/Conditionals/OrExpressionNode.cs index e9469f07aca..73a91600f9b 100644 --- a/src/Build/Evaluation/Conditionals/OrExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/OrExpressionNode.cs @@ -19,14 +19,14 @@ internal sealed class OrExpressionNode : OperatorExpressionNode internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) { ProjectErrorUtilities.VerifyThrowInvalidProject - (LeftChild.CanBoolEvaluate(state), + (LeftChild.TryBoolEvaluate(state, out bool leftBool), state.ElementLocation, "ExpressionDoesNotEvaluateToBoolean", LeftChild.GetUnexpandedValue(state), LeftChild.GetExpandedValue(state), state.Condition); - if (LeftChild.BoolEvaluate(state)) + if (leftBool) { // Short circuit return true; @@ -34,14 +34,14 @@ internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState else { ProjectErrorUtilities.VerifyThrowInvalidProject - (RightChild.CanBoolEvaluate(state), + (RightChild.TryBoolEvaluate(state, out bool rightBool), state.ElementLocation, "ExpressionDoesNotEvaluateToBoolean", RightChild.GetUnexpandedValue(state), RightChild.GetExpandedValue(state), state.Condition); - return RightChild.BoolEvaluate(state); + return rightBool; } } diff --git a/src/Build/Evaluation/Conditionals/StringExpressionNode.cs b/src/Build/Evaluation/Conditionals/StringExpressionNode.cs index 61eabd438fd..54afb1dc6b8 100644 --- a/src/Build/Evaluation/Conditionals/StringExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/StringExpressionNode.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; - using Microsoft.Build.Shared; namespace Microsoft.Build.Evaluation @@ -29,60 +28,35 @@ internal StringExpressionNode(string value, bool expandable) _expandable = expandable; } - /// - /// Evaluate as boolean - /// - internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state, out bool result) { - return ConversionUtilities.ConvertStringToBool(GetExpandedValue(state)); + return ConversionUtilities.TryConvertStringToBool(GetExpandedValue(state), out result); } - /// - /// Evaluate as numeric - /// - internal override double NumericEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state, out double result) { if (ShouldBeTreatedAsVisualStudioVersion(state)) { - return ConversionUtilities.ConvertDecimalOrHexToDouble(MSBuildConstants.CurrentVisualStudioVersion); + result = ConversionUtilities.ConvertDecimalOrHexToDouble(MSBuildConstants.CurrentVisualStudioVersion); + return true; } - - return ConversionUtilities.ConvertDecimalOrHexToDouble(GetExpandedValue(state)); - } - - internal override Version VersionEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - if (ShouldBeTreatedAsVisualStudioVersion(state)) + else { - return Version.Parse(MSBuildConstants.CurrentVisualStudioVersion); + return ConversionUtilities.TryConvertDecimalOrHexToDouble(GetExpandedValue(state), out result); } - - return Version.Parse(GetExpandedValue(state)); } - internal override bool CanBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - return ConversionUtilities.CanConvertStringToBool(GetExpandedValue(state)); - } - - internal override bool CanNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state) + internal override bool TryVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state, out Version result) { if (ShouldBeTreatedAsVisualStudioVersion(state)) { + result = Version.Parse(MSBuildConstants.CurrentVisualStudioVersion); return true; } - - return ConversionUtilities.ValidDecimalOrHexNumber(GetExpandedValue(state)); - } - - internal override bool CanVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state) - { - if (ShouldBeTreatedAsVisualStudioVersion(state)) + else { - return true; + return Version.TryParse(GetExpandedValue(state), out result); } - - return Version.TryParse(GetExpandedValue(state), out _); } /// @@ -98,6 +72,25 @@ internal override bool EvaluatesToEmpty(ConditionEvaluator.IConditionEvaluationS { if (_expandable) { + switch (_value.Length) + { + case 0: + _cachedExpandedValue = String.Empty; + return true; + // If the length is 1 or 2, it can't possibly be a property, item, or metadata, and it isn't empty. + case 1: + case 2: + _cachedExpandedValue = _value; + return false; + default: + if (_value[1] != '(' || (_value[0] != '$' && _value[0] != '%' && _value[0] != '@') || _value[_value.Length - 1] != ')') + { + // This isn't just a property, item, or metadata value, and it isn't empty. + return false; + } + break; + } + string expandBreakEarly = state.ExpandIntoStringBreakEarly(_value); if (expandBreakEarly == null) diff --git a/src/Build/Evaluation/ExpressionShredder.cs b/src/Build/Evaluation/ExpressionShredder.cs index ffa04158d9d..1ea8786cf67 100644 --- a/src/Build/Evaluation/ExpressionShredder.cs +++ b/src/Build/Evaluation/ExpressionShredder.cs @@ -110,12 +110,14 @@ internal static List GetReferencedItemExpressions(string { List subExpressions = null; - if (expression.IndexOf('@') < 0) + int startIndex = expression.IndexOf('@', start, end - start); + + if (startIndex < 0) { return null; } - for (int i = start; i < end; i++) + for (int i = startIndex; i < end; i++) { int restartPoint; int startPoint; diff --git a/src/Shared/ConversionUtilities.cs b/src/Shared/ConversionUtilities.cs index 351b3b04e46..c2e424e322e 100644 --- a/src/Shared/ConversionUtilities.cs +++ b/src/Shared/ConversionUtilities.cs @@ -64,6 +64,21 @@ internal static string ConvertByteArrayToHex(byte[] bytes) return sb.ToString(); } + internal static bool TryConvertStringToBool(string parameterValue, out bool boolValue) + { + boolValue = false; + if (ValidBooleanTrue(parameterValue)) + { + boolValue = true; + return true; + } + else if (ValidBooleanFalse(parameterValue)) + { + return true; + } + return false; + } + /// /// Returns true if the string can be successfully converted to a bool, /// such as "on" or "yes" @@ -123,30 +138,40 @@ internal static double ConvertHexToDouble(string number) /// internal static double ConvertDecimalOrHexToDouble(string number) { - if (ConversionUtilities.ValidDecimalNumber(number)) + if (TryConvertDecimalOrHexToDouble(number, out double result)) { - return ConversionUtilities.ConvertDecimalToDouble(number); + return result; } - else if (ConversionUtilities.ValidHexNumber(number)) + ErrorUtilities.VerifyThrow(false, "Cannot numeric evaluate"); + return 0.0D; + } + + internal static bool TryConvertDecimalOrHexToDouble(string number, out double doubleValue) + { + if (ConversionUtilities.ValidDecimalNumber(number, out doubleValue)) { - return ConversionUtilities.ConvertHexToDouble(number); + return true; + } + else if (ConversionUtilities.ValidHexNumber(number, out int hexValue)) + { + doubleValue = (double)hexValue; + return true; } else { - ErrorUtilities.VerifyThrow(false, "Cannot numeric evaluate"); - return 0.0D; + return false; } } /// /// Returns true if the string is a valid hex number, like "0xABC" /// - private static bool ValidHexNumber(string number) + private static bool ValidHexNumber(string number, out int value) { bool canConvert = false; + value = 0; if (number.Length >= 3 && number[0] == '0' && (number[1] == 'x' || number[1] == 'X')) { - int value; canConvert = Int32.TryParse(number.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat, out value); } return canConvert; @@ -155,9 +180,8 @@ private static bool ValidHexNumber(string number) /// /// Returns true if the string is a valid decimal number, like "-123.456" /// - private static bool ValidDecimalNumber(string number) + private static bool ValidDecimalNumber(string number, out double value) { - double value; return Double.TryParse(number, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture.NumberFormat, out value) && !double.IsInfinity(value); } @@ -166,7 +190,7 @@ private static bool ValidDecimalNumber(string number) /// internal static bool ValidDecimalOrHexNumber(string number) { - return ValidDecimalNumber(number) || ValidHexNumber(number); + return ValidDecimalNumber(number, out _) || ValidHexNumber(number, out _); } } }