Beispiel #1
0
        public CallInfo[] Collect(Mono.Cecil.MethodDefinition method, Func <Instruction, bool> filter = null)
        {
            //if (method.Name != "SimpleLogFromTryCatch"
            //	&& method.Name != "EnqueueNoOpIfNeeded") return new CallInfo[0];

            if (!method.HasBody)
            {
                return(new CallInfo[0]);
            }

            var body = method.Body;

            if (method.Body.Instructions.Any(i => i.Is(OpCodes.Call, OpCodes.Callvirt) && filter(i)))
            {
                var function    = ilReader.ReadIL(body);
                var opsByOffset = body.Instructions.ToDictionary(instr => instr.Offset);

                var transformContext = decompiler.CreateILTransformContext(function);
                //foreach (var t in ILTransforms)
                //{
                //				t.Run(function, transformContext);
                //}

                var collector = new BlockVisitor(function, i =>
                {
                    var range = i.ILRange;
                    var op    = opsByOffset[range.Start];
                    while (!op.Is(OpCodes.Call, OpCodes.Callvirt))
                    {
                        op = op.Next;
                        if (op.Offset > range.End)
                        {
                            return(false);
                        }
                    }

                    return(filter(op));
                });

                function.AcceptVisitor(collector);

                if (collector.CallStarts.Count > 0)
                {
                    var retval = new CallInfo[collector.CallStarts.Count];

                    var i = 0;
                    foreach (var(blockStart, callOffset) in collector.CallStarts)
                    {
                        retval[i++] = new CallInfo(opsByOffset[blockStart], opsByOffset[callOffset]);
                    }

                    return(retval);
                }
            }

            return(new CallInfo[0]);
        }
Beispiel #2
0
            public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options)
            {
                base.DecompileMethod(method, output, options);
                var module    = method.ParentModule.PEFile;
                var metadata  = module.Metadata;
                var methodDef = metadata.GetMethodDefinition((SRM.MethodDefinitionHandle)method.MetadataToken);

                if (!methodDef.HasBody())
                {
                    return;
                }
                IAssemblyResolver assemblyResolver = module.GetAssemblyResolver();
                var typeSystem = new DecompilerTypeSystem(module, assemblyResolver);
                var reader     = new ILReader(typeSystem.MainModule);

                reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols;
                var        methodBody = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
                ILFunction il         = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken);
                var        decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings)
                {
                    CancellationToken = options.CancellationToken
                };
                ILTransformContext context = decompiler.CreateILTransformContext(il);

                context.Stepper.StepLimit = options.StepLimit;
                context.Stepper.IsDebug   = options.IsDebug;
                try
                {
                    il.RunTransforms(transforms, context);
                }
                catch (StepLimitReachedException)
                {
                }
                catch (Exception ex)
                {
                    output.WriteLine(ex.ToString());
                    output.WriteLine();
                    output.WriteLine("ILAst after the crash:");
                }
                finally
                {
                    // update stepper even if a transform crashed unexpectedly
                    if (options.StepLimit == int.MaxValue)
                    {
                        Stepper = context.Stepper;
                        OnStepperUpdated(new EventArgs());
                    }
                }
                (output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate {
                    Docking.DockWorkspace.Instance.ShowToolPane(DebugStepsPaneModel.PaneContentId);
                });
                output.WriteLine();
                il.WriteTo(output, DebugSteps.Options);
            }
Beispiel #3
0
        ILFunction TransformDelegateConstruction(NewObj value, out ILInstruction target)
        {
            target = null;
            if (!IsDelegateConstruction(value))
            {
                return(null);
            }
            var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method;

            if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod))
            {
                return(null);
            }
            target = value.Arguments[0];
            var methodDefinition = (Mono.Cecil.MethodDefinition)context.TypeSystem.GetCecil(targetMethod);

            if (methodDefinition == null)
            {
                return(null);
            }
            var localTypeSystem = context.TypeSystem.GetSpecializingTypeSystem(targetMethod.Substitution);
            var ilReader        = new ILReader(localTypeSystem);

            ilReader.UseDebugSymbols = context.Settings.UseDebugSymbols;
            var function = ilReader.ReadIL(methodDefinition.Body, context.CancellationToken);

            function.DelegateType = value.Method.DeclaringType;
            function.CheckInvariant(ILPhase.Normal);

            var contextPrefix = targetMethod.Name;

            foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter))
            {
                v.Name = contextPrefix + v.Name;
            }

            var nestedContext = new ILTransformContext(function, localTypeSystem, context.Settings)
            {
                CancellationToken = context.CancellationToken,
                DecompileRun      = context.DecompileRun
            };

            function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)), nestedContext);
            function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter)));
            // handle nested lambdas
            ((IILTransform) new DelegateConstruction()).Run(function, nestedContext);
            function.AddILRange(target.ILRange);
            function.AddILRange(value.Arguments[1].ILRange);
            return(function);
        }
Beispiel #4
0
            public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
            {
                base.DecompileMethod(method, output, options);
                if (!method.HasBody)
                {
                    return;
                }
                var typeSystem             = new DecompilerTypeSystem(method.Module);
                var specializingTypeSystem = typeSystem.GetSpecializingTypeSystem(new SimpleTypeResolveContext(typeSystem.Resolve(method)));
                var reader = new ILReader(specializingTypeSystem);

                reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols;
                ILFunction         il      = reader.ReadIL(method.Body, options.CancellationToken);
                ILTransformContext context = new ILTransformContext(il, typeSystem, options.DecompilerSettings)
                {
                    CancellationToken = options.CancellationToken
                };

                context.Stepper.StepLimit = options.StepLimit;
                context.Stepper.IsDebug   = options.IsDebug;
                try {
                    il.RunTransforms(transforms, context);
                } catch (StepLimitReachedException) {
                } catch (Exception ex) {
                    output.WriteLine(ex.ToString());
                    output.WriteLine();
                    output.WriteLine("ILAst after the crash:");
                } finally {
                    // update stepper even if a transform crashed unexpectedly
                    if (options.StepLimit == int.MaxValue)
                    {
                        Stepper = context.Stepper;
                        OnStepperUpdated(new EventArgs());
                    }
                }
                (output as ISmartTextOutput)?.AddUIElement(OptionsCheckBox(nameof(writingOptions.UseFieldSugar)));
                output.WriteLine();
                (output as ISmartTextOutput)?.AddUIElement(OptionsCheckBox(nameof(writingOptions.UseLogicOperationSugar)));
                output.WriteLine();
                (output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate {
                    DebugSteps.Show();
                });
                output.WriteLine();
                il.WriteTo(output, writingOptions);
            }
Beispiel #5
0
            public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
            {
                base.DecompileMethod(method, output, options);
                if (!method.HasBody)
                {
                    return;
                }
                var      typeSystem = new DecompilerTypeSystem(method.Module);
                ILReader reader     = new ILReader(typeSystem);

                reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols;
                ILFunction         il      = reader.ReadIL(method.Body, options.CancellationToken);
                ILTransformContext context = new ILTransformContext {
                    Settings          = options.DecompilerSettings,
                    TypeSystem        = typeSystem,
                    CancellationToken = options.CancellationToken
                };

                context.Stepper.StepLimit = options.StepLimit;
                context.Stepper.IsDebug   = options.IsDebug;
                try {
                    il.RunTransforms(transforms, context);
                } catch (StepLimitReachedException) {
                } finally {
                    // update stepper even if a transform crashed unexpectedly
                    if (options.StepLimit == int.MaxValue)
                    {
                        Stepper = context.Stepper;
                        OnStepperUpdated(new EventArgs());
                    }
                }
                (output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate {
                    DebugSteps.Show();
                });
                output.WriteLine();
                il.WriteTo(output);
            }
Beispiel #6
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            this.context = context;
            foreach (var inst in function.Descendants.OfType <CallInstruction>())
            {
                MethodDefinition methodDef = context.TypeSystem.GetCecil(inst.Method) as MethodDefinition;
                if (methodDef != null && methodDef.Body != null)
                {
                    if (IsDefinedInCurrentOrOuterClass(inst.Method, context.Function.Method.DeclaringTypeDefinition) && inst.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
                    {
                        // partially copied from CSharpDecompiler
                        var specializingTypeSystem = this.context.TypeSystem.GetSpecializingTypeSystem(inst.Method.Substitution);
                        var ilReader = new ILReader(specializingTypeSystem);
                        System.Threading.CancellationToken cancellationToken = new System.Threading.CancellationToken();
                        var proxyFunction    = ilReader.ReadIL(methodDef.Body, cancellationToken);
                        var transformContext = new ILTransformContext(proxyFunction, specializingTypeSystem, this.context.Settings)
                        {
                            CancellationToken = cancellationToken,
                            DecompileRun      = context.DecompileRun
                        };
                        foreach (var transform in CSharp.CSharpDecompiler.GetILTransforms())
                        {
                            if (transform.GetType() != typeof(ProxyCallReplacer))                               // don't call itself on itself
                            {
                                cancellationToken.ThrowIfCancellationRequested();
                                transform.Run(proxyFunction, transformContext);
                            }
                        }

                        if (!(proxyFunction.Body is BlockContainer blockContainer))
                        {
                            return;
                        }
                        if (blockContainer.Blocks.Count != 1)
                        {
                            return;
                        }
                        var  block = blockContainer.Blocks[0];
                        Call call  = null;
                        if (block.Instructions.Count == 1)
                        {
                            // leave IL_0000 (call Test(ldloc this, ldloc A_1))
                            if (!block.Instructions[0].MatchLeave(blockContainer, out ILInstruction returnValue))
                            {
                                return;
                            }
                            call = returnValue as Call;
                        }
                        else if (block.Instructions.Count == 2)
                        {
                            // call Test(ldloc this, ldloc A_1)
                            // leave IL_0000(nop)
                            call = block.Instructions[0] as Call;
                            if (!block.Instructions[1].MatchLeave(blockContainer, out ILInstruction returnValue))
                            {
                                return;
                            }
                            if (!returnValue.MatchNop())
                            {
                                return;
                            }
                        }
                        if (call == null)
                        {
                            return;
                        }
                        if (call.Method.IsConstructor)
                        {
                            return;
                        }

                        // check if original arguments are only correct ldloc calls
                        for (int i = 0; i < call.Arguments.Count; i++)
                        {
                            var originalArg = call.Arguments[i];
                            if (!originalArg.MatchLdLoc(out ILVariable var) ||
                                var.Kind != VariableKind.Parameter ||
                                var.Index != i - 1)
                            {
                                return;
                            }
                        }

                        Call newInst = (Call)call.Clone();

                        newInst.Arguments.ReplaceList(inst.Arguments);
                        inst.ReplaceWith(newInst);
                    }
                }
            }
        }
Beispiel #7
0
        public Dictionary <long, EventRegistration[]> DecompileEventMappings(string fullTypeName, CancellationToken cancellationToken)
        {
            var            result = new Dictionary <long, EventRegistration[]>();
            TypeDefinition type   = this.assembly.MainModule.GetType(fullTypeName);

            if (type == null)
            {
                return(result);
            }

            MethodDefinition method = null;

            foreach (var m in type.Methods)
            {
                if (m.Name == "System.Windows.Markup.IComponentConnector.Connect")
                {
                    method = m;
                    break;
                }
            }

            if (method == null)
            {
                return(result);
            }

            // decompile method and optimize the switch
            var typeSystem = new DecompilerTypeSystem(method.Module);
            var ilReader   = new ILReader(typeSystem);
            var function   = ilReader.ReadIL(method.Body, cancellationToken);

            var context = new ILTransformContext(function, typeSystem)
            {
                CancellationToken = cancellationToken
            };

            function.RunTransforms(CSharpDecompiler.GetILTransforms(), context);

            var block    = function.Body.Children.OfType <Block>().First();
            var ilSwitch = block.Children.OfType <SwitchInstruction>().FirstOrDefault();

            if (ilSwitch != null)
            {
                foreach (var section in ilSwitch.Sections)
                {
                    var events = FindEvents(section.Body);
                    foreach (long id in section.Labels.Values)
                    {
                        result.Add(id, events);
                    }
                }
            }
            else
            {
                foreach (var ifInst in function.Descendants.OfType <IfInstruction>())
                {
                    var comp = ifInst.Condition as Comp;
                    if (comp.Kind != ComparisonKind.Inequality && comp.Kind != ComparisonKind.Equality)
                    {
                        continue;
                    }
                    int id;
                    if (!comp.Right.MatchLdcI4(out id))
                    {
                        continue;
                    }
                    var events = FindEvents(comp.Kind == ComparisonKind.Inequality ? ifInst.FalseInst : ifInst.TrueInst);
                    result.Add(id, events);
                }
            }
            return(result);
        }
Beispiel #8
0
        /// <summary>Checks the specified assemblies for any obvious Lingo-related problems, including unused strings, mismatched enum translations.</summary>
        /// <typeparam name="TTranslation">The type of the translation class.</typeparam>
        /// <param name="rep">Post-build step reporter.</param>
        /// <param name="assemblies">A list of assemblies to check. The Lingo assembly is included automatically to ensure correct operation.</param>
        public static void PostBuildStep <TTranslation>(IPostBuildReporter rep, params Assembly[] assemblies)
        {
            if (!assemblies.Contains(Assembly.GetExecutingAssembly()))
            {
                assemblies = assemblies.Concat(Assembly.GetExecutingAssembly()).ToArray();
            }

            // Check that all enum translations are sensible
            var allEnumTrs = allEnumTranslations(assemblies).ToList();

            foreach (var tr in allEnumTrs)
            {
                checkEnumTranslation(rep, tr.EnumType, tr.TranslationType);
            }

            // Check all component model member translations
            foreach (var type in assemblies.SelectMany(a => a.GetTypes()))
            {
                // All functions returning MemberTr and accepting a TranslationBase descendant must conform
                var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => p.Name).ToHashSet();
                foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    if (method.ReturnType == typeof(MemberTr) && method.GetParameters().Length == 1 && typeof(TranslationBase).IsAssignableFrom(method.GetParameters()[0].ParameterType))
                    {
                        if (!method.IsStatic)
                        {
                            rep.Error("A member translation method must be static. Translation method: {0}".Fmt(method.DeclaringType.FullName + "." + method.Name), "class " + method.DeclaringType.Name, typeof(MemberTr).Name + " " + method.Name);
                        }
                        if (!method.Name.EndsWith("Tr"))
                        {
                            rep.Error("The name of a member translation method must end with the letters \"Tr\". Translation method: {0}".Fmt(method.DeclaringType.FullName + "." + method.Name), "class " + method.DeclaringType.Name, typeof(MemberTr).Name + " " + method.Name);
                        }
                        var propertyName = method.Name.Substring(0, method.Name.Length - 2);
                        if (!properties.Contains(propertyName))
                        {
                            rep.Error("Member translation method has no corresponding property named \"{1}\". Translation method: {0}".Fmt(method.DeclaringType.FullName + "." + method.Name, propertyName), "class " + method.DeclaringType.Name, typeof(MemberTr).Name + " " + method.Name);
                        }
                    }
                }
            }

            // Find unused strings
            var fields = new HashSet <FieldInfo>();

            addAllLingoRelevantFields(typeof(TTranslation), fields);

            // Treat all fields used for enum translations as used
            foreach (var f in allEnumTrs.SelectMany(et => et.TranslationType.GetAllFields()))
            {
                fields.Remove(f);
            }

            // Treat all fields that occur in a ldfld / ldflda instruction as used
            foreach (var mod in assemblies.SelectMany(a => a.GetModules(false)))
            {
                foreach (var typ in mod.GetTypes())
                {
                    foreach (var meth in
                             typ.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance).Where(m => m.DeclaringType == typ).Cast <MethodBase>().Concat(
                                 typ.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance).Where(c => c.DeclaringType == typ).Cast <MethodBase>()))
                    {
                        foreach (var instr in ILReader.ReadIL(meth, typ))
                        {
                            if (instr.OpCode == OpCodes.Ldfld || instr.OpCode == OpCodes.Ldflda)
                            {
                                fields.Remove((FieldInfo)instr.Operand);
                                if (fields.Count == 0)
                                {
                                    goto done; // don't have to break the loop early, but there's really no point in searching the rest of the code now
                                }
                            }
                        }
                    }
                }
            }

            // Report warnings for all unused strings (not errors so that the developer can test things in the presence of unused strings)
done:
            foreach (var field in fields)
            {
                rep.Warning("Unused Lingo field: " + field.DeclaringType.FullName + "." + field.Name, "class " + field.DeclaringType.Name, field.FieldType.Name, field.Name);
            }
        }
Beispiel #9
0
        /// <summary>
        ///     Runs all post-build checks defined in the specified assemblies. This is intended to be run as a post-build
        ///     event. See remarks for details.</summary>
        /// <remarks>
        ///     <para>
        ///         In non-DEBUG mode, does nothing and returns 0.</para>
        ///     <para>
        ///         Intended use is as follows:</para>
        ///     <list type="bullet">
        ///         <item><description>
        ///             <para>
        ///                 Add the following line to your project's post-build event:</para>
        ///             <code>
        ///                 "$(TargetPath)" --post-build-check "$(SolutionDir)."</code></description></item>
        ///         <item><description>
        ///             <para>
        ///                 Add the following code at the beginning of your project's Main() method:</para>
        ///             <code>
        ///                 if (args.Length == 2 &amp;&amp; args[0] == "--post-build-check")
        ///                     return PostBuildChecker.RunPostBuildChecks(args[1], Assembly.GetExecutingAssembly());</code>
        ///             <para>
        ///                 If your project entails several assemblies, you can specify additional assemblies in the call to
        ///                 <see cref="PostBuildChecker.RunPostBuildChecks"/>. For example, you could specify
        ///                 <c>typeof(SomeTypeInMyLibrary).Assembly</c>.</para></description></item>
        ///         <item><description>
        ///             <para>
        ///                 Add post-build check methods to any type where they may be relevant. For example, you might use
        ///                 code similar to the following:</para>
        ///             <code>
        ///                 #if DEBUG
        ///                     private static void PostBuildCheck(IPostBuildReporter rep)
        ///                     {
        ///                         if (somethingWrong())
        ///                             rep.Error("Error XYZ occurred.", "class", "Gizmo");
        ///                     }
        ///                 #endif</code>
        ///             <para>
        ///                 The method is expected to have one parameter of type <see cref="IPostBuildReporter"/>, a return
        ///                 type of void, and it is expected to be static and non-public. Errors and warnings can be reported
        ///                 by calling methods on said <see cref="IPostBuildReporter"/> object. (In the above example,
        ///                 PostBuildChecker will attempt to find a class called Gizmo to link the error message to a location
        ///                 in the source.) Throwing an exception will also report an error.</para></description></item></list></remarks>
        /// <param name="sourcePath">
        ///     Specifies the path to the folder containing the C# source files.</param>
        /// <param name="assemblies">
        ///     Specifies the compiled assemblies from which to run post-build checks.</param>
        /// <returns>
        ///     1 if any errors occurred, otherwise 0.</returns>
        public static int RunPostBuildChecks(string sourcePath, params Assembly[] assemblies)
        {
            int countMethods = 0;
            var rep          = new postBuildReporter(sourcePath);
            var attempt      = Ut.Lambda((Action action) =>
            {
                try
                {
                    action();
                }
                catch (Exception e)
                {
                    rep.AnyErrors = true;
                    string indent = "";
                    while (e != null)
                    {
                        var st          = new StackTrace(e, true);
                        string fileLine = null;
                        for (int i = 0; i < st.FrameCount; i++)
                        {
                            var frame = st.GetFrame(i);
                            if (frame.GetFileName() != null)
                            {
                                fileLine = frame.GetFileName() + "(" + frame.GetFileLineNumber() + "," + frame.GetFileColumnNumber() + "): ";
                                break;
                            }
                        }

                        Console.Error.WriteLine($"{fileLine}Error: {indent}{e.Message.Replace("\n", " ").Replace("\r", "")} ({e.GetType().FullName})");
                        Console.Error.WriteLine(e.StackTrace);
                        e       = e.InnerException;
                        indent += "---- ";
                    }
                }
            });

            // Step 1: Run all the custom-defined PostBuildCheck methods
            foreach (var ty in assemblies.SelectMany(asm => asm.GetTypes()))
            {
                attempt(() =>
                {
                    var meth = ty.GetMethod("PostBuildCheck", BindingFlags.NonPublic | BindingFlags.Static);
                    if (meth != null)
                    {
                        if (meth.GetParameters().Select(p => p.ParameterType).SequenceEqual(new Type[] { typeof(IPostBuildReporter) }) && meth.ReturnType == typeof(void))
                        {
                            countMethods++;
                            meth.Invoke(null, new object[] { rep });
                        }
                        else
                        {
                            rep.Error(
                                $"The type {ty.FullName} has a method called PostBuildCheck() that is not of the expected signature. There should be one parameter of type {typeof(IPostBuildReporter).FullName}, and the return type should be void.",
                                (ty.IsValueType ? "struct " : "class ") + ty.Name, "PostBuildCheck");
                        }
                    }
                });
            }

            // Step 2: Run all the built-in checks on IL code
            foreach (var asm in assemblies)
            {
                foreach (var type in asm.GetTypes())
                {
                    foreach (var meth in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly))
                    {
                        attempt(() =>
                        {
                            var instructions = ILReader.ReadIL(meth, type).ToArray();
                            for (int i = 0; i < instructions.Length; i++)
                            {
                                // Check that “throw new ArgumentNullException(...)” statements refer to an actual parameter
                                if (instructions[i].OpCode.Value == OpCodes.Newobj.Value)
                                {
                                    var constructor       = (ConstructorInfo)instructions[i].Operand;
                                    string wrong          = null;
                                    string wrongException = "ArgumentNullException";
                                    if (constructor.DeclaringType == typeof(ArgumentNullException) && constructor.GetParameters().Select(p => p.ParameterType).SequenceEqual(typeof(string)))
                                    {
                                        if (instructions[i - 1].OpCode.Value == OpCodes.Ldstr.Value)
                                        {
                                            if (!meth.GetParameters().Any(p => p.Name == (string)instructions[i - 1].Operand))
                                            {
                                                wrong = (string)instructions[i - 1].Operand;
                                            }
                                        }
                                    }
                                    if (constructor.DeclaringType == typeof(ArgumentNullException) && constructor.GetParameters().Select(p => p.ParameterType).SequenceEqual(typeof(string), typeof(string)))
                                    {
                                        if (instructions[i - 1].OpCode.Value == OpCodes.Ldstr.Value && instructions[i - 2].OpCode.Value == OpCodes.Ldstr.Value)
                                        {
                                            if (!meth.GetParameters().Any(p => p.Name == (string)instructions[i - 2].Operand))
                                            {
                                                wrong = (string)instructions[i - 2].Operand;
                                            }
                                        }
                                    }
                                    if (constructor.DeclaringType == typeof(ArgumentException) && constructor.GetParameters().Select(p => p.ParameterType).SequenceEqual(typeof(string), typeof(string)))
                                    {
                                        if (instructions[i - 1].OpCode.Value == OpCodes.Ldstr.Value)
                                        {
                                            if (!meth.GetParameters().Any(p => p.Name == (string)instructions[i - 1].Operand))
                                            {
                                                wrong          = (string)instructions[i - 1].Operand;
                                                wrongException = "ArgumentException";
                                            }
                                        }
                                    }

                                    if (wrong != null)
                                    {
                                        rep.Error(
                                            Regex.IsMatch(meth.DeclaringType.Name, @"<.*>d__\d")
                                                ? $@"The iterator method ""{type.FullName}.{meth.Name}"" constructs a {wrongException}. Move this argument check outside the iterator."
                                                : $@"The method ""{type.FullName}.{meth.Name}"" constructs an {wrongException} with a parameter name ""{wrong}"" which doesn't appear to be a parameter in that method.",
                                            getDebugClassName(meth),
                                            getDebugMethodName(meth),
                                            wrongException,
                                            wrong
                                            );
                                    }

                                    if (constructor.DeclaringType == typeof(ArgumentException) && constructor.GetParameters().Select(p => p.ParameterType).SequenceEqual(typeof(string)))
                                    {
                                        rep.Error(
                                            Regex.IsMatch(meth.DeclaringType.Name, @"<.*>d__\d")
                                                ? $@"The iterator method ""{type.FullName}.{meth.Name}"" constructs an ArgumentException. Move this argument check outside the iterator."
                                                : $@"The method ""{type.FullName}.{meth.Name}"" uses the single-argument constructor to ArgumentException. Please use the two-argument constructor and specify the parameter name. If there is no parameter involved, use InvalidOperationException.",
                                            getDebugClassName(meth),
                                            getDebugMethodName(meth),
                                            "ArgumentException");
                                    }
                                }
                                else if (i < instructions.Length - 1 && (instructions[i].OpCode.Value == OpCodes.Call.Value || instructions[i].OpCode.Value == OpCodes.Callvirt.Value) && instructions[i + 1].OpCode.Value == OpCodes.Pop.Value)
                                {
                                    var method = (MethodInfo)instructions[i].Operand;
                                    var mType  = method.DeclaringType;
                                    if (postBuildGetNoPopMethods().Contains(method))
                                    {
                                        rep.Error(
                                            $@"Useless call to ""{mType.FullName}.{method.Name}"" (the return value is discarded).",
                                            getDebugClassName(meth),
                                            getDebugMethodName(meth),
                                            method.Name
                                            );
                                    }
                                }
                            }
                        });
                    }
                }
            }

            Console.WriteLine($"Post-build checks ran on {assemblies.Length} assemblies, {countMethods} methods and completed {(rep.AnyErrors ? "with ERRORS" : "SUCCESSFULLY")}.");

            return(rep.AnyErrors ? 1 : 0);
        }