Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove redundant code from matcher infrastructure #514

Merged
merged 5 commits into from Nov 6, 2017

Conversation

stakx
Copy link
Contributor

@stakx stakx commented Nov 5, 2017

The matcher infrastructure is currently somewhat difficult to understand:

Current situation:

  • There is Match and Match<T>, which is basically a wrapper around a Predicate<T>. (Unfortunately, this delegate type was chosen over Func<T, bool>, but alas.)
  • Then there is IMatcher, having several implementations in the Moq.Matchers namespace.
  • One of those is Matcher, which wraps a Match<T> (which wraps a Predicate<T>).

Regarding extensibility, the situation is similarly complex:

  • There is a [Matcher] custom attribute, which appears to be the extension point for the infrastructure that was built first.
  • Then there is the static Match.Create method, which supposedly deprecates [Matcher].
  • Then there is a [AdvancedMatcher] attribute, which is perhaps the newest addition and related to IMatcher, but hardly used at all.

Changes:

This PR removes some of these things that aren't actually used, or don't make any difference whatsoever.

  • Matcher is removed, as Match can already do the same things.
  • [Matcher] is marked as obsolete.
  • [AdvancedMatcher] is removed, as it has no observable effect.

@stakx stakx added the draft label Nov 5, 2017
@stakx stakx force-pushed the matchers branch 2 times, most recently from bd65ab0 to b305ccc Compare November 6, 2017 09:03
@@ -202,16 +202,11 @@ public static Expression PartialMatcherAwareEval(this Expression expression)

private static bool ReturnsMatch(MethodCallExpression expression)
{
if (!expression.Method.IsDefined(typeof(AdvancedMatcherAttribute), true))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One would expect there to be a check for the obsolete [Matcher] attribute, but such a check wasn't there previously. It might be good to investigate more thoroughly exactly when ReturnsMatch gets used.

@@ -61,11 +66,6 @@ public static IMatcher CreateMatcher(Expression expression, bool isParams)
// the values are ints, but if the method to call
// expects, say, a double, a Convert node will be on
// the expression.
if (isParams && (expression.NodeType == ExpressionType.NewArrayInit || !expression.Type.IsArray))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets moved up a few lines because the preceding comment belongs with code further below; but shouldn't the type conversion code be the first thing in the method instead?

var matcher = new MatcherAttributeMatcher();
matcher.Initialize(originalExpression);
return matcher;
return new MatcherAttributeMatcher(call);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing call instead of originalExpression is actually a small change, as the latter has had any type conversion node removed and the former hasn't. But it would seem more correct since code inside MatcherAttributeMatcher silently assumes that it's given a MethodCallExpression (and not a conversion node).


namespace Moq.Matchers
{
internal class PredicateMatcher : IMatcher
Copy link
Contributor Author

@stakx stakx Nov 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was used together with [AdvancedMatcher] on ItExpr.Is. Surprisingly, it doesn't appear to make any difference whatsoever whether that attribute is there or not. Perhaps check again whether diagnostic output (rendered expression in error messages) is affected, as that might be something not covered by any test.

@@ -157,6 +157,8 @@ namespace Moq
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This feature has been deprecated in favor of `Match.Create`.")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An existing remark in the documentation comment already states that this type has been deprecated, this just formalises it.

This extensibility point has never been part of the public API, and
its internal use (in Moq's codebase) is minimal (`ItExpr.IsAny`).
Furthermore, it does not appear to make any difference whatsoever.
Therefore, let's remove it.
Now that `[AdvancedMatcher]` is gone, two-step initialization is no
longer needed and we can get rid of this anti-pattern.
@stakx stakx changed the title Reduce expression tree compilation in matcher infrastructure Remove redundant code from matcher infrastructure Nov 6, 2017

return false;
}
return Match.Create<Order>(o => o.Amount >= 1000);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what was originally used before the introduction of [AdvancedMatcher].


if (attr != null)
{
var matcher = attr.CreateMatcher();
matcher.Initialize(originalExpression);
return matcher;
}
else if (staticMatcherMethodAttr != null)
#pragma warning disable 618
else if (call.Method.IsDefined(typeof(MatcherAttribute), true))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor optimization here: We don't need the actual attribute (GetCustomAttribute; above), we just need to know whether it is there or not (IsDefined). This call is about twice as fast.

The former is completely unnecessary if we just let the latter imple-
ment `IMatcher` directly. (Ideally, it would be possible to rename
`Match` to `Matcher` for consistency, but that's not easily possible
as `Match` is part of the public API.)
@stakx stakx removed the draft label Nov 6, 2017
@stakx stakx merged commit 0e5f70d into devlooped:develop Nov 6, 2017
@stakx stakx deleted the matchers branch November 6, 2017 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant