public RuleResult CheckMethod (MethodDefinition method) { // ingore methods without body and generated code if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; int num = method.Body.MaxStackSize; if (num <= MaximumStackSize) return RuleResult.Success; string msg = String.Format ("Found {0} maximum stack size (maximum {1}).", num, MaximumStackSize); Runner.Report (method, Severity.High, Confidence.High, msg); return RuleResult.Failure; }
public RuleResult CheckMethod (MethodDefinition method) { // rule does not apply to properties, events, without parameters or for generated code if (method.IsSpecialName || !method.HasParameters || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; foreach (ParameterDefinition parameter in method.Parameters) { if (parameter.ParameterType.FullName != "System.Object&") continue; // suggest using generics Runner.Report (parameter, Severity.Medium, Confidence.High); } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // rule only applies if the method has a body // rule doesn't not apply to generated code (out of developer's control) if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // check if the method contains a Pop instruction if (!OpCodeEngine.GetBitmask (method).Get (Code.Pop)) return RuleResult.DoesNotApply; foreach (Instruction instruction in method.Body.Instructions) { if (instruction.OpCode.Code == Code.Pop) { CheckForViolation (method, instruction.Previous); } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // rule does not apply to properties, events, without parameters or for generated code if (method.IsSpecialName || !method.HasParameters || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // exclude the "bool Try* (ref)" pattern from the rule if (method.Name.StartsWith ("Try", StringComparison.Ordinal) && method.ReturnType.IsNamed ("System", "Boolean")) return RuleResult.DoesNotApply; foreach (ParameterDefinition parameter in method.Parameters) { if (!parameter.ParameterType.IsNamed ("System", "Object&")) continue; // suggest using generics Runner.Report (parameter, Severity.Medium, Confidence.High); } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // ingore methods without body and generated code if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // special case for System.Windows.Forms since it's designer does not // mark this code as generated :-( if (method.Name == "InitializeComponent") { if (method.DeclaringType.Inherits ("System.Windows.Forms.Form")) return RuleResult.DoesNotApply; } int num = method.Body.Variables.Count; if (num <= MaximumVariables) return RuleResult.Success; string msg = String.Format ("Found {0} local variables (maximum {1}).", num, MaximumVariables); Runner.Report (method, Severity.High, Confidence.High, msg); return RuleResult.Failure; }
public RuleResult CheckMethod (MethodDefinition method) { // rule does not apply if there's no IL code // or if it was generated by the compiler or tools if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // is there a Newobj *and* a Throw instruction in this method if (!bitmask.IsSubsetOf (OpCodeEngine.GetBitmask (method))) return RuleResult.DoesNotApply; int n = 0; bool cond_branch = false; foreach (Instruction inst in method.Body.Instructions) { // check if the code is linear or with branches if (FlowControl.Cond_Branch == inst.OpCode.FlowControl) cond_branch = true; // if there are branch and it's long enough then assume it is implemented if (cond_branch && (++n > 10)) break; // check for "throw new NotImplementedException (...)" if (inst.OpCode.Code != Code.Newobj) continue; MethodReference ctor = (MethodReference) inst.Operand; if ("System.NotImplementedException" != ctor.DeclaringType.FullName) continue; if (inst.Next.OpCode.Code != Code.Throw) continue; // the defect is more severe if the method is visible outside it's assembly Severity severity = method.IsPublic ? Severity.High : Severity.Medium; Runner.Report (method, severity, Confidence.Normal); return RuleResult.Failure; } return RuleResult.Success; }
public RuleResult CheckMethod (MethodDefinition method) { // ignore constructors (.ctor or .cctor) and compiler/tool-generated code if (method.IsConstructor || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // don't consider private add / remove on events if ((method.IsAddOn || method.IsRemoveOn) && method.IsPrivate) return RuleResult.DoesNotApply; string name = method.Name; MethodSemanticsAttributes attrs = method.SemanticsAttributes; if ((attrs & mask) != 0) { // it's something special int underscore = name.IndexOf ('_'); if (underscore != -1) name = name.Substring (underscore + 1); } else if (method.IsSpecialName) { return RuleResult.Success; } // like types, methods/props should all be PascalCased, too if (!IsPascalCase (name)) { string errorMessage = String.Format ("By existing naming conventions, all the method and property names should all be pascal-cased (e.g. MyOperation). Rename '{0}' to '{1}'.", name, PascalCase (name)); Runner.Report (method, Severity.Medium, Confidence.High, errorMessage); } // check parameters if (method.HasParameters) { foreach (ParameterDefinition param in method.Parameters) { // params should all be camelCased if (!IsCamelCase (param.Name)) { string errorMessage = String.Format ("By existing naming conventions, the parameter names should all be camel-cased (e.g. myParameter). Rename '{0}' parameter to '{1}'.", param, CamelCase (param.Name)); Runner.Report (method, Severity.Medium, Confidence.High, errorMessage); } } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // rule does not apply to external methods (e.g. p/invokes) // and generated code (by compilers or tools) if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; var variables = method.Body.Variables; int count = variables.Count; if (count == 0) return RuleResult.Success; if (used == null) { used = new BitArray (Math.Max (DefaultLength, count)); } else if (count > used.Length) { used = new BitArray (count); } used.SetAll (false); foreach (Instruction ins in method.Body.Instructions) { VariableDefinition vd = ins.GetVariable (method); if (vd != null) used [vd.Index] = true; } for (int i = 0; i < count; i++) { if (!used [i]) { // sometimes the compilers generates some locals without really // using them (e.g. assign only a constant). In this case we need // to determine if the variable is "genuine" or a compiler // (*) seen in a while (true) loop over a switch VariableDefinition variable = variables [i]; string var_name = variable.Name; if (var_name.StartsWith ("V_") || var_name.Contains ("$")) continue; string s = String.Format ("Variable '{0}' of type '{1}'", var_name, variable.VariableType.FullName); Runner.Report (method, Severity.Low, Confidence.Normal, s); } } return Runner.CurrentRuleResult; }
// looking for 'protected override void Dispose (bool disposing)' with generated code bool IsGeneratedDispose(MethodDefinition method) { if (!method.IsFamily || !method.IsVirtual || method.IsNewSlot || !method.HasParameters || !method.HasBody) return false; return ((method.Name == "Dispose") && method.IsGeneratedCode ()); }
public RuleResult CheckMethod (MethodDefinition method) { // exclude constrcutors, non-visible methods and generated code if (method.IsConstructor || !method.IsVisible () || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // the rule does not apply if the code is an interface to COM objects if (UsedForComInterop (method.DeclaringType as TypeDefinition)) return RuleResult.DoesNotApply; // check the method name if (!CheckName (method.Name, method.IsSpecialName)) Runner.Report (method, Severity.Medium, Confidence.High); if (method.HasParameters) { foreach (ParameterDefinition parameter in method.Parameters) { if (!CheckName (parameter.Name, false)) Runner.Report (parameter, Severity.Medium, Confidence.High); } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // rule does not apply if // - the method has no body (e.g. p/invokes, icalls don't) // - the method is static // - the method was generated by the compiler or a tool if (!method.HasBody || method.IsStatic || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // avoid looping if we're sure there's no call in the method if (!OpCodeEngine.GetBitmask (method).Get (Code.Stsfld)) return RuleResult.DoesNotApply; // *** ok, the rule applies! *** foreach (Instruction ins in method.Body.Instructions) { // look for stsfld instructions if (ins.OpCode.Code == Code.Stsfld) { FieldReference fr = (ins.Operand as FieldReference); if (CheckField (fr)) { string text = String.Format ("The static field '{0}', of type '{1}'. is being set in an instance method.", fr.Name, fr.FieldType); Runner.Report (method, ins, Severity.Medium, Confidence.High, text); } } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { if (!method.HasBody) return RuleResult.DoesNotApply; // Don't want to count the code generated by yield statements. if (method.IsGeneratedCode ()) return RuleResult.DoesNotApply; if (Preflight (method)) { Log.WriteLine (this); Log.WriteLine (this, "-----------------------------------------"); Log.WriteLine (this, method); call_using_this = false; field_access_using_this = false; creates_exception = false; has_dispose_check = false; CheckBody (method); if ((call_using_this || field_access_using_this) && !creates_exception) { if (!has_dispose_check) { Runner.Report (method, Severity.Medium, Confidence.High); } } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // catch abstract, pinvoke and icalls - where rule does not apply if (!method.HasBody) return RuleResult.DoesNotApply; // skip methods without parameters if (!method.HasParameters) return RuleResult.DoesNotApply; // rule doesn't apply to virtual, overrides or generated code // doesn't apply to code referenced by delegates (note: more complex check moved last) if (method.IsVirtual || method.HasOverrides || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // Also EventArgs parameters are often required in method signatures, // but often not required. Reduce "false positives"(*) for GUI apps // (*) it's more a "don't report things outside developer's control" if (method.IsEventCallback () || IsReferencedByDelegate (method)) return RuleResult.DoesNotApply; // methods with [Conditional] can be empty (not using any parameter) IL-wise but not source-wise, ignore them if (method.HasCustomAttributes) { if (method.CustomAttributes.ContainsType ("System.Diagnostics.ConditionalAttribute")) return RuleResult.DoesNotApply; } // rule applies // we limit ourselves to the first 64 parameters (so we can use a bitmask) IList<ParameterDefinition> pdc = method.Parameters; int pcount = pdc.Count; if (pcount > 64) pcount = 64; ulong mask = 0; // scan IL to see which parameter is being used foreach (Instruction ins in method.Body.Instructions) { ParameterDefinition parameter = ins.GetParameter (method); if (parameter == null) continue; mask |= ((ulong)1 << (parameter.GetSequence () - 1)); } // quick out based on value - i.e. every parameter is being used int shift = 64 - pcount; if ((mask << shift) == (UInt64.MaxValue << shift)) return RuleResult.Success; for (int i = 0; i < pcount; i++) { if ((mask & ((ulong) 1 << i)) == 0) { ParameterDefinition parameter = pdc [i]; string text = String.Format ("Parameter '{0}' of type '{1}' is never used in the method.", parameter.Name, parameter.ParameterType); Runner.Report (parameter, Severity.Medium, Confidence.Normal, text); } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // rule applies only if the method has generic type parameters if (!method.HasGenericParameters || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // look if every generic type parameter... foreach (GenericParameter gp in method.GenericParameters) { Severity severity = Severity.Medium; bool found = false; string nspace = gp.Namespace; string name = gp.Name; // ... is being used by the method parameters foreach (ParameterDefinition pd in method.Parameters) { if (IsGenericType (pd.ParameterType, nspace, name)) { found = true; break; } } if (!found) { // it's a defect when used only for the return value - but we reduce its severity if (IsGenericType (method.ReturnType, nspace, name)) severity = Severity.Low; } if (!found) { string msg = String.Format (CultureInfo.InvariantCulture, "Generic parameter '{0}' is not used by the method parameters.", name); Runner.Report (method, severity, Confidence.High, msg); } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // we only check non static, non virtual methods and not constructors if (method.IsStatic || method.IsVirtual || method.IsConstructor) return RuleResult.DoesNotApply; // we only check methods with a body if (!method.HasBody) return RuleResult.DoesNotApply; // we also don't check event callbacks, as they usually bring a lot of false positive, // and that developers are tied to their signature if (method.IsEventCallback ()) return RuleResult.DoesNotApply; // that aren't compiler generated (e.g. anonymous methods) or generated by a tool (e.g. web services) if (method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // methods with [Conditional] can be empty (not using 'this') IL-wise but not source-wise, ignore them if (method.HasCustomAttributes) { if (method.HasAttribute ("System.Diagnostics", "ConditionalAttribute")) return RuleResult.DoesNotApply; } // rule applies // if we find a use of the "this" reference, it's ok // in most (all?) case "this" is used with the ldarg_0 instruction if (OpCodeEngine.GetBitmask (method).Get (Code.Ldarg_0)) return RuleResult.Success; // but it's also valid to have an ldarg with a 0 value operand foreach (Instruction instr in method.Body.Instructions) { if (instr.OpCode.Code == Code.Ldarg) { ParameterDefinition pd = (instr.Operand as ParameterDefinition); if (pd.Index == -1) return RuleResult.Success; } } Runner.Report (method, Severity.Low, Confidence.Total); return RuleResult.Failure; }
public RuleResult CheckMethod (MethodDefinition method) { // rules do not apply to constructors, methods returning an array, properties if (method.IsConstructor || method.IsSpecialName) return RuleResult.DoesNotApply; // we don't apply the rule to overrides since the base method can be // outside the developer's control (and if not this is the *one* that // should be reported) if (method.IsVirtual && !method.IsNewSlot) return RuleResult.DoesNotApply; // ignore methods returning arrays TypeReference return_type = method.ReturnType; if (return_type.IsArray) return RuleResult.DoesNotApply; // rules do not apply to code generated by the compiler (e.g. anonymous methods) // or generated by a tool (e.g. web services) if (method.IsGeneratedCode ()) return RuleResult.DoesNotApply; string name = method.Name; // ignore the some common Get* method names used in the framework foreach (string s in whitelist) { if (name == s) return RuleResult.DoesNotApply; } // rule applies // If it starts with "get" or "is" or "has", has no parameters and returns something bool get = name.StartsWith ("get", StringComparison.OrdinalIgnoreCase); bool isp = name.StartsWith ("is", StringComparison.OrdinalIgnoreCase); bool has = name.StartsWith ("has", StringComparison.OrdinalIgnoreCase); if ((get || isp || has) && (method.Parameters.Count == 0) && (return_type.FullName != Void)) { // if it's a getter then look for a setter (to complete the report) string msg = get ? ReportAssociatedSetter (method) : String.Empty; Runner.Report (method, Severity.Low, Confidence.Normal, msg); return RuleResult.Failure; } return RuleResult.Success; }
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) { if (!calls.Get (ins.OpCode.Code)) continue; MethodReference calledMethod = (MethodReference) ins.Operand; if (calledMethod.DeclaringType.FullName != 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 apply only if the method has a body (e.g. p/invokes, icalls don't) if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // is there any Call or Callvirt instructions in the method OpCodeBitmask bitmask = OpCodeEngine.GetBitmask (method); if (!OpCodeBitmask.Calls.Intersect (bitmask)) return RuleResult.DoesNotApply; foreach (Instruction ins in method.Body.Instructions) { Code code = ins.OpCode.Code; if ((code != Code.Call) && (code != Code.Callvirt)) continue; MethodReference mref = (ins.Operand as MethodReference); // covers Equals(string) method and both == != operators switch (mref.Name) { case "Equals": if (mref.Parameters.Count > 1) continue; TypeReference type = mref.DeclaringType; if (type.Namespace != "System") continue; string name = type.Name; if ((name != "String") && (name != "Object")) continue; break; case "op_Equality": case "op_Inequality": if (!mref.DeclaringType.IsNamed ("System", "String")) continue; break; default: continue; } Instruction prev = ins.Previous; switch (prev.OpCode.Code) { case Code.Ldstr: if ((prev.Operand as string).Length > 0) continue; break; case Code.Ldsfld: FieldReference field = (prev.Operand as FieldReference); if (!field.DeclaringType.IsNamed ("System", "String")) continue; // unlikely to be anything else (at least with released fx) if (field.Name != "Empty") continue; break; default: continue; } Runner.Report (method, ins, Severity.Medium, Confidence.High); } return Runner.CurrentRuleResult; }
private void CheckMethodBody (MethodDefinition method) { var synchronizedEvents = new Dictionary<MethodReference, List<MethodReference>> (); var thisSynchronized = new List<TypeReference> (); foreach (Instruction ins in method.Body.Instructions) { MethodReference candidate = null; switch (ins.OpCode.Code) { case Code.Newobj: if (ins.Previous != null && ins.Previous.OpCode.Code == Code.Ldftn) { MethodReference ctor = (MethodReference) ins.Operand; TypeReference type = ctor.DeclaringType; if (type.IsDelegate ()) { string nspace = type.Namespace; // ldftn entry-point // newobj System.Void System.Threading.XXX::.ctor (System.Object,System.IntPtr) // i.e. creation of a System.Threading delegate if (nspace == "System.Threading") { string name = type.Name; if (name == "ThreadStart" || name == "ParameterizedThreadStart" || name == "WaitCallback" || name == "WaitOrTimerCallback" || name == "TimerCallback") { candidate = (MethodReference) ins.Previous.Operand; } // ldftn entry-point // newobj System.Void System.AsyncCallback::.ctor (System.Object,System.IntPtr) // i.e. creation of a async delegate } else if (nspace == "System") { if (type.Name == "AsyncCallback") { candidate = (MethodReference) ins.Previous.Operand; } // ldftn entry-point // newobj System.Void ThreadedDelegate::.ctor (System.Object,System.IntPtr) // i.e. creation of a delegate which is decorated with a threading attribute } else if (!ThreadRocks.ThreadedNamespace (nspace)) { // Delegates must be able to call the methods they are bound to. MethodDefinition target = ((MethodReference) ins.Previous.Operand).Resolve (); if (target != null) { ThreadModel callerModel = type.ThreadingModel (); if (!target.IsGeneratedCode () || target.IsProperty ()) { ThreadModel targetModel = target.ThreadingModel (); if (!IsValidCall (callerModel, targetModel)) { string mesg = string.Format ("{0} delegate cannot be bound to {1} {2} method.", callerModel, targetModel, target.Name); ++DefectCount; Log.WriteLine (this, "Defect: {0}", mesg); Defect defect = new Defect (this, method, method, ins, Severity.High, Confidence.High, mesg); Runner.Report (defect); } } else if (!callerModel.Is (ThreadModel.MainThread)) { anonymous_entry_points.Add (target); } } } } } break; case Code.Call: case Code.Callvirt: if (!method.IsGeneratedCode () || method.IsProperty ()) CheckForLegalCall (method, ins); // ldftn entry-point // newobj XXX // callvirt System.Void SynchronizedType::add_Name (XXX) // i.e. adding a delegate to an event in a type which uses SynchronizingObject MethodReference call = (MethodReference) ins.Operand; TypeReference call_type = call.DeclaringType; if (ins.Previous.Is (Code.Newobj) && ins.Previous.Previous.Is (Code.Ldftn)) { // A few events are blacklisted because they do not use SynchronizingObject and // are therefore always threaded. if (IsNonSynchronizedSetter (call)) { candidate = (MethodReference) ins.Previous.Previous.Operand; // But most events do use SynchronizingObject and therefore their threading // depends on whether and how SynchronizingObject is initialized. } else if (HasSynchronizingObject (call_type)) { List<MethodReference> methods; if (!synchronizedEvents.TryGetValue (call, out methods)) { methods = new List<MethodReference> (); synchronizedEvents.Add (call, methods); } methods.AddIfNew ((MethodReference) ins.Previous.Previous.Operand); // Misc threaded events. } else if (call_type.FullName == "System.ComponentModel.BackgroundWorker") { if (call.Name == "add_DoWork") { candidate = (MethodReference) ins.Previous.Previous.Operand; } } // callvirt System.Void System.Diagnostics.Process::set_SynchronizingObject (System.ComponentModel.ISynchronizeInvoke) } else if (SetSynchronizingObject.Matches (call)) { if (ins.Previous.OpCode.Code == Code.Ldarg_0) { thisSynchronized.Add (call_type); } } break; } if (candidate != null) { Log.WriteLine (this, "{0} is a thread entry point", candidate); CheckEntryPoint (candidate); } } // For every method added to a threaded event, ThreadModel? method_model = null; foreach (KeyValuePair<MethodReference, List<MethodReference>> entry in synchronizedEvents) { // if the event is synchronized on this then the target must have the same thread // as the current method's type or better and it should not be treated as a entry point. if (thisSynchronized.Contains (entry.Key.DeclaringType)) { if (method_model == null) method_model = method.DeclaringType.ThreadingModel (); foreach (MethodReference mr in entry.Value) { MethodDefinition target = mr.Resolve (); if (target != null) { ThreadModel targetModel = target.ThreadingModel (); if (!IsValidCall (method_model.Value, targetModel)) { string mesg = string.Format ("{0} {1} cannot be bound to {2} {3} method.", method_model, entry.Key, targetModel, target.Name); ReportDefect (method, Severity.High, Confidence.High, mesg); } } } // otherwise the method has to be treated as a thread entry point. } else { foreach (MethodReference mr in entry.Value) { Log.WriteLine (this, "{0} is a thread entry point", mr); CheckEntryPoint (mr); } } } }
public RuleResult CheckMethod (MethodDefinition method) { //does rule apply? if (!method.HasBody || method.IsGeneratedCode () || method.IsCompilerControlled) return RuleResult.DoesNotApply; //yay! rule do apply! // quick optimization: if the number of instructions is lower // than our SuccessThreshold then it cannot be too complex if (method.Body.Instructions.Count < SuccessThreshold) return RuleResult.Success; int cc = GetCyclomaticComplexity (method); if (cc < SuccessThreshold) return RuleResult.Success; //how's severity? Severity sev = GetCyclomaticComplexitySeverity(cc); Runner.Report (method, sev, Confidence.High, String.Format ("Method's cyclomatic complexity : {0}.", cc)); return RuleResult.Failure; }
public RuleResult CheckMethod (MethodDefinition method) { if (!method.HasBody || !method.HasParameters || method.IsCompilerControlled) return RuleResult.DoesNotApply; if (method.IsProperty () || method.IsGeneratedCode () || method.IsEventCallback ()) return RuleResult.DoesNotApply; // we cannot change parameter types if: // - we're overriding a base virtual method; or // - they were specified by an interface if (IsSignatureDictated (method)) return RuleResult.DoesNotApply; int pcount = method.Parameters.Count; if (pcount > types_least.Length) { // that should be quite rare (does not happen for mono 2.0 class libs) types_least = new TypeReference [pcount]; depths_least = new int [pcount]; } CheckParameters (method); CheckParametersSpecializationDelta (method); Array.Clear (types_least, 0, types_least.Length); Array.Clear (depths_least, 0, depths_least.Length); return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // rule does not apply if method as no code (e.g. abstract, p/invoke) // rule does not apply to code outside the developer's control // rule does not apply to autogenerated code from some tools if (!method.HasBody || method.IsGeneratedCode () || IsAutogeneratedByTools (method)) return RuleResult.DoesNotApply; // rule applies! int field_count = 0; if (method.IsConstructor) field_count = GetFieldCount ((method.DeclaringType as TypeDefinition), method.IsStatic); // if we have debugging information available and we're not asked to use IL approximation if (!UseIlApproximation && method.DeclaringType.Module.HasSymbols) { // add a few extra lines to let the constructors initialize the fields int max = MaxSourceLineOfCode + field_count; int sloc = CountSourceLinesOfCode (method); if (sloc <= max) return RuleResult.Success; string message = String.Format ("Logical SLOC: {0}. Maximum : {1}", sloc, max); Runner.Report (method, Severity.High, Confidence.High, message); } else { // success if the instruction count is below the defined threshold // add a few extra lines to let the constructors initialize the fields int max = MaxInstructions + field_count * AssignationRatio; int count = CountInstructions (method); if (count <= max) return RuleResult.Success; string message = String.Format ("Method IL Size: {0}. Maximum Size: {1}", count, max); Runner.Report (method, Severity.High, Confidence.Normal, message); } return RuleResult.Failure; }
public RuleResult CheckMethod (MethodDefinition method) { // Check property getters/setters. In order to prevent the property from // being reported twice, setters are only checked if the property has no getter. PropertyDefinition property = method.IsProperty () ? method.GetPropertyByAccessor () : null; if (property != null) { // however do not exclude automatic properties (getter/setter marked a generated code) if ((method.IsSetter && property.GetMethod != null) || property.IsGeneratedCode ()) return RuleResult.DoesNotApply; if (!IsOkay (property.PropertyType, property.Name)) Runner.Report (property, Severity.Medium, Confidence.Normal); } else { // exclude generated code like webservices if (method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // Check the method's parameters. if (method.HasParameters) CheckParameters (method); // Check the method's return type. if (!IsOkay (method.ReturnType, method.Name)) Runner.Report (method, Severity.Medium, Confidence.Normal); } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { // rule does not apply to method without IL (e.g. p/invoke) or generated code (e.g. compiler or tools) if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // avoid looping if we're sure there's no call in the method if (!OpCodeBitmask.Calls.Intersect (OpCodeEngine.GetBitmask (method))) return RuleResult.DoesNotApply; foreach (Instruction ins in method.Body.Instructions) { if ((ins.OpCode.Code != Code.Callvirt) && (ins.OpCode.Code != Code.Call)) continue; // look for calls to .Invoke MethodReference mr = (ins.Operand as MethodReference); if (!MethodSignatures.Invoke.Matches (mr)) continue; // limit ourself to events if (!mr.IsEventCallback ()) continue; // first check if we're looking from a variable or directly from // the field (bad, it could be null) Instruction caller = ins.TraceBack (method); FieldDefinition field = caller.GetField (); if (field != null) { string msg = String.Format (CultureInfo.InvariantCulture, "Possible race condition since field '{0}' is accessed directly.", field.Name); Runner.Report (method, ins, Severity.High, Confidence.High, msg); } else { // look for the variable, if it's not then stop analysis VariableDefinition load = caller.GetVariable (method); if ((load != null) && !CheckVariable (method, caller, load)) { string msg = String.Format (CultureInfo.InvariantCulture, "Variable '{0}' does not seems to be checked against null.", load.Name); Runner.Report (method, ins, Severity.High, Confidence.Normal, msg); } } } 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 cast {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; }
// note: it's tempting to use IType rule here, since it would avoid iterating // all non-constructors methods. However the reporting would be less precise // since we want to report which source line inside a ctor is problematic public RuleResult CheckMethod (MethodDefinition method) { if (!method.IsConstructor || !method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; TypeReference type = method.DeclaringType; foreach (Instruction ins in method.Body.Instructions) { // check for assignation on instance or static fields Code code = ins.OpCode.Code; bool is_static = (code == Code.Stsfld); bool is_instance = (code == Code.Stfld); if (!is_static && !is_instance) continue; // special case: a struct ctor MUST assign every instance fields if (type.IsValueType && is_instance) continue; // make sure we assign to this type (and not another one) FieldReference fr = (ins.Operand as FieldReference); if (fr.DeclaringType != type) continue; bool unneeded = false; if (fr.FieldType.IsValueType) { unneeded = ins.Previous.IsOperandZero (); } else { unneeded = ins.Previous.OpCode.Code == Code.Ldnull; } if (unneeded) { // we're more confident about the unneeded initialization // on static ctor, since another (previous) ctor, can't set // the values differently Confidence c = method.IsStatic ? Confidence.High : Confidence.Normal; Runner.Report (method, ins, Severity.Medium, c, fr.Name); } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod(MethodDefinition method) { // applies only to methods with IL that are not generated by the compiler or tools if (!method.HasBody || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; // avoid processing methods that do not call any methods if (!OpCodeBitmask.Calls.Intersect (OpCodeEngine.GetBitmask (method))) return RuleResult.DoesNotApply; calls.Clear (); foreach (Instruction ins in method.Body.Instructions) { MethodReference mr = ins.GetMethod (); if ((mr == null) || mr.HasParameters) continue; MethodDefinition md = mr.Resolve (); // md can be null for things like: new byte[,]; if ((md == null) || !md.IsGetter) continue; if ((!md.IsVirtual || md.IsFinal) && IsInliningCandidate (md)) continue; // some properties are known, by design, to be called several time if (Filter (md)) continue; string key = GetKey (method, md, ins); KeyValuePair<MethodDefinition,int> kvp; if (calls.TryGetValue (key, out kvp)) { kvp = new KeyValuePair<MethodDefinition, int> (md, kvp.Value + 1); calls [key] = kvp; } else { kvp = new KeyValuePair<MethodDefinition, int> (md, 1); calls.Add (key, kvp); } } return ReportResults (method); }
private static bool IsResource (MethodDefinition method) { return method.IsStatic && method.IsGetter && method.IsGeneratedCode (); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.IsVirtual || !method.HasParameters || method.IsGeneratedCode ()) return RuleResult.DoesNotApply; MethodDefinition baseMethod = null; if (!method.IsNewSlot) baseMethod = GetBaseMethod (method); if (baseMethod == null) baseMethod = GetInterfaceMethod (method); if (baseMethod == null) return RuleResult.Success; IList<ParameterDefinition> base_pdc = baseMethod.Parameters; //do not trigger false positives on Boo macros if (IsBooAssemblyUsingMacro && IsBooMacroParameter (base_pdc [0])) return RuleResult.Success; IList<ParameterDefinition> pdc = method.Parameters; for (int i = 0; i < pdc.Count; i++) { if (pdc [i].Name != base_pdc [i].Name) { string s = String.Format (CultureInfo.InvariantCulture, "The name of parameter #{0} ({1}) does not match the name of the parameter in the overriden method ({2}).", i + 1, pdc [i].Name, base_pdc [i].Name); Runner.Report (method, Severity.Medium, Confidence.High, s); } } return Runner.CurrentRuleResult; }
static private bool Applicable (MethodDefinition method) { // rule doesn't apply to static ctor (called by the runtime) if (method.IsStatic && method.IsConstructor) return false; // don't consider the compiler generated add / remove on events if (((method.IsAddOn || method.IsRemoveOn) && method.IsSynchronized)) return false; // rule doesn't apply if the method is the assembly entry point or Main if (method.IsEntryPoint () || method.IsMain ()) return false; // rule doesn't apply if the method is generated by the compiler or by a tool if (method.IsGeneratedCode ()) return false; // does not apply if the method is used to register/unregister COM objects // or it is decorated with a [Conditional("x")] attribute if (method.HasCustomAttributes) { if (method.CustomAttributes.ContainsAnyType (SpecialAttributes)) return false; } return true; }