protected override void CheckMethod(MethodDefinition method, bool abstractWarning) { if ((method == null) || !method.HasBody) { return; } OpCodeBitmask bitmask = OpCodeEngine.GetBitmask(method); // method must have a CALL[VIRT] and either STFLD or STELEM_REF if (!bitmask.Intersect(OpCodeBitmask.Calls) || !bitmask.Intersect(StoreFieldBitmask)) { return; } foreach (Instruction ins in method.Body.Instructions) { MethodReference mr = (ins.Operand as MethodReference); if (mr == null || mr.DeclaringType.IsNative()) { continue; } FieldDefinition field = null; Instruction next = ins.Next; if (next.Is(Code.Stfld)) { field = next.Operand as FieldDefinition; } else if (next.Is(Code.Stobj) || next.Is(Code.Stind_I)) { Instruction origin = next.TraceBack(method); if (origin.Is(Code.Ldelema)) { origin = origin.TraceBack(method); if (origin != null) { field = origin.Operand as FieldDefinition; } } } if (field != null && FieldCandidates.Contains(field)) { Runner.Report(field, Severity.High, Confidence.High, abstractWarning ? AbstractTypeMessage : TypeMessage); } } }
protected override void CheckMethod(MethodDefinition method, bool abstractWarning) { if ((method == null) || !method.HasBody) { return; } OpCodeBitmask bitmask = OpCodeEngine.GetBitmask(method); // method must have a NEWOBJ and either STFLD or STELEM_REF if (!bitmask.Get(Code.Newobj) || !bitmask.Intersect(StoreFieldBitmask)) { return; } foreach (Instruction ins in method.Body.Instructions) { if (!ins.Is(Code.Newobj)) { continue; } FieldDefinition field = null; Instruction next = ins.Next; if (next.Is(Code.Stfld)) { field = next.Operand as FieldDefinition; } else if (next.Is(Code.Stelem_Ref)) { Instruction origin = next.TraceBack(method); if (origin != null) { field = origin.Operand as FieldDefinition; } } if (field != null && FieldCandidates.Contains(field)) { Runner.Report(field, Severity.High, Confidence.High, abstractWarning ? AbstractTypeMessage : TypeMessage); } } }
public RuleResult CheckType(TypeDefinition type) { // that will cover interfaces, delegates too if (!type.HasFields) { return(RuleResult.DoesNotApply); } // rule doesn't apply to enums, interfaces, structs, delegates or generated code if (type.IsEnum || type.IsValueType || type.IsGeneratedCode()) { return(RuleResult.DoesNotApply); } MethodDefinition explicitDisposeMethod = null; MethodDefinition implicitDisposeMethod = null; bool abstractWarning = false; if (type.Implements("System", "IDisposable")) { implicitDisposeMethod = type.GetMethod(MethodSignatures.Dispose); explicitDisposeMethod = type.GetMethod(MethodSignatures.DisposeExplicit); if (IsAbstract(implicitDisposeMethod) || IsAbstract(explicitDisposeMethod)) { abstractWarning = true; } else { return(RuleResult.Success); } } FieldCandidates.Clear(); foreach (FieldDefinition field in type.Fields) { // we can't dispose static fields in IDisposable if (field.IsStatic) { continue; } TypeDefinition fieldType = field.FieldType.GetElementType().Resolve(); if (fieldType == null) { continue; } if (FieldTypeIsCandidate(fieldType)) { FieldCandidates.Add(field); } } // if there are fields types that implements IDisposable if (type.HasMethods && (FieldCandidates.Count > 0)) { // check if we're assigning new object to them foreach (MethodDefinition method in type.Methods) { CheckMethod(method, abstractWarning); } } // Warn about possible confusion if the Dispose methods are abstract if (IsAbstract(implicitDisposeMethod)) { Runner.Report(implicitDisposeMethod, Severity.Medium, Confidence.High, AbstractDisposeMessage); } return(Runner.CurrentRuleResult); }