On Thu, Dec 25, 2008 at 10:50 PM, Cedric Vivier <[email protected]> wrote:
> This new rule in Correctness checks that disposable locals are always
> disposed before the method returns.
>
Updated patch fixing false positives with foreach and generators.
Also attached patch fixing the only defect found with self-test:
Target: System.Void
Gendarme.Wizard::SaveReportButtonClick(System.Object,System.EventArgs)
Assembly: gendarme-wizard, Version=2.3.0.0, Culture=neutral,
PublicKeyToken=null
Severity: Medium Confidence: Normal
Source:
/home/cedric/dev/workspace/mono/gendarme/gendarme/swf-wizard-runner/Wizard.cs(≈656)
Details: Local 'writer' is not guaranteed to be disposed.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Gendarme" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/gendarme?hl=en
-~----------~----~----~----~------~----~------~--~---
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs b/gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs
index 8692838..140b1d6 100644
--- a/gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs
+++ b/gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs
@@ -120,8 +120,11 @@ namespace Gendarme.Rules.Correctness {
//ignore properties (likely not the place where the IDisposable is *created*)
if (call.IsProperty ())
return false;
- if (call.Name == MethodDefinition.Ctor || call.Name == MethodDefinition.Cctor)
+ if (call.Name == MethodDefinition.Ctor || call.Name == MethodDefinition.Cctor) {
+ if (call.DeclaringType.IsGeneratedCode ())
+ return false; //eg. generators
return call.DeclaringType.Implements ("System.IDisposable");
+ }
return call.ReturnType.ReturnType.Implements ("System.IDisposable");
}
@@ -138,10 +141,27 @@ namespace Gendarme.Rules.Correctness {
return false;
}
- static Instruction FindRelatedSuspectLocal (MethodDefinition method, Instruction ins)
+ static Instruction LocalTraceBack (MethodDefinition method, Instruction ins)
{
ins = ins.TraceBack (method);
- if (null == ins || !ins.IsLoadLocal ())
+ while (ins != null) {
+ if (ins.IsLoadLocal () || ins.IsStoreLocal ())
+ return ins;
+
+ if (ins.OpCode.FlowControl == FlowControl.Branch
+ || ins.OpCode.Code == Code.Isinst
+ || ins.OpCode.Code == Code.Dup) {
+ ins = ins.TraceBack (method);
+ } else
+ break;
+ }
+ return null;
+ }
+
+ static Instruction FindRelatedSuspectLocal (MethodDefinition method, Instruction ins)
+ {
+ ins = LocalTraceBack (method, ins);
+ if (null == ins)
return null;
int index = ins.GetVariable (method).Index;
@@ -182,13 +202,11 @@ namespace Gendarme.Rules.Correctness {
if (IsDispose (call)) {
Instruction local = FindRelatedSuspectLocal (method, ins);
if (local != null) {
- if (AreBothInstructionsInSameTryFinallyBlock (method.Body, local, ins)) {
- suspectLocals.Remove (local);
- } else {
+ if (!AreBothInstructionsInSameTryFinallyBlock (method.Body, local, ins)) {
string msg = string.Format ("Local {0}is not guaranteed to be disposed.", GetFriendlyNameOrEmpty (local.GetVariable (method)));
Runner.Report (method, local, Severity.Medium, Confidence.Normal, msg);
- suspectLocals.Remove (local);
}
+ suspectLocals.Remove (local);
}
continue;
}
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs b/gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs
index 660853d..900d921 100644
--- a/gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs
+++ b/gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs
@@ -27,6 +27,7 @@
using System;
using System.IO;
using System.Xml;
+using System.Collections.Generic;
using NUnit.Framework;
using Gendarme.Rules.Correctness;
@@ -89,6 +90,24 @@ namespace Test.Rules.Correctness {
sr = new StreamReader ("bar.xml"); //field
}
+ //foreach(enumerator)
+ static string Success5 (IEnumerable<string> strings)
+ {
+ foreach (var s in strings) {
+ if (s.Length > 0)
+ return s;
+ }
+ return null;
+ }
+
+ //generator
+ //csc/gmcs for some reason build an IDisposable iterator class that is never disposed
+ static IEnumerable<string> Success6 ()
+ {
+ yield return "foo";
+ yield return "bar";
+ }
+
void Failure0 () {
var reader = XmlReader.Create ("foo.xml");
Console.WriteLine (reader.ToString ());
@@ -197,6 +216,18 @@ namespace Test.Rules.Correctness {
}
[Test]
+ public void Success5 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success5");
+ }
+
+ [Test]
+ public void Success6 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success6");
+ }
+
+ [Test]
public void Failure0 ()
{
AssertRuleFailure<DisposalCases> ("Failure0", 1);
From 1564d8170ee600b769cf9aac8a9b812ab3195e60 Mon Sep 17 00:00:00 2001
From: Cedric Vivier <[email protected]>
Date: Thu, 25 Dec 2008 20:59:40 +0800
Subject: [PATCH] WIP in 'EnsureLocalDisposal' branch.
2008-12-25 Cedric Vivier <[email protected]>
* EnsureLocalDisposalRule.cs: New. Rule that checks if locals
are guaranteed to be disposed before the method returns.
---
.../rules/Gendarme.Rules.Correctness/ChangeLog | 5 +
.../EnsureLocalDisposalRule.cs | 250 ++++++++++++++++++
.../rules/Gendarme.Rules.Correctness/Makefile.am | 2 +
.../Test/EnsureLocalDisposalTest.cs | 266 ++++++++++++++++++++
4 files changed, 523 insertions(+), 0 deletions(-)
create mode 100644 gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs
create mode 100644 gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog b/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog
index f620970..7b6784f 100644
--- a/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog
+++ b/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog
@@ -1,3 +1,8 @@
+2008-12-25 Cedric Vivier <[email protected]>
+
+ * EnsureLocalDisposalRule.cs: New. Rule that checks if locals
+ are guaranteed to be disposed before the method returns.
+
2008-12-23 Cedric Vivier <[email protected]>
* ProvideCorrectRegexPatternRule.cs: New. Rule that checks if
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs b/gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs
new file mode 100644
index 0000000..140b1d6
--- /dev/null
+++ b/gendarme/rules/Gendarme.Rules.Correctness/EnsureLocalDisposalRule.cs
@@ -0,0 +1,250 @@
+//
+// Gendarme.Rules.Correctness.EnsureLocalDisposalRule class
+//
+// Authors:
+// Cedric Vivier <[email protected]>
+//
+// Copyright (C) 2008 Cedric Vivier
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Gendarme.Framework;
+using Gendarme.Framework.Rocks;
+using Gendarme.Framework.Engines;
+using Gendarme.Framework.Helpers;
+
+
+namespace Gendarme.Rules.Correctness {
+
+ /// <summary>
+ /// This rule checks that disposable locals are always disposed before the method returns.
+ /// Use 'using' statement (or a try/finally block) to guarantee local disposal even
+ /// in the event an unhandled exception occurs.
+ /// </summary>
+ /// <example>
+ /// Bad example (non-guaranteed disposal):
+ /// <code>
+ /// void DecodeFile (string file) {
+ /// var stream = new StreamReader (file);
+ /// DecodeHeader (stream);
+ /// if (!DecodedHeader.HasContent)
+ /// return;
+ /// DecodeContent (stream);
+ /// stream.Dispose ();
+ /// }
+ /// </code>
+ /// </example>
+ /// <example>
+ /// Good example (non-guaranteed disposal):
+ /// <code>
+ /// void DecodeFile (string file) {
+ /// using (var stream = new StreamReader (file)) {
+ /// DecodeHeader (stream);
+ /// if (!DecodedHeader.HasContent)
+ /// return;
+ /// DecodeContent (stream);
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ /// <example>
+ /// Bad example (not disposed / not locally disposed):
+ /// <code>
+ /// void DecodeFile (string file) {
+ /// var stream = new StreamReader (file);
+ /// Decode (stream);
+ /// }
+ ///
+ /// void Decode (Stream stream)
+ /// {
+ /// /*code to decode the stream*/
+ /// stream.Dispose ();
+ /// }
+ /// </code>
+ /// </example>
+ /// <example>
+ /// Good example (not disposed / not locally disposed):
+ /// <code>
+ /// void DecodeFile (string file) {
+ /// using (var stream = new StreamReader (file)) {
+ /// Decode (stream);
+ /// }
+ /// }
+ ///
+ /// void Decode (Stream stream)
+ /// {
+ /// /*code to decode the stream*/
+ /// }
+ /// </code>
+ /// </example>
+
+ [Problem ("This disposable local is not guaranteed to be disposed before the method returns.")]
+ [Solution ("Use 'using' statement or surround the local's usage with a try/finally block.")]
+ [EngineDependency (typeof (OpCodeEngine))]
+ public class EnsureLocalDisposalRule : Rule, IMethodRule {
+
+ static OpCodeBitmask callsAndNewobjBitmask = BuildCallsAndNewobjOpCodeBitmask ();
+ static HashSet<Instruction> suspectLocals = new HashSet<Instruction> ();
+
+ static bool IsDispose (MethodReference call)
+ {
+ if (!call.HasThis)
+ return false;
+ return MethodSignatures.Dispose.Matches (call) || MethodSignatures.DisposeExplicit.Matches (call);
+ }
+
+ static bool DoesReturnDisposable (MethodReference call)
+ {
+ //ignore properties (likely not the place where the IDisposable is *created*)
+ if (call.IsProperty ())
+ return false;
+ if (call.Name == MethodDefinition.Ctor || call.Name == MethodDefinition.Cctor) {
+ if (call.DeclaringType.IsGeneratedCode ())
+ return false; //eg. generators
+ return call.DeclaringType.Implements ("System.IDisposable");
+ }
+
+ return call.ReturnType.ReturnType.Implements ("System.IDisposable");
+ }
+
+ static bool AreBothInstructionsInSameTryFinallyBlock (MethodBody body, Instruction a, Instruction b)
+ {
+ foreach (ExceptionHandler eh in body.ExceptionHandlers) {
+ if (eh.Type != ExceptionHandlerType.Finally)
+ continue;
+ if (eh.TryStart.Offset <= a.Next.Offset && eh.TryEnd.Offset >= a.Offset
+ && eh.HandlerStart.Offset <= b.Offset && eh.HandlerEnd.Offset >= b.Offset)
+ return true;
+ }
+ return false;
+ }
+
+ static Instruction LocalTraceBack (MethodDefinition method, Instruction ins)
+ {
+ ins = ins.TraceBack (method);
+ while (ins != null) {
+ if (ins.IsLoadLocal () || ins.IsStoreLocal ())
+ return ins;
+
+ if (ins.OpCode.FlowControl == FlowControl.Branch
+ || ins.OpCode.Code == Code.Isinst
+ || ins.OpCode.Code == Code.Dup) {
+ ins = ins.TraceBack (method);
+ } else
+ break;
+ }
+ return null;
+ }
+
+ static Instruction FindRelatedSuspectLocal (MethodDefinition method, Instruction ins)
+ {
+ ins = LocalTraceBack (method, ins);
+ if (null == ins)
+ return null;
+
+ int index = ins.GetVariable (method).Index;
+ foreach (var local in suspectLocals) {
+ if (local.GetVariable (method).Index == index)
+ return local;
+ }
+ return null;
+ }
+
+ public RuleResult CheckMethod (MethodDefinition method)
+ {
+ if (!method.HasBody || method.IsGeneratedCode ())
+ return RuleResult.DoesNotApply;
+
+ //we ignore methods/constructors that returns IDisposable themselves
+ //where local(s) are most likely used for disposable object construction
+ if (DoesReturnDisposable (method))
+ return RuleResult.DoesNotApply;
+
+ //is there any potential IDisposable-getting opcode in the method?
+ OpCodeBitmask methodBitmask = OpCodeEngine.GetBitmask (method);
+ if (!callsAndNewobjBitmask.Intersect (methodBitmask))
+ return RuleResult.DoesNotApply;
+
+ //do we potentially store that IDisposable in a local?
+ if (!OpCodeBitmask.StoreLocal.Intersect (methodBitmask))
+ return RuleResult.DoesNotApply;
+
+ suspectLocals.Clear ();
+
+ foreach (Instruction ins in method.Body.Instructions) {
+ if (!callsAndNewobjBitmask.Get (ins.OpCode.Code))
+ continue;
+
+ MethodReference call = (MethodReference) ins.Operand;
+
+ if (IsDispose (call)) {
+ Instruction local = FindRelatedSuspectLocal (method, ins);
+ if (local != null) {
+ if (!AreBothInstructionsInSameTryFinallyBlock (method.Body, local, ins)) {
+ string msg = string.Format ("Local {0}is not guaranteed to be disposed.", GetFriendlyNameOrEmpty (local.GetVariable (method)));
+ Runner.Report (method, local, Severity.Medium, Confidence.Normal, msg);
+ }
+ suspectLocals.Remove (local);
+ }
+ continue;
+ }
+
+ if (ins.Next == null || !ins.Next.IsStoreLocal ())
+ continue; //even if an IDisposable, it isn't stored in a local
+
+ if (!DoesReturnDisposable (call))
+ continue;
+
+ suspectLocals.Add (ins.Next);
+ }
+
+ foreach (var local in suspectLocals) {
+ string msg = string.Format ("Local {0}is not disposed (at least not locally).", GetFriendlyNameOrEmpty (local.GetVariable (method)));
+ Runner.Report (method, local, Severity.High, Confidence.Normal, msg);
+ }
+
+ return Runner.CurrentRuleResult;
+ }
+
+ static string GetFriendlyNameOrEmpty (VariableReference variable)
+ {
+ if (null == variable.Name || variable.Name.StartsWith ("V_"))
+ return string.Empty;
+ return string.Format ("'{0}' ", variable.Name);
+ }
+
+ static OpCodeBitmask BuildCallsAndNewobjOpCodeBitmask ()
+ {
+ #if true
+ return new OpCodeBitmask (0x8000000000, 0x4400000000000, 0x0, 0x0);
+ #else
+ OpCodeBitmask mask = new OpCodeBitmask ();
+ mask.UnionWith (OpCodeBitmask.Calls);
+ mask.Set (Code.Newobj);
+ return mask;
+ #endif
+ }
+ }
+}
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am b/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am
index fae489d..e593edb 100644
--- a/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am
+++ b/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am
@@ -12,6 +12,7 @@ rules_sources = \
DisposableFieldsShouldBeDisposedRule.cs \
DoNotRoundIntegersRule.cs \
DontCompareWithNaNRule.cs \
+ EnsureLocalDisposalRule.cs \
FinalizersShouldCallBaseClassFinalizerRule.cs \
FloatComparisonRule.cs \
ReviewInconsistentIdentityRule.cs \
@@ -37,6 +38,7 @@ tests_sources = \
DisposableFieldsShouldBeDisposedTest.cs \
DontCompareWithNaNTest.cs \
DoNotRoundIntegersTest.cs \
+ EnsureLocalDisposalTest.cs \
FinalizersShouldCallBaseClassFinalizerTest.cs \
ReviewInconsistentIdentityTest.cs \
MethodCanBeMadeStaticTest.cs \
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs b/gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs
new file mode 100644
index 0000000..900d921
--- /dev/null
+++ b/gendarme/rules/Gendarme.Rules.Correctness/Test/EnsureLocalDisposalTest.cs
@@ -0,0 +1,266 @@
+//
+// Unit tests for EnsureLocalDisposalRule
+//
+// Authors:
+// Cedric Vivier <[email protected]>
+//
+// Copyright (C) 2008 Cedric Vivier
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Xml;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+using Gendarme.Rules.Correctness;
+
+using Test.Rules.Fixtures;
+using Test.Rules.Helpers;
+using Test.Rules.Definitions;
+
+namespace Test.Rules.Correctness {
+
+ class DisposalCases {
+ string foo;
+ StreamReader sr;
+
+ string DoesNotApply1 () { //no call/newobj/stloc
+ return foo;
+ }
+
+ XmlReader DoesNotApply2 () { //returns IDisposable
+ return null;
+ }
+
+ void DoesNotApply3 () {
+ sr = new StreamReader ("bar.xml"); //field
+ }
+
+ string Success0 () {
+ var o = new object ();
+ return o.ToString ();
+ }
+
+ void Success1 () {
+ using (var reader = XmlReader.Create ("foo.xml")) {
+ Console.WriteLine (reader.ToString ());
+ }
+ }
+
+ void Success2 (string pattern) {
+ using (var reader = XmlReader.Create ("foo.xml")) {
+ Console.WriteLine (reader.ToString ());
+ StreamReader reader2 = null;
+ try {
+ reader2 = new StreamReader ("bar.xml"); //newobj
+ } catch (InvalidOperationException e) {
+ Console.WriteLine (e.Message);
+ } finally {
+ if (reader2 != null)
+ reader2.Dispose ();
+ }
+ }
+ }
+
+ void Success3 (IDisposable disposable) {
+ int x = 0;
+ disposable.Dispose ();
+ }
+
+ void Success4 () {
+ int x = 0;
+ sr = new StreamReader ("bar.xml"); //field
+ }
+
+ //foreach(enumerator)
+ static string Success5 (IEnumerable<string> strings)
+ {
+ foreach (var s in strings) {
+ if (s.Length > 0)
+ return s;
+ }
+ return null;
+ }
+
+ //generator
+ //csc/gmcs for some reason build an IDisposable iterator class that is never disposed
+ static IEnumerable<string> Success6 ()
+ {
+ yield return "foo";
+ yield return "bar";
+ }
+
+ void Failure0 () {
+ var reader = XmlReader.Create ("foo.xml");
+ Console.WriteLine (reader.ToString ());
+ }
+
+ void Failure1 () {
+ var reader = XmlReader.Create ("foo.xml");
+ Console.WriteLine (reader.ToString ());
+ ((IDisposable) reader).Dispose (); //not guaranteed
+ }
+
+ void Failure2 () {
+ XmlReader reader = null;
+ try {
+ reader = XmlReader.Create ("foo.xml");
+ Console.WriteLine (reader.ToString ());
+ } catch (InvalidOperationException e) {
+ Console.WriteLine (e.Message);
+ }
+ ((IDisposable) reader).Dispose ();
+ }
+
+ void Failure3 () {
+ using (var reader = XmlReader.Create ("foo.xml")) {
+ Console.WriteLine (reader.ToString ());
+ try {
+ var reader2 = new StreamReader ("bar.xml"); //newobj
+ } finally {
+ ((IDisposable) reader).Dispose (); //reader != reader2 !!!
+ }
+ }
+ }
+
+ void Failure4 () {
+ var reader = XmlReader.Create ("foo.xml");
+ reader.ToString ();
+ Success3 (reader);
+ }
+
+ bool Failure5 () {
+ using (var reader = XmlReader.Create ("foo.xml")) {
+ var reader2 = new StreamReader ("bar.xml"); //newobj
+ var reader3 = new StreamReader ("baz.xml"); //newobj
+ if (reader2.ReadLine () == reader3.ReadLine ())
+ return true;
+ }
+ return false;
+ }
+ }
+
+ [TestFixture]
+ public class EnsureLocalDisposalTest : MethodRuleTestFixture<EnsureLocalDisposalRule> {
+
+ [Test]
+ public void DoesNotApply0 ()
+ {
+ AssertRuleDoesNotApply (SimpleMethods.EmptyMethod);
+ }
+
+ [Test]
+ public void DoesNotApply1 ()
+ {
+ AssertRuleDoesNotApply<DisposalCases> ("DoesNotApply1");
+ }
+
+ [Test]
+ public void DoesNotApply2 ()
+ {
+ AssertRuleDoesNotApply<DisposalCases> ("DoesNotApply2");
+ }
+
+ [Test]
+ public void DoesNotApply3 ()
+ {
+ AssertRuleDoesNotApply<DisposalCases> ("DoesNotApply3");
+ }
+
+ [Test]
+ public void Success0 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success0");
+ }
+
+ [Test]
+ public void Success1 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success1");
+ }
+
+ [Test]
+ public void Success2 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success2");
+ }
+
+ [Test]
+ public void Success3 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success3");
+ }
+
+ [Test]
+ public void Success4 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success4");
+ }
+
+ [Test]
+ public void Success5 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success5");
+ }
+
+ [Test]
+ public void Success6 ()
+ {
+ AssertRuleSuccess<DisposalCases> ("Success6");
+ }
+
+ [Test]
+ public void Failure0 ()
+ {
+ AssertRuleFailure<DisposalCases> ("Failure0", 1);
+ }
+
+ [Test]
+ public void Failure1 ()
+ {
+ AssertRuleFailure<DisposalCases> ("Failure1", 1);
+ }
+
+ [Test]
+ public void Failure2 ()
+ {
+ AssertRuleFailure<DisposalCases> ("Failure2", 1);
+ }
+
+ [Test]
+ public void Failure3 ()
+ {
+ AssertRuleFailure<DisposalCases> ("Failure3", 2);
+ }
+
+ [Test]
+ public void Failure4 ()
+ {
+ AssertRuleFailure<DisposalCases> ("Failure4", 1);
+ }
+
+ [Test]
+ public void Failure5 ()
+ {
+ AssertRuleFailure<DisposalCases> ("Failure5", 2);
+ }
+ }
+}
--
1.6.0.3
diff --git a/gendarme/swf-wizard-runner/Wizard.cs b/gendarme/swf-wizard-runner/Wizard.cs
index 445ae08..72e15e8 100644
--- a/gendarme/swf-wizard-runner/Wizard.cs
+++ b/gendarme/swf-wizard-runner/Wizard.cs
@@ -653,11 +653,8 @@ namespace Gendarme {
if (save_file_dialog.ShowDialog () != DialogResult.OK)
return;
- ResultWriter writer = GetSelectedWriter (save_file_dialog.FilterIndex, save_file_dialog.FileName);
- if (writer != null) {
+ using (var writer = GetSelectedWriter (save_file_dialog.FilterIndex, save_file_dialog.FileName))
writer.Report ();
- writer.Dispose ();
- }
}
private void ViewReportButtonClick (object sender, EventArgs e)