Skip to content

Commit

Permalink
Merge branch 'next' into FixNegLineNumberDetectedInGoToNegOne
Browse files Browse the repository at this point in the history
  • Loading branch information
MDoerner committed Oct 11, 2020
2 parents 6cb105d + 136889f commit c5ec039
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 131 deletions.
Expand Up @@ -21,10 +21,10 @@ private void ListView_SynchronizeCurrentSelection_OnGotFocus(object sender, Rout
UpdateCurrentSelection((Selector)sender);

var cs = ViewModel.CurrentSelection;
Description.Text = cs.Description;
Version.Text = cs.Version;
LocaleName.Text = cs.LocaleName;
FullPath.Text = cs.FullPath;
Description.Text = cs?.Description ?? string.Empty;
Version.Text = cs?.Version ?? string.Empty;
LocaleName.Text = cs?.LocaleName ?? string.Empty;
FullPath.Text = cs?.FullPath ?? string.Empty;
}

private void UpdateCurrentSelection(Selector sender)
Expand Down
Expand Up @@ -31,15 +31,14 @@ public class PermissiveAssertClass : AssertClass
/// </remarks>
public override void AreEqual(object expected, object actual, string message = "")
{
// vbNullString is marshalled as null. assume value semantics:
expected = expected ?? string.Empty;
actual = actual ?? string.Empty;

if (!PermissiveComparer.Equals(expected, actual))
if (PermissiveComparer.Equals(expected, actual))
{
AssertHandler.OnAssertSucceeded();
}
else
{
AssertHandler.OnAssertFailed(message);
}
AssertHandler.OnAssertSucceeded();
}

public override void AreNotEqual(object expected, object actual, string message = "")
Expand Down
48 changes: 25 additions & 23 deletions Rubberduck.Parsing/Preprocessing/VBAConditionalCompilationParser.g4
Expand Up @@ -10,17 +10,17 @@ ccBlock :
(ccConst | ccIfBlock | physicalLine)*?
;

ccConst : WS* hashConst WS+ ccVarLhs WS* EQ WS* ccExpression ccEol;
ccConst : whiteSpace* hashConst whiteSpace+ ccVarLhs whiteSpace* EQ whiteSpace* ccExpression ccEol;
ccVarLhs : name;

ccIfBlock : ccIf ccBlock ccElseIfBlock* ccElseBlock? ccEndIf;
ccIf : WS* hashIf WS+ ccExpression WS+ THEN ccEol;
ccIf : whiteSpace* hashIf whiteSpace+ ccExpression whiteSpace+ THEN ccEol;
ccElseIfBlock : ccElseIf ccBlock;
ccElseIf : WS* hashElseIf WS+ ccExpression WS+ THEN ccEol;
ccElseIf : whiteSpace* hashElseIf whiteSpace+ ccExpression whiteSpace+ THEN ccEol;
ccElseBlock : ccElse ccBlock;
ccElse : WS* hashElse ccEol;
ccEndIf : WS* hashEndIf ccEol;
ccEol : WS* comment? (NEWLINE | EOF);
ccElse : whiteSpace* hashElse ccEol;
ccEndIf : whiteSpace* hashEndIf ccEol;
ccEol : whiteSpace* comment? (NEWLINE | EOF);
// We use parser rules instead of tokens (such as HASHCONST) because
// marked file numbers have a similar format and cause conflicts.
hashConst : HASH CONST;
Expand All @@ -32,26 +32,26 @@ hashEndIf : HASH END_IF;
physicalLine : ~(NEWLINE | EOF)* (NEWLINE | EOF);

ccExpression :
LPAREN WS* ccExpression WS* RPAREN
| ccExpression WS* POW WS* ccExpression
| MINUS WS* ccExpression
| ccExpression WS* (MULT | DIV) WS* ccExpression
| ccExpression WS* INTDIV WS* ccExpression
| ccExpression WS* MOD WS* ccExpression
| ccExpression WS* (PLUS | MINUS) WS* ccExpression
| ccExpression WS* AMPERSAND WS* ccExpression
| ccExpression WS* (EQ | NEQ | LT | GT | LEQ | GEQ | LIKE | IS) WS* ccExpression
| NOT WS* ccExpression
| ccExpression WS* AND WS* ccExpression
| ccExpression WS* OR WS* ccExpression
| ccExpression WS* XOR WS* ccExpression
| ccExpression WS* EQV WS* ccExpression
| ccExpression WS* IMP WS* ccExpression
LPAREN whiteSpace* ccExpression whiteSpace* RPAREN
| ccExpression whiteSpace* POW whiteSpace* ccExpression
| MINUS whiteSpace* ccExpression
| ccExpression whiteSpace* (MULT | DIV) whiteSpace* ccExpression
| ccExpression whiteSpace* INTDIV whiteSpace* ccExpression
| ccExpression whiteSpace* MOD whiteSpace* ccExpression
| ccExpression whiteSpace* (PLUS | MINUS) whiteSpace* ccExpression
| ccExpression whiteSpace* AMPERSAND whiteSpace* ccExpression
| ccExpression whiteSpace* (EQ | NEQ | LT | GT | LEQ | GEQ | LIKE | IS) whiteSpace* ccExpression
| NOT whiteSpace* ccExpression
| ccExpression whiteSpace* AND whiteSpace* ccExpression
| ccExpression whiteSpace* OR whiteSpace* ccExpression
| ccExpression whiteSpace* XOR whiteSpace* ccExpression
| ccExpression whiteSpace* EQV whiteSpace* ccExpression
| ccExpression whiteSpace* IMP whiteSpace* ccExpression
| intrinsicFunction
| literal
| name;

intrinsicFunction : intrinsicFunctionName LPAREN WS* ccExpression WS* RPAREN;
intrinsicFunction : intrinsicFunctionName LPAREN whiteSpace* ccExpression whiteSpace* RPAREN;

intrinsicFunctionName :
INT
Expand Down Expand Up @@ -270,4 +270,6 @@ statementKeyword :
| WEND
| WHILE
| WITH
;
;

whiteSpace : (WS | LINE_CONTINUATION)+;
Expand Up @@ -14,19 +14,7 @@ public class PermissiveObjectComparer : IEqualityComparer<object>
/// <returns>VBA equity</returns>
public new bool Equals(object x, object y)
{
if (x == null)
{
return y == null;
}

if (y == null)
{
return false;
}

var converted = VariantConverter.ChangeType(y, x.GetType());

return x.Equals(converted);
return VariantComparer.Compare(x, y) == VariantComparisonResults.VARCMP_EQ;
}

/// <summary>
Expand Down
31 changes: 31 additions & 0 deletions Rubberduck.VBEEditor/Variants/VariantComparer.cs
@@ -0,0 +1,31 @@
using System.Globalization;

namespace Rubberduck.VBEditor.Variants
{
public static class VariantComparer
{
public static VariantComparisonResults Compare(object x, object y)
{
return Compare(x, y, VariantComparisonFlags.NORM_IGNORECASE);
}

public static VariantComparisonResults Compare(object x, object y, VariantComparisonFlags flags)
{
return Compare(x, y, CultureInfo.InvariantCulture.LCID, flags);
}

public static VariantComparisonResults Compare(object x, object y, int lcid, VariantComparisonFlags flags)
{
object dy;
try
{
dy = VariantConverter.ChangeType(y, x.GetType());
}
catch
{
dy = y;
}
return (VariantComparisonResults)VariantNativeMethods.VarCmp(ref x, ref dy, lcid, (uint)flags);
}
}
}
78 changes: 3 additions & 75 deletions Rubberduck.VBEEditor/Variants/VariantConverter.cs
Expand Up @@ -4,56 +4,6 @@

namespace Rubberduck.VBEditor.Variants
{
public enum VARENUM
{
VT_EMPTY = 0x0000,
VT_NULL = 0x0001,
VT_I2 = 0x0002,
VT_I4 = 0x0003,
VT_R4 = 0x0004,
VT_R8 = 0x0005,
VT_CY = 0x0006,
VT_DATE = 0x0007,
VT_BSTR = 0x0008,
VT_DISPATCH = 0x0009,
VT_ERROR = 0x000A,
VT_BOOL = 0x000B,
VT_VARIANT = 0x000C,
VT_UNKNOWN = 0x000D,
VT_DECIMAL = 0x000E,
VT_I1 = 0x0010,
VT_UI1 = 0x0011,
VT_UI2 = 0x0012,
VT_UI4 = 0x0013,
VT_I8 = 0x0014,
VT_UI8 = 0x0015,
VT_INT = 0x0016,
VT_UINT = 0x0017,
VT_VOID = 0x0018,
VT_HRESULT = 0x0019,
VT_PTR = 0x001A,
VT_SAFEARRAY = 0x001B,
VT_CARRAY = 0x001C,
VT_USERDEFINED = 0x001D,
VT_LPSTR = 0x001E,
VT_LPWSTR = 0x001F,
VT_RECORD = 0x0024,
VT_INT_PTR = 0x0025,
VT_UINT_PTR = 0x0026,
VT_ARRAY = 0x2000,
VT_BYREF = 0x4000
}

[Flags]
public enum VariantConversionFlags : ushort
{
NO_FLAGS = 0x00,
VARIANT_NOVALUEPROP = 0x01, //Prevents the function from attempting to coerce an object to a fundamental type by getting the Value property. Applications should set this flag only if necessary, because it makes their behavior inconsistent with other applications.
VARIANT_ALPHABOOL = 0x02, //Converts a VT_BOOL value to a string containing either "True" or "False".
VARIANT_NOUSEROVERRIDE = 0x04, //For conversions to or from VT_BSTR, passes LOCALE_NOUSEROVERRIDE to the core coercion routines.
VARIANT_LOCALBOOL = 0x08 //For conversions from VT_BOOL to VT_BSTR and back, uses the language specified by the locale in use on the local computer.
}

/// <summary>
/// Handles variant conversions, enabling us to have same implicit conversion behaviors within
/// .NET as we can observe it from VBA/VB6.
Expand All @@ -67,40 +17,18 @@ public enum VariantConversionFlags : ushort
/// </remarks>
public static class VariantConverter
{
private const string dllName = "oleaut32.dll";

// HRESULT VariantChangeType(
// VARIANTARG *pvargDest,
// const VARIANTARG *pvarSrc,
// USHORT wFlags,
// VARTYPE vt
// );
[DllImport(dllName, EntryPoint = "VariantChangeType", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
private static extern int VariantChangeType(ref object pvargDest, ref object pvarSrc, VariantConversionFlags wFlags, VARENUM vt);

// HRESULT VariantChangeTypeEx(
// VARIANTARG *pvargDest,
// const VARIANTARG *pvarSrc,
// LCID lcid,
// USHORT wFlags,
// VARTYPE vt
// );
[DllImport(dllName, EntryPoint = "VariantChangeTypeEx", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
private static extern int VariantChangeTypeEx(ref object pvargDest, ref object pvarSrc, int lcid, VariantConversionFlags wFlags, VARENUM vt);

public static object ChangeType(object value, VARENUM vt)
{
return ChangeType(value, vt, null);
}

private static bool HRESULT_FAILED(int hr) => hr < 0;
public static object ChangeType(object value, VARENUM vt, CultureInfo cultureInfo)
{
object result = null;
var hr = cultureInfo == null
? VariantChangeType(ref result, ref value, VariantConversionFlags.NO_FLAGS, vt)
: VariantChangeTypeEx(ref result, ref value, cultureInfo.LCID, VariantConversionFlags.NO_FLAGS, vt);
if (HRESULT_FAILED(hr))
? VariantNativeMethods.VariantChangeType(ref result, ref value, VariantConversionFlags.NO_FLAGS, vt)
: VariantNativeMethods.VariantChangeTypeEx(ref result, ref value, cultureInfo.LCID, VariantConversionFlags.NO_FLAGS, vt);
if (HResult.Failed(hr))
{
throw Marshal.GetExceptionForHR(hr);
}
Expand Down
114 changes: 114 additions & 0 deletions Rubberduck.VBEEditor/Variants/VariantNativeMethods.cs
@@ -0,0 +1,114 @@
using System;
using System.Runtime.InteropServices;

namespace Rubberduck.VBEditor.Variants
{
public enum VARENUM
{
VT_EMPTY = 0x0000,
VT_NULL = 0x0001,
VT_I2 = 0x0002,
VT_I4 = 0x0003,
VT_R4 = 0x0004,
VT_R8 = 0x0005,
VT_CY = 0x0006,
VT_DATE = 0x0007,
VT_BSTR = 0x0008,
VT_DISPATCH = 0x0009,
VT_ERROR = 0x000A,
VT_BOOL = 0x000B,
VT_VARIANT = 0x000C,
VT_UNKNOWN = 0x000D,
VT_DECIMAL = 0x000E,
VT_I1 = 0x0010,
VT_UI1 = 0x0011,
VT_UI2 = 0x0012,
VT_UI4 = 0x0013,
VT_I8 = 0x0014,
VT_UI8 = 0x0015,
VT_INT = 0x0016,
VT_UINT = 0x0017,
VT_VOID = 0x0018,
VT_HRESULT = 0x0019,
VT_PTR = 0x001A,
VT_SAFEARRAY = 0x001B,
VT_CARRAY = 0x001C,
VT_USERDEFINED = 0x001D,
VT_LPSTR = 0x001E,
VT_LPWSTR = 0x001F,
VT_RECORD = 0x0024,
VT_INT_PTR = 0x0025,
VT_UINT_PTR = 0x0026,
VT_ARRAY = 0x2000,
VT_BYREF = 0x4000
}

[Flags]
public enum VariantConversionFlags : ushort
{
NO_FLAGS = 0x00,
VARIANT_NOVALUEPROP = 0x01, //Prevents the function from attempting to coerce an object to a fundamental type by getting the Value property. Applications should set this flag only if necessary, because it makes their behavior inconsistent with other applications.
VARIANT_ALPHABOOL = 0x02, //Converts a VT_BOOL value to a string containing either "True" or "False".
VARIANT_NOUSEROVERRIDE = 0x04, //For conversions to or from VT_BSTR, passes LOCALE_NOUSEROVERRIDE to the core coercion routines.
VARIANT_LOCALBOOL = 0x08 //For conversions from VT_BOOL to VT_BSTR and back, uses the language specified by the locale in use on the local computer.
}

[Flags]
public enum VariantComparisonFlags : ulong
{
NO_FLAGS = 0x00,
NORM_IGNORECASE = 0x00000001, //Ignore case.
NORM_IGNORENONSPACE = 0x00000002, //Ignore nonspace characters.
NORM_IGNORESYMBOLS = 0x00000004, //Ignore symbols.
NORM_IGNOREWIDTH = 0x00000008, //Ignore string width.
NORM_IGNOREKANATYPE = 0x00000040, //Ignore Kana type.
NORM_IGNOREKASHIDA = 0x00040000 //Ignore Arabic kashida characters.
}

public enum VariantComparisonResults : int
{
VARCMP_LT = 0, //pvarLeft is less than pvarRight.
VARCMP_EQ = 1, //The parameters are equal.
VARCMP_GT = 2, //pvarLeft is greater than pvarRight.
VARCMP_NULL = 3 //Either expression is NULL.
}

public static class HResult
{
internal static bool Succeeded(int hr) => hr >= 0;
internal static bool Failed(int hr) => hr < 0;
}

internal static class VariantNativeMethods
{
private const string dllName = "oleaut32.dll";

// HRESULT VariantChangeType(
// VARIANTARG *pvargDest,
// const VARIANTARG *pvarSrc,
// USHORT wFlags,
// VARTYPE vt
// );
[DllImport(dllName, EntryPoint = "VariantChangeType", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
internal static extern int VariantChangeType(ref object pvargDest, ref object pvarSrc, VariantConversionFlags wFlags, VARENUM vt);

// HRESULT VariantChangeTypeEx(
// VARIANTARG *pvargDest,
// const VARIANTARG *pvarSrc,
// LCID lcid,
// USHORT wFlags,
// VARTYPE vt
// );
[DllImport(dllName, EntryPoint = "VariantChangeTypeEx", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
internal static extern int VariantChangeTypeEx(ref object pvargDest, ref object pvarSrc, int lcid, VariantConversionFlags wFlags, VARENUM vt);

// HRESULT VarCmp(
// LPVARIANT pvarLeft,
// LPVARIANT pvarRight,
// LCID lcid,
// ULONG dwFlags
// );
[DllImport(dllName, EntryPoint = "VarCmp", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
internal static extern int VarCmp(ref object pvarLeft, ref object pvarRight, int lcid, ulong dwFlags);
}
}

0 comments on commit c5ec039

Please sign in to comment.