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); } } }
public RuleResult CheckMethod(MethodDefinition method) { // Rule does not apply if the method has no IL if (!method.HasBody) { return(RuleResult.DoesNotApply); } // // We need to check all methods which has any of the following opcodes: call, stfld or stsfld. // OpCodeBitmask bitmask = OpCodeEngine.GetBitmask(method); if (!applicable_method_bitmask.Intersect(bitmask)) { return(RuleResult.DoesNotApply); } /* Unfortunately it's possible to generate IL this rule will choke on, especially when * using non-standard compilers or obfuscators. */ try { return(CheckMethodUnsafe(method)); } catch (Exception ex) { // FIXME: This problem should be reported some other way, it's not really a failure. // Pending implementation of "analysis warnings", as mentioned here (post #21): // http://groups.google.com/group/gendarme/browse_frm/thread/c37d157ae0c9682/57f89f3abf14f2fd?tvc=1&q=Gendarme+2.6+Preview+1+is+ready+for+download#57f89f3abf14f2fd Runner.Report(method, Severity.Low, Confidence.Low, String.Format(CultureInfo.CurrentCulture, "An exception occurred while verifying this method. " + "This failure can probably be ignored, it's most likely due to an " + "uncommon code sequence in the method the rule didn't understand. {0}", ex.Message)); return(RuleResult.Failure); } }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } if (!Throwers.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } if (!HasCatchBlock(method.Body)) { // default severity for (most) methods severity = Severity.High; // by default no exceptions are allowed allowedExceptions = null; // special case for Equals is_equals = false; string method_label = PreflightMethod(method); if (method_label.Length > 0) { Log.WriteLine(this); Log.WriteLine(this, "-------------------------------------------"); Log.WriteLine(this, method); ProcessMethod(method, method_label); } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { // rule only applies to methods with IL and exceptions handlers if (!method.HasBody || !method.Body.HasExceptionHandlers) { return(RuleResult.DoesNotApply); } // and when the IL contains a Throw instruction (Rethrow is fine) OpCodeBitmask mask = OpCodeEngine.GetBitmask(method); if (!mask.Get(Code.Throw)) { return(RuleResult.DoesNotApply); } // we can use a faster code path when no branches are present in the method if (mask.Intersect(branches)) { Branches(method); } else { Branchless(method); } warned_offsets_in_method.Clear(); return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } if (!Remainder.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { switch (ins.OpCode.Code) { case Code.Rem: case Code.Rem_Un: if (CheckModuloOne(ins.Previous)) { Runner.Report(method, ins, Severity.High, Confidence.Total); } break; } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } //is there any interesting opcode in the method? OpCodeBitmask calls = OpCodeBitmask.Calls; if (!calls.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { if (!calls.Get(ins.OpCode.Code)) { continue; } CheckCall(method, ins, (MethodReference)ins.Operand); } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } if (!Remainder.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { Severity severity; // look for a remainder operation switch (ins.OpCode.Code) { case Code.Rem: // this won't work when negative numbers are used severity = Severity.High; break; case Code.Rem_Un: // this will work since it can't be a negative number // but it's a coding bad (practice) example severity = Severity.Low; break; default: continue; } // x % 2 if (!IsLoadConstant(ins.Previous, 2)) { continue; } // compared to 1 if (!IsLoadConstant(ins.Next, 1)) { continue; } // using equality Instruction cmp = ins.Next.Next; if (Conversion.Get(cmp.OpCode.Code)) { cmp = cmp.Next; } if (cmp.OpCode.Code != Code.Ceq) { continue; } Runner.Report(method, ins, severity, Confidence.Normal); } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } // exclude methods that don't have any conditional branches if (!Branches.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { switch (ins.OpCode.Code) { case Code.Brfalse: case Code.Brfalse_S: case Code.Brtrue: case Code.Brtrue_S: // BNE is used by [G]MCS case Code.Bne_Un: case Code.Bne_Un_S: case Code.Beq: case Code.Beq_S: Instruction br = (ins.Operand as Instruction); int delta = br.Offset - ins.Next.Offset; if (delta == 0) { // Medium: since compiler already warned about this Runner.Report(method, ins, Severity.Medium, Confidence.Normal); } else if (delta <= 2) { // is the block (between the jumps) small and empty ? // CSC does this, probably to help the debugger. // [G]MCS does not while (delta > 0) { br = br.Previous; if (br.OpCode.Code != Code.Nop) { break; } delta--; } if (delta == 0) { Runner.Report(method, ins, Severity.Low, Confidence.Normal); } } break; } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { // rule apply only if the method has a body (e.g. p/invokes, icalls don't) // and was not generated by the compiler or a tool (outside of developer's control) if (!method.HasBody || method.IsGeneratedCode()) { return(RuleResult.DoesNotApply); } // is there any IsInst or Castclass instructions in the method ? if (!Casts.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } // Console.WriteLine (); // Console.WriteLine ("-----------------------------------------"); // Console.WriteLine (new MethodPrinter(method)); foreach (Instruction ins in method.Body.Instructions) { Code code = ins.OpCode.Code; // IsInst -> if (t is T) ... // -> t = t as T; ... // Castclass -> t = (T) t; ... if ((code == Code.Isinst) || (code == Code.Castclass)) { casts.Add(ins); } } // if there's only one then it can't be a duplicate cast while (casts.Count > 1) { Instruction ins = casts [0]; TypeReference type = (ins.Operand as TypeReference); Instruction origin = GetOrigin(ins); int count = FindDuplicates(method, type, origin); if (count > 1) { // Console.WriteLine ("found {0} duplicates for {1:X4}", count, ins.Offset); // rare, but it's possible to cast a null value (ldnull) object name = origin.GetOperand(method) ?? "Null"; string msg = String.Format(CultureInfo.InvariantCulture, "'{0}' is casted {1} times for type '{2}'.", name, count, type.GetFullName()); Runner.Report(method, ins, Severity.Medium, Confidence.Normal, msg); } casts.RemoveAt(0); } casts.Clear(); return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } // is there any Unbox or Unbox_Any instructions in the method ? if (!Unbox.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { switch (ins.OpCode.Code) { case Code.Unbox: case Code.Unbox_Any: string previous = Previous(method, ins); if (previous.Length == 0) { continue; } int num; if (!unboxed.TryGetValue(previous, out num)) { unboxed.Add(previous, 1); } else { unboxed [previous] = ++num; } break; } } // report findings (one defect per variable/parameter/field) foreach (KeyValuePair <string, int> kvp in unboxed) { // we can't (always) avoid unboxing one time if (kvp.Value < 2) { continue; } string s = String.Format(CultureInfo.InvariantCulture, kvp.Key, kvp.Value); Runner.Report(method, GetSeverityFromCount(kvp.Value), Confidence.Normal, s); } unboxed.Clear(); return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { // rule applies only if the method has a body if (!method.HasBody) { return(RuleResult.DoesNotApply); } // avoid looping if we're sure there's no Call[virt] or NewObj in the method if (!CallsNew.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } IList <Instruction> instructions = method.Body.Instructions; for (int i = 0; i < instructions.Count; i++) { Instruction ins = instructions [i]; switch (ins.OpCode.FlowControl) { case FlowControl.Call: MethodReference callee = (ins.Operand as MethodReference); // check type name only if the call isn't virtual bool virtual_call = (ins.OpCode.Code == Code.Callvirt); // continue scanning unless we're calling ourself if (!CompareMethods(method, callee, virtual_call)) { break; } // recursion detected! check if there a way out of it if (CheckForEndlessRecursion(method, i)) { Runner.Report(method, ins, Severity.Critical, Confidence.High); return(RuleResult.Failure); } break; case FlowControl.Cond_Branch: case FlowControl.Return: case FlowControl.Throw: // if there's a way to break free before a recursive call then we let it go return(RuleResult.Success); } } // should never be reached (since there's always a RET instruction) return(RuleResult.Success); }
public RuleResult CheckMethod(MethodDefinition method) { // rule applies only if the method has a body if (!method.HasBody) { return(RuleResult.DoesNotApply); } // is there any bge*, bgt*, ble* or blt* instructions in the method ? if (!GreaterOrLesserThan.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { // check for specific cases like: '<', '>', '<=', '>=' if (!GreaterOrLesserThan.Get(ins.OpCode.Code)) { continue; } // find the two values on stack Instruction current = ins.Previous; string op1 = GetPrevious(method, ref current); if (op1.Length == 0) { continue; } current = current.Previous; string op2 = GetPrevious(method, ref current); if (op2.Length == 0) { continue; } // check value used immediately on both sides on the branch string next = GetNext(method, ins.Next); string branch = GetNext(method, ins.Operand as Instruction); // if value before and after the branch match then we have a candidate if (((op1 == next) && (op2 == branch)) || ((op2 == next) && (op1 == branch))) { Runner.Report(method, ins, Severity.Medium, Confidence.Normal); } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } // exclude methods that don't have calls OpCodeBitmask mask = OpCodeEngine.GetBitmask(method); if (!OpCodeBitmask.Calls.Intersect(mask)) { return(RuleResult.DoesNotApply); } // *and* methods that don't convert into an [u]int64 if (!Convert8.Intersect(mask)) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { if (ins.OpCode.FlowControl != FlowControl.Call) { continue; } if (!IsInt64BitsToDouble(ins.Operand as MethodReference)) { continue; } // if the previous call convert a value into a long (int64) // then it's likely that the API is being misused switch (ins.Previous.OpCode.Code) { case Code.Conv_I8: case Code.Conv_U8: case Code.Conv_Ovf_I8: case Code.Conv_Ovf_I8_Un: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_U8_Un: Runner.Report(method, ins, Severity.High, Confidence.High); break; } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { // rule doesn't not apply to methods without code (e.g. p/invokes) if (!method.HasBody) { return(RuleResult.DoesNotApply); } // is there any Call or Callvirt instructions in the method OpCodeBitmask calls = OpCodeBitmask.Calls; if (!calls.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } // go! // we look for a call to String.Length property (since it's much easier // than checking a string being compared to null) foreach (Instruction current in method.Body.Instructions) { if (!calls.Get(current.OpCode.Code)) { continue; } MethodReference mr = (current.Operand as MethodReference); if (mr.IsNamed("System", "String", "get_Length")) { // now that we found it we check that // 1 - we previously did a check null on the same value (that we already know is a string) Instruction branch = PreLengthCheck(method, current.Previous); if (branch == null) { continue; } // 2 - we compare the return value (length) with 0 if (PostLengthCheck(current.Next, branch)) { Runner.Report(method, current, Severity.Medium, Confidence.High); } } } return(Runner.CurrentRuleResult); }
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 CheckMethod(MethodDefinition method) { if (!IsApplicable(method)) { return(RuleResult.DoesNotApply); } // extra check - rule applies only if the method contains Ldc_R4 or Ldc_R8 if (!Ldc_R.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } IList <Instruction> il = method.Body.Instructions; for (int i = 0; i < il.Count; i++) { Instruction ins = il [i]; switch (ins.OpCode.Code) { // handle == and != case Code.Ceq: if (!CheckPrevious(il, i - 1)) { Runner.Report(method, ins, Severity.Critical, Confidence.Total, EqualityMessage); } break; // handle calls to [Single|Double].Equals case Code.Call: case Code.Callvirt: MemberReference callee = ins.Operand as MemberReference; if ((callee != null) && (callee.Name == "Equals") && callee.DeclaringType.IsFloatingPoint()) { if (!CheckPrevious(il, i - 1)) { Runner.Report(method, ins, Severity.Critical, Confidence.Total, EqualsMessage); } } break; } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } // check if the method creates array (Newarr) OpCodeBitmask bitmask = OpCodeEngine.GetBitmask(method); if (!bitmask.Get(Code.Newarr)) { return(RuleResult.DoesNotApply); } // check if the method loads the 0 constant if (!bitmask.Intersect(zero)) { return(RuleResult.DoesNotApply); } // look for array of Type creation foreach (Instruction ins in method.Body.Instructions) { if (ins.OpCode != OpCodes.Newarr) { continue; } if (!(ins.Operand as TypeReference).IsNamed("System", "Type")) { continue; } if (ins.Previous.IsOperandZero()) { Runner.Report(method, ins, Severity.Medium, Confidence.High); } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody || method.IsGeneratedCode()) { return(RuleResult.DoesNotApply); } // is there any Call or Callvirt instructions in the method OpCodeBitmask calls = OpCodeBitmask.Calls; if (!calls.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { MethodReference calledMethod = ins.GetMethod(); if (calledMethod == null) { continue; } if (!calledMethod.DeclaringType.IsNamed("System", "DateTime")) { continue; } if (!MethodSignatures.op_Subtraction.Matches(calledMethod)) { continue; } if (CheckUsage(method, ins)) { Runner.Report(method, ins, Severity.Low, Confidence.High); } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { // rule does not apply to methods without IL if (!method.HasBody) { return(RuleResult.DoesNotApply); } // avoid looping if we're sure there's no call in the method if (!CallsNew.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { // check for calls (or newobj) if (ins.OpCode.FlowControl != FlowControl.Call) { continue; } // Check for usage of Process or Ping based on their presence if (process_present && CheckProcessSetPriorityClass(ins)) { // code won't work with default (non-root) users == High Runner.Report(method, ins, Severity.High, Confidence.High, ProcessMessage); } else if (ping_present && CheckPing(ins)) { // code won't work with default (non-root) users == High Runner.Report(method, ins, Severity.High, Confidence.High, PingMessage); } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { // rule doesn't apply if method has no IL if (!method.HasBody) { return(RuleResult.DoesNotApply); } // check if any instruction refers to methods or types that MoMA could track if (!mask.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } // rule applies foreach (Instruction ins in method.Body.Instructions) { // look for any instruction that could use something incomplete if (!mask.Get(ins.OpCode.Code)) { continue; } // filter calls to assemblies that MoMA likely does not include (e.g. your own code) MethodReference mr = (ins.Operand as MethodReference); if ((mr == null) || !Filter(mr.DeclaringType.Scope as AssemblyNameReference)) { continue; } // MethodReference.ToString is costly so we do it once for the three checks string callee = mr.ToString(); // calling not implemented method is very likely not to work == High if ((NotImplemented != null) && NotImplementedInternal.Contains(callee)) { string message = String.Format(NotImplementedMessage, callee); // confidence is Normal since we can't be sure if MoMA data is up to date Runner.Report(method, ins, Severity.High, Confidence.Normal, message); } // calling missing methods can't work == Critical if ((Missing != null) && Missing.Contains(callee)) { string message = String.Format(MissingMessage, callee); Runner.Report(method, ins, Severity.Critical, Confidence.Normal, message); } // calling todo methods migh work with some limitations == Medium if (ToDo != null) { string value; if (ToDo.TryGetValue(callee, out value)) { string message = String.Format(TodoMessage, callee, value); Runner.Report(method, ins, Severity.Medium, Confidence.Normal, message); } } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } //is there any potential IDisposable-getting opcode in the method? if (!callsAndNewobjBitmask.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } // we will not report IDiposable locals that are returned from a method bool return_idisposable = DoesReturnDisposable(method); locals.ClearAll(); foreach (Instruction ins in method.Body.Instructions) { Code code = ins.OpCode.Code; switch (code) { case Code.Ret: if (return_idisposable) { CheckForReturn(method, ins.Previous); } continue; case Code.Stind_Ref: CheckForOutParameters(method, ins); continue; default: if (!callsAndNewobjBitmask.Get(code)) { continue; } break; } MethodReference call = (MethodReference)ins.Operand; if (IsDispose(call)) { CheckDisposeCalls(method, ins); continue; } if (call.HasThis && (code != Code.Newobj)) { if (!CheckCallsToOtherInstances(method, ins, call)) { continue; } } if (!DoesReturnDisposable(call)) { continue; } Instruction nextInstruction = ins.Next; if (nextInstruction == null) { continue; } Code nextCode = nextInstruction.OpCode.Code; if (nextCode == Code.Pop || OpCodeBitmask.Calls.Get(nextCode)) { // We ignore setter because it is an obvious share of the IDisposable if (!IsSetter(nextInstruction.Operand as MethodReference)) { ReportCall(method, ins, call); } } else if (nextInstruction.IsStoreLocal()) { // make sure we're not re-assigning over a non-disposed IDisposable CheckReassignment(method, nextInstruction); } } ReportNonDisposedLocals(method); return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } // no chain are possible without Call[virt] instructions within the method OpCodeBitmask calls = OpCodeBitmask.Calls; if (!calls.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } Log.WriteLine(this); Log.WriteLine(this, "-------------------------------------"); Log.WriteLine(this, method); // walk back so we don't process very long chains multiple times // (we don't need to go down to zero since it would not be big enough for a chain to exists) IList <Instruction> ic = method.Body.Instructions; for (int i = ic.Count - 1; i >= MaxChainLength; i--) { Instruction ins = ic [i]; // continue until we find a Call[virt] instruction if (!calls.Get(ins.OpCode.Code)) { continue; } // operators "break" chains MethodReference mr = (ins.Operand as MethodReference); if (mr.Name.StartsWith("op_", StringComparison.Ordinal)) { continue; } int counter = 1; // trace back every call (including new objects and arrays) and // check if the caller is a call (i.e. not a local) Instruction caller = ins.TraceBack(method); while ((caller != null) && ValidLink(caller)) { counter++; i = ic.IndexOf(caller); caller = caller.TraceBack(method); } Log.WriteLine(this, "chain of length {0} at {1:X4}", counter, ins.Offset); if (counter > MaxChainLength) { string msg = String.Format(CultureInfo.CurrentCulture, "Chain length {0} versus maximum of {1}.", counter, MaxChainLength); Runner.Report(method, ins, Severity.Medium, Confidence.Normal, msg); } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody || method.IsGeneratedCode()) { return(RuleResult.DoesNotApply); } // exclude methods that don't store fields or arguments if (!mask.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } foreach (Instruction ins in method.Body.Instructions) { Instruction next = ins.Next; if (next == null) { continue; } if (next.OpCode.Code == Code.Stfld) { // is it the same (instance) field ? CheckFields(ins, next, method, false); } else if (next.OpCode.Code == Code.Stsfld) { // is it the same (static) field ? CheckFields(ins, next, method, true); // too much false positive because compilers add their own variables // and don't always "play well" with them #if false } else if (ins.IsLoadLocal() && next.IsStoreLocal()) { VariableDefinition variable = next.GetVariable(method); if (variable == ins.GetVariable(method)) { // the compiler often introduce it's own variable if (!variable.Name.StartsWith("V_")) { msg = String.Format("Variable '{0}' of type '{1}'.", variable.Name, variable.VariableType.GetFullName()); } } #endif } else if (ins.IsLoadArgument() && next.IsStoreArgument()) { ParameterDefinition parameter = next.GetParameter(method); if (parameter == ins.GetParameter(method)) { string msg = String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' of type '{1}'.", parameter.Name, parameter.ParameterType.GetFullName()); Runner.Report(method, ins, Severity.Medium, Confidence.Normal, msg); } } } return(Runner.CurrentRuleResult); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } OpCodeBitmask calls = OpCodeBitmask.Calls; if (!calls.Intersect(OpCodeEngine.GetBitmask(method))) { return(RuleResult.DoesNotApply); } Log.WriteLine(this, "--------------------------------------"); Log.WriteLine(this, method); // Loop through each instruction, foreach (Instruction ins in method.Body.Instructions) { // if we're calling a method, if (!calls.Get(ins.OpCode.Code)) { continue; } // and the method is a System.Linq.Enumerable method then, var target = ins.Operand as MethodReference; if (!target.DeclaringType.IsNamed("System.Linq", "Enumerable")) { continue; } string tname = target.Name; int tcount = target.HasParameters ? target.Parameters.Count : 0; // see if we can use a more efficient method. if (tname == "Count" && tcount == 1) { TypeReference tr = ins.Previous.GetOperandType(method); if (tr != null) { CheckForCountProperty(tr, method, ins); CheckForAny(method, ins); } } else if ((tname == "ElementAt" || tname == "ElementAtOrDefault") && tcount == 2) { Instruction arg = ins.TraceBack(method); TypeReference tr = arg.GetOperandType(method); if (tr != null) { CheckForSubscript(tr, method, ins, tname); } } else if ((tname == "Last" || tname == "LastOrDefault") && tcount == 1) { TypeReference tr = ins.Previous.GetOperandType(method); if (tr != null) { CheckForSubscript(tr, method, ins, tname); } } else if (tname == "OrderBy" || tname == "OrderByDescending") { Instruction arg = ins.TraceBack(method); TypeReference tr = arg.GetOperandType(method); if (tr != null) { CheckForSort(tr, method, ins, tname); } } } return(Runner.CurrentRuleResult); }