Skip to content

Commit

Permalink
Revise code as per feedback. Add additional test cases and revise rul…
Browse files Browse the repository at this point in the history
…e code.
  • Loading branch information
nolanlum committed Nov 29, 2010
1 parent eded840 commit e6105e8
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 21 deletions.
Expand Up @@ -42,25 +42,34 @@ namespace Gendarme.Rules.Performance {
/// <example>
/// Bad example:
/// <code>
/// public override string ToString()
/// public override string ToString ()
/// {
/// return base.ToString();
/// return base.ToString ();
/// }
/// </code>
/// </example>
/// <example>
/// Good example:
/// Good example (1):
/// <code>
/// [FileIOPermission (SecurityAction.Demand, @"c:\dir\file")]
/// public override string ToString()
/// public override string ToString ()
/// {
/// return base.ToString();
/// return base.ToString ();
/// }
/// </code>
/// </example>
/// <example>
/// Good example (2):
/// <code>
/// /*public override string ToString ()
/// {
/// return base.ToString ();
/// }*/
/// </code>
/// </example>

[Problem ("This override of a base class method is unnecessary.")]
[Solution ("Remove the override method.")]
[Solution ("Remove the override method or extend the functionality of the method.")]
public class AvoidUnnecessaryOverridesRule : Rule, ITypeRule {

public RuleResult CheckType(TypeDefinition type)
Expand Down Expand Up @@ -88,9 +97,8 @@ public RuleResult CheckType(TypeDefinition type)
while (i < instrs.Count && (instrs[i].Is (Code.Nop) || instrs[i].IsLoadArgument ()))
i++;

// If the next instruction is not a call we are good. (Relatively certain
// calls to the base class cannot be virtual.)
if (!instrs[i].Is (Code.Call))
// If the next instruction is not a call we are good.
if (!instrs[i].Is (Code.Call) && !instrs[i].Is(Code.Call))

This comment has been minimized.

Copy link
@spouliot

spouliot Dec 1, 2010

I bet you meant Callvirt in the second case ;-)
missing space before '(' too

continue;
// Check to make sure the call is to the base class, and the same method name...
MethodReference mr = instrs[i].Operand as MethodReference;
Expand All @@ -102,6 +110,26 @@ public RuleResult CheckType(TypeDefinition type)
continue;
if (mr.Name != method.Name)
continue;
// Account for calling overrides.
if (mr.HasParameters != method.HasParameters)
continue;
// Check the parameter count and type.
if (mr.Parameters.Count != method.Parameters.Count)

This comment has been minimized.

Copy link
@spouliot

spouliot Dec 1, 2010

This would compare two empty collections (where above check would be false != false, skipping the continue.
That defeats the purpose of HasParameters property - which is to avoid the lazy creation of the collections (allocation, GC tracking, release of memory) only to return a Count of 0.

continue;
// Shallow check for equality.
bool sameParam = true;
for (int o = 0; o < mr.Parameters.Count; o++) {
var p1 = mr.Parameters[o];
var p2 = method.Parameters[o];
sameParam = true;
sameParam &= p1.HasDefault == p2.HasDefault;
sameParam &= p1.HasFieldMarshal == p2.HasFieldMarshal;
sameParam &= p1.IsOptional == p2.IsOptional;
sameParam &= p1.IsOut == p2.IsOut;
sameParam &= p1.ParameterType == p2.ParameterType;
if (!sameParam) break;

This comment has been minimized.

Copy link
@spouliot

spouliot Dec 1, 2010

Pretty complete :-) however I think it's simpler (and even more complete) to just compare the Attributes properties (of both p1 and p2) since all the above (except ParameterType) properties are based on that value.
see Mono.Cecil/Mono.Cecil/ParameterDefinition.cs source code

}
if (!sameParam) continue;

This comment has been minimized.

Copy link
@spouliot

spouliot Dec 1, 2010

break; and continue; should be on separate lines


i++;
// If the return type is void, all we should have is nop and return.
Expand Down
Expand Up @@ -35,74 +35,109 @@

using Test.Rules.Definitions;
using Test.Rules.Fixtures;
using Test.Rules.Helpers;

namespace Tests.Rules.Performance {

[TestFixture]
public class AvoidUnnecessaryOverridesTest : TypeRuleTestFixture <AvoidUnnecessaryOverridesRule> {

private class TestBaseClass {
public virtual string DoSomething(string s)
public virtual string DoSomething (string s)
{
return s;
}
public virtual string DoSomething ()
{
return ":D";
}
}

private class TestClassGood : TestBaseClass {
[STAThread]
public override string DoSomething(string s)
public override string DoSomething (string s)
{
return base.DoSomething (s);
}
public override string DoSomething ()
{
return base.DoSomething (":P");
}
[FileIOPermission (SecurityAction.Demand)]
public override string ToString()
public override string ToString ()
{
return base.ToString ();
}
public override bool Equals(object obj)
public override bool Equals (object obj)
{
if (obj == null)
return false;
else
return base.Equals (obj);
}
}

private class TestClassAlsoGood : ApplicationException {
public override bool Equals (object obj)
{
if (obj.GetType () != typeof (TestClassAlsoGood))
return false;

return base.Equals (obj);
}
}

private class TestClassBad : TestBaseClass {
public override string ToString()
public override string ToString ()
{
return base.ToString ();
}
public override string DoSomething(string s)
public override string DoSomething (string s)
{
return base.DoSomething (s);
}
public override string DoSomething ()
{
return base.DoSomething ();
}
}

private class TestClassAlsoBad : ApplicationException {
public override Exception GetBaseException ()
{
return base.GetBaseException ();
}
}

private class MySimpleClass {
}

private Mono.Cecil.TypeDefinition SimpleClassNoMethods;

[SetUp]
public void SetUp()
public void SetUp ()
{
// Classes always have a constuctor so we hack our own methodless class.
SimpleClassNoMethods = SimpleTypes.Class;
SimpleClassNoMethods = DefinitionLoader.GetTypeDefinition<MySimpleClass> ();
SimpleClassNoMethods.Methods.Clear ();
}

[Test]
public void Good()
public void Good ()
{
AssertRuleSuccess<TestClassGood> ();
AssertRuleSuccess<TestClassAlsoGood> ();
}

[Test]
public void Bad()
public void Bad ()
{
AssertRuleFailure<TestClassBad> (2);
AssertRuleFailure<TestClassBad> (3);
AssertRuleFailure<TestClassAlsoBad> (1);
}

[Test]
public void DoesNotApply()
public void DoesNotApply ()
{
AssertRuleDoesNotApply (SimpleClassNoMethods);
}
Expand Down

2 comments on commit e6105e8

@spouliot
Copy link

Choose a reason for hiding this comment

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

a bug (in either Firefox and github) does not let me scroll the source past line 135. I'll try to refresh the page and continue.

@spouliot
Copy link

Choose a reason for hiding this comment

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

ah, the patch goes no further :-) but the horizontal scrollbar was hiding this.

Please sign in to comment.