Showing posts with label Code Contracts. Show all posts
Showing posts with label Code Contracts. Show all posts

2017-04-28

Code Contracts in Visual Studio 2017

At the moment, the Code Contracts project has not caught up to Visual Studio 2017. As a work-around, you can copy a file from the install for Visual Studio 2015 into the proper place and it will allow the projects to build. It will not, however, enable the tab for setting the options in the Project Properties. For that you will still need to open the project in Visual Studio 2015.

The word Community will need to be replaced by the specific version of Visual Studio you have installed, Community, Professional, etc., in the script that follows.

This can be placed into a batch file (fixcontracts.bat) that can be checked into the project. Run the batch file as an administrator.

xcopy /y "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targets" "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Microsoft.Common.targets\ImportAfter\"
setx /m TargetFrameworkSDKToolsDirectory "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7 Tools\\"
pause

The line with TargetFrameworkSDKToolsDirectory sets a system environment variable to point to a directory where it can find sn.exe that re-signs the executable after adding the contracts. The defaults inside the targets file do not find sn.exe correctly (at least in some of my environments) without this variable. The two slashes at the end are not a mistake; the first escapes the second and only seems to be needed when preceding a quote mark. If you need to adjust this variable later, you can type System in the taskbar search box, then click the  Advanced system settings item, then click the Environment Variables button and edit it in the dialog that appears.
Contracts won't work with the .Net Framework 4.7, at least until they do a new release, so stick with 4.6 at the latest.

2014-11-26

Code Contracts and the Visual Studio 2015 Preview

I have been attempting to get a project to compile in the Visual Studio 2015 Preview. Unfortunately, it isn't working because I use Code Contracts throughout the project, with the Contract.Requires() method. The current version of Code Contracts does not install or work with the Visual Studio 2015 preview.

I found one work-around, but it only got me part of the way there. From this post about the Visual Studio 2013 Preview, I discovered that you can copy a file from a working installation of Visual Studio into the correct directory for Visual Studio 2015 and the build process will attempt to use Code Contracts. The trick is to copy the file C:\Program Files (x86)\MSBuild\12.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targets to the folder C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.Targets\ImportAfter\

However, that only solves the first hurdle. The Code Contracts rewriter goes through your compiled code and modifies the IL (Intermediate Language) to  support the Code Contracts. However, the C# Compiler used by Visual Studio 2015 is the Roslyn compiler. When I try to compile, the Code Contracts tool ccrefgen.exe produces the error: AsmMeta failed with uncaught exception: Unknown custom metadata item kind: 5.

The reason is that the Roslyn compiler is producing some meta data that the Code Contracts tools don't handle correctly. There is a switch statement that handles cases for the numbers 1 through 4, but not 5. This article describes the problem and a possible convoluted fix by patching the code contracts tools to ignore the value 5.Trying to update the code is not easy.

I will update this article if I get things working. [8/11/2015 It is FINALLY fixed in the latest release!]

The right solution, of course, is for the Code Contracts people to support the preview. Vote up this forum request so we can get that to happen.

2014-03-12

10 Steps for Implementing Code Contracts Static Analysis

I'm a fan of the Code Contracts from Microsoft Research. Adding them to an existing code base can be an exercise in frustration, though. If you turn on static analysis, it can generate thousands of warnings. I have some tips on how to attack those warnings, based on my experience.

Just like C#, I start counting at zero, and actually this list goes to 11.

 0. Change the settings for contracts.

Perform Static Contract Checking in the Background. Turn on all the check boxes except Baseline. It will be slightly faster if you have SQLExpress installed on your computer. Add .\SQLExpress into the SQL Server field. Set the warning level to low. Set the Contract Reference Assembly to build. Change the Extra Static Checker Options to -maxWarnings 5000.

 1. Reduce the problem.

In the assemblyinfo.cs file, add the following line:

[assembly: ContractVerification(false)]

This causes the static analyzer to ignore all of the warnings. Now go to the most basic class in your project and add ContractVerification(true) to the top of it. You can identify these basic classes because they have no dependencies on the other classes in the project. You may find that adding a Class Diagram to the project helps identify these basic classes.

An example of using ContractVerification on a particular class:

[ContractVerification(true)]
public class Foo
{
}

This will cause the static analyzer to only report warnings for that one class. Fix those warnings, then move the [ContractVerification(true)] to the next class you want to work on.

2. Work from the bottom up.

Work on classes that have no dependencies inside your project. Then after you have those cleaned up, work on the ones that only depend on the ones that you have already cleaned up until the entire assembly is complete. Again, adding a Class Diagram to the project may help finding the next class to work on.

3. For each class, add ContractPublicPropertyName attributes to your property backing fields.

For each field that is used to back a property, add the ContractPublicPropertyName attribute to the field showing what public property accesses the field.

[ContractPublicPropertyName("Count")]
private int count = 0;

public int Count
{
    get
    {
        return this.count;
    }
}

4. Add invariants.

Don't worry about other issues until the invariants are in place. I add an invariant section to the bottom of each class like the code below. (I sort my method names alphabetically and I like mine at the bottom of the code, hence the Zz. You can name the method anything you want, but be consistent throughout the project.)

/// <summary>Object invariant.</summary>
[ContractInvariantMethod]
private void ZzObjectInvariant()
{
    Contract.Invariant(this.count >= 0);
}

For each thing will always be true after the constructor is complete, add a contract to the ZzObjectInvariant method. You want the invariants to be in place first because it makes it so you don't need contracts in each individual method or property.

5. Go through each constructor, method, and property set in the class and add contracts.

For each parameter to the method, add appropriate Contract.Requires<exception>() contracts. For example:

public int Foo(Bar bar, int increment)
{
    Contract.Requires<ArgumentNullException>(bar != null);
    Contract.Requires<ArgumentOutOfRangeException>(increment > 0);

    // more stuff
}

For properties, validate the set clause value.

public int Count
{
    get
    {
        return this.count;
    }

    set
    {
        Contract.Requires<ArgumentOutOfRangeException>(value >= 0);
        this.count = value;
    }
}

If there are two conditions, put them into separate contracts rather than using &&. For example:

public int Count
{
    get
    {
        return this.count;
    }

    set
    {
        // not Contract.Requires<ArgumentOutOfRangeException>(value >= 0 && value <= 100);
        Contract.Requires<ArgumentOutOfRangeException>(value >= 0);
        Contract.Requires<ArgumentOutOfRangeException>(value <= 100);
        this.count = value;
    }
}

If a member overrides an inherited member or implements an interface, you will not be able to add Contract.Requires contracts to the member. First see if you can add the Contract.Requires to the class or interface that you are overriding or implementing. If you can't, then add a Contract.Assumes to the code. Adding Contract.Requires to an interface or abstract class requires creating a class that implements the interface or abstract class and decorating it with the ContractClassFor attribute. See sections 2.8 and 2.9 of the Code Contracts user manual.

Understand that depending on your project contract settings these contracts may not be present at run time. Other warnings may report that you are trying to invoke a method or access a property of a null (the dreaded CA1062). Even though it will be impossible to pass in a null if the contract is in place, the code analysis can't count that the contract will be in the delivered code. You will need to add additional code that acts as if the contract doesn't exist and properly handles the condition. It's redundant and has some expense. I like none of the other options, though.

You can throw an exception. This is similar to the legacy Custom Parameter Validation, except this happens after the Contracts, and isn't actually part of them. There is no Contract.EndContractBlock().

public int Foo(Bar bar, int increment)
{
    Contract.Requires<ArgumentNullException>(bar != null);
    Contract.Requires<ArgumentOutOfRangeException>(increment > 0);

    if (bar == null)
    {
        throw new ArgumentNullException("bar");
    }

    // more stuff
}

You can also return an appropriate value, like this:

public int Foo(Bar bar, int increment)
{
    Contract.Requires<ArgumentNullException>(bar != null);
    Contract.Requires<ArgumentOutOfRangeException>(increment > 0);

    int result = 0;

    if (bar != null)
    {
        // more stuff that assigns result
    }

    return result;
}

In both cases, the contract makes it impossible for bar to be null, yet I handle it anyway as if the contract wasn't there. Some day they may make the coordination between the static analyzer and the compiler such that this isn't necessary. You would then have to have code contracts turned on in the shipping version, which you may not want.

6. Go through the methods and properties and add Contract.Ensure contracts.

Add Contract.Ensure methods for all of the return values of the constructors, methods, and properties. You will need Contract.Result<T>() frequently to examine return values. For example:

public int Foo(Bar bar, int increment)
{
    Contract.Requires<ArgumentNullException>(bar != null);
    Contract.Requires<ArgumentOutOfRangeException>(increment > 0);
    Contract.Ensures(Contract.Result<int>() > 0);

    // more stuff
}

7. Fix Bugs.

Compile the code. The static analyzer will complain about various things, such as calling methods on possibly null values. Fix those. This is really the point of the whole exercise. It will warn you about many subtle things that you might have initially thought impossible, but are actually real edge cases.

When you compile the project, it may make suggestions for Contract.Requires and Contract.Assumes. Do not automatically add these. See if there is an invariant that you can add that would handle this for all members. Also see if the code should be handling whatever the warning is suggesting. For example, if the warning suggests adding a Contract.Requires<ArgumentNullException>(value != null), it may be legitimate to be able to pass in null here, but you are performing a method on the value object. You really need an 'if (value != null)' in the code, not a contract. Determining whether null should be allowed requires intimate knowledge of the code.

8. Judiciously add Contract.Assume() calls.

Static analysis is hard. The analyzer has to figure out what the code is doing without running it. In some cases it can't. This is especially true if you use a library that you don't control. Microsoft has been better about adding contracts to the .NET framework, but there is not 100% support yet. For example, using the .NET Framework 4.0, I have this code:

PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(figure);

The static analyzer complains that pathGeometry.Figures might be null. This should never be the case. There is a missing Contract.Ensures in the .NET Framework PathGeometry constructor that .Figures is not null. You can help the analyzer by adding an Contract.Assume() call. Like this:

public int Count
{
    get
    {
        return this.count;
    }

    set
    {
        Contract.Requires<ArgumentOutOfRangeException>(value >= 0);
        this.count = value;
    }
}

The warning about .Figures possibly being null now goes away.

Add Assumes for things that should be impossible in your code.

9. Don't use Contract.Assert.

If the analyzer reports that an Assume can be proven and can be turned into an Assert, just remove the line. The analyzer actually has that knowledge at that point, so you don't need an assert.

10. Don't give up.

When all the warnings are killed, move on to the next class. Move the [ContractVerification(true)] and fix those. When you actually have all the warnings fixed, go back to the assemblyinfo.cs and turn on warnings for the entire assembly and remove the attribute from the individual classes. Then try bumping the warning level up a notch in the project properties and fix some more.

Some of the warnings are a real puzzle as you try to trick the static analyzer to certify your code. Adding more contracts, ensures, and assumes will cause them to go away.

2014-03-09

Listing SuppressMessage Justifications

If you perform Code Analysis on a project, it will complain about many things. Most are legitimate. A few are not. When they are not, you can make the warning go away using the SuppressMessage attribute. This attribute decorates the method, property, or whatever, and causes the Code Analysis tool to ignore that warning in that scope. For example:

[SuppressMessage(
    "Microsoft.Design",
    "CA1031:DoNotCatchGeneralExceptionTypes",
    Justification = "Want to catch all exceptions here.")]

If you use StyleCop on the project, it will require the SuppressMessage to have the Justification property filled in. If you don't, then StyleCop will give a warning about a missing justification, which is a warning about the code that you used to make a different warning go away. So I fill in all my Justification properties.

I wanted to look at the justifications that I had used across an entire code base to make them consistent, and see if there were any that weren't really needed. As code gets refactored, the SuppressMessage attributes may no longer apply. The compiler, by the way, does not remove the SuppressMessage attributes; they get placed into the executable code.

I created a little tool that examines a directory and performs reflection on all the .exe and .dll files in it. It extracts all of the SuppressMessage attributes and writes their details to the console output. It's actually a nice little example of using reflection.

Below is the code for the Program.cs file of a console application. This is a quick implementation, for my own purposes, not a commercial product, so probably could be better in many ways. For example, it only searches for dlls with a lower case extension, because I never have upper case ones. In a commercial project, I'd test for either. I could fix that in less time than it took to write this sentence (using .ToUpper()), but it doesn't solve any problems for my environment. Making code commercial grade as opposed to a tool takes time.

The code takes a directory path on the command line and documents the justifications for all the .NET assemblies in that path.

//--------------------------------------------------------------------------------------------------
// <copyright file="Program.cs" company="Xoc Software">
// Copyright © 2014 Xoc Software
// </copyright>
// <summary>Implements the program class</summary>
//--------------------------------------------------------------------------------------------------
namespace Xoc.Justification
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Diagnostics.Contracts;
    using System.Globalization;
    using System.IO;
    using System.Reflection;

    /// <summary>The Justification Program.</summary>
    public static class Program
    {
        /// <summary>Main entry-point for this application.</summary>
        /// <param name="args">Array of command-line argument strings. args[0] must be path to examine.</param>
        public static void Main(string[] args)
        {
            if (args != null && args.Length > 0 && !string.IsNullOrEmpty(args[0]))
            {
                DirectoryInfo directoryInfo = new DirectoryInfo(args[0]);
                if (directoryInfo != null)
                {
                    Console.WriteLine("Finding Justifications in {0}", directoryInfo.FullName);
                    try
                    {
                        foreach (FileInfo file in directoryInfo.GetFiles())
                        {
                            switch (file.Name.ToUpper(CultureInfo.InvariantCulture))
                            {
                                // Ignore NLog. Add other DLLs to ignore here.
                                case "NLOG.DLL":
                                    break;
                                default:
                                    ProcessAssembly(file);

                                    break;
                            }
                        }
                    }
                    catch (IOException)
                    {
                        Console.WriteLine("Directory is invalid");
                    }
                }
            }
            else
            {
                Console.WriteLine("Syntax:\nXoc.Justification.exe <directory-path>");
            }
        }

        /// <summary>Process the assembly described by file.</summary>
        /// <param name="fileInfo">The FileInfo assembly for the assembly to document.</param>
        private static void ProcessAssembly(FileInfo fileInfo)
        {
            if (fileInfo.Extension == ".dll" || fileInfo.Extension == ".exe")
            {
                Assembly assembly = Assembly.LoadFrom(fileInfo.FullName);
                string assemblyName = assembly.GetName().Name;
                try
                {
                    foreach (Type type in assembly.GetTypes())
                    {
                        string typeName = type.Name;
                        BindingFlags flags =
                            BindingFlags.Static
                            | BindingFlags.Instance
                            | BindingFlags.Public
                            | BindingFlags.NonPublic;

                        var attributes = type.GetCustomAttributes<SuppressMessageAttribute>();
                        if (attributes != null)
                        {
                            foreach (SuppressMessageAttribute attribute in attributes)
                            {
                                if (attribute != null)
                                {
                                    AddToList(assemblyName, typeName, "type", attribute);
                                }
                            }
                        }

                        foreach (MemberInfo memberInfo in type.GetMembers(flags))
                        {
                            if (memberInfo != null)
                            {
                                EnumerateTypes(assemblyName, typeName, memberInfo);
                            }
                        }
                    }
                }
                catch (ReflectionTypeLoadException)
                {
                    Console.WriteLine("{0} not a .NET assembly.", assemblyName);
                }
            }
        }

        /// <summary>Adds to list.</summary>
        /// <param name="assembly">The assembly to document.</param>
        /// <param name="type">The type to document.</param>
        /// <param name="member">The member to document.</param>
        /// <param name="attribute">The SuppressMessage attribute.</param>
        private static void AddToList(
            string assembly,
            string type,
            string member,
            SuppressMessageAttribute attribute)
        {
            Contract.Requires<ArgumentNullException>(attribute != null);

            Console.WriteLine(
                "{0} | {1} | {2} | {3} | {4}",
                assembly,
                type,
                member,
                attribute.CheckId,
                attribute.Justification);
        }

        /// <summary>Enumerate types.</summary>
        /// <param name="assembly">The assembly to document.</param>
        /// <param name="type">The type to document.</param>
        /// <param name="memberInfo">Information describing the member.</param>
        private static void EnumerateTypes(string assembly, string type, MemberInfo memberInfo)
        {
            Contract.Requires<ArgumentNullException>(memberInfo != null);

            var attributes = memberInfo.GetCustomAttributes<SuppressMessageAttribute>();
            if (attributes != null)
            {
                foreach (SuppressMessageAttribute attribute in attributes)
                {
                    if (attribute != null)
                    {
                        AddToList(assembly, type, memberInfo.Name, attribute);
                    }
                }
            }
        }
    }
}

You can then add this as an external tool in Visual Studio tools menu. Set the argument to be  $(BinDir).

If you'd really like the complete project, let me know in the comments, and I'll do the extra work to publish it.

2014-02-17

Requiring Parameter Value to be a Valid Enum Value Using a Code Contract

Let's suppose that you have an Enum in C#, that has four values:

public enum Direction
{
    North,
    East,
    South,
    West
}

and you have a method

public void GoDirection(Direction direction)
{
}

An enum is really just an Int32, with some additional compiler support for the syntax. The four values, since they weren't specified, are North=0, East=1, South=2, and West=3. With a cast, however, any valid Int32 value can be passed into the GoDirection method. So most often, you would pass in one of the four directions, like this:

GoDirection(Direction.East);

But you could just as well do this:

GoDirection((Direction)200);

Most often, if a value that isn't one of the four values is passed in, it is a bug. It would help if a Code Contract verified that it was one of the four values. You can apply a contract to verify that the value passed in is one of the four like this:

public void GoDirection(Direction direction)
{
    Contract.Requires<ArgumentOutOfRangeException>(
        Enum.IsDefined(typeof(Direction), direction);
    // other code
}

This causes the Contract not to validate if the direction is not one of the four given by the Direction enum (so passing in 200 is an error). Your Code Contract settings (in the Project Properties) will determine when this verification happens. By default this verification would only happen in Debug builds, and not even compiled into the code on Release builds, but you can change that if you like.