Archive

Posts Tagged ‘Visual Studio 2010 Extensibility’

Extending FxCop Rules with fixing ability

June 28, 2011 14 comments

Whenever I see a tool telling a developer what to do to fix a problem and that problem is relatively easy to fix, I tend to label the creator of the tool as lazy. If you can find out a problem and know how to fix it, why don’t you offer the solution in place?

This is the case with static CodeAnalysis of Visual Studio. It issues FXCop rules to generate warnings/information in the form of "CAXXXX: Micorsoft.xxxx: the message”.

Wouldn’t it be nice to have the ability to right-click on the warning and tell it to fix it?

screenshot

Well, this is what this blog is about: extending FxCop rules with the ability to fix itself.

 

How it works

 

It’s actually very simple. We only have an ErrorItem object as input, retrieved from the ErrorList. An ErrorItem does not give you much more than textual information about the origin of the warning/error. No object linked to be used. Only the columns shown in the ErrorList are the properties of the ErrorItem. Luckely it shows the rule identity (CAXXXX). We can use this to look for the fix.

To gather the fixes we make use of an interface, IFixable.  The interface looks like this:

image

Members:

  • ErrorMessage: the VS Package will pass the description of the ErrorItem before calling the FixIt
  • IsBreaking: indicates when the fix is applied it will break the code
  • RuleIdentifier: here the implementor indicates the RuleId that will be fixed
  • FixIt(fileName, lineNr, Column): the actual fix. Notice nullable integers are used since the ErrorItem not always gives the needed information.

The VS Package has the responsibility of loading the assemblies and showing the command in the context menu. Some notifications (like when it is breaking) to the user are also added. It also provides a FileManager as a tool for loading and saving the file in the VS IDE.

The Package makes use of MEF to load the fixing assemblies. This means you’ll need to apply an export of the interface IFixable. The FileManager, mentioned above, can be imported using the IFileManager interface.

Example of an implementation:

using System.ComponentModel.Composition;
using Elbanique.FxCopRuleFixVSPackageLib;

namespace RecommendedRulesFixes.Design
{
    /// <summary>
    /// A delegate that handles a public or protected event does not have 
    /// the correct signature, return type, or parameter names.
    /// To fix a violation of this rule, correct the signature, 
    /// return type, or parameter names of the delegate. 
    /// </summary>
    [Export(typeof(IFixable))]
    public class CA1009 : IFixable
    {
        const string id = "CA1009";

        [Import(typeof(IFileManager))]
        private IFileManager _FileManager { get; set; }

        /// <summary>
        /// Gets the rule identifier.
        /// </summary>
        public string RuleIdentifier { get { return id; } }

        /// <summary>
        /// Gets a value indicating whether this instance is breaking.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is breaking; otherwise, <c>false</c>.
        /// </value>
        public bool IsBreaking { get { return true; } }

        /// <summary>
        /// Gets or sets the error message.
        /// </summary>
        /// <value>
        /// The error message.
        /// </value>
        public string ErrorMessage { get; set; }

        /// <summary>
        /// Fixes the rule.
        /// </summary>
        /// <param name="originalFile">The original file.</param>
        /// <param name="lineNr">The line nr.</param>
        /// <param name="column">The column.</param>
        /// <returns>
        /// True if success, otherwise false.
        /// </returns>
        public bool FixIt(string originalFile, int? lineNr, int? column)
        {
            if (_FileManager == null)
                return false;

            _FileManager.Open(originalFile);
            string contextLine = null;
            if (lineNr.HasValue)
            {
                contextLine = _FileManager.GetLine(lineNr.Value -1);
                string fixedLine = FixLine(contextLine);
                _FileManager.ReplaceLine(lineNr.Value - 1, fixedLine);
                _FileManager.Save();
                return true;
            }

            return false;
        }

        private string FixLine(string contextLine)
        {
            //should be of type:  
            //public delegate void AlarmEventHandler(object sender, EventArgs e);
            MethodParser mp = MethodParser.Create(contextLine);
            if (mp.Return != "void") 
                mp.Return = "void";

            if (mp.Arguments[0].Type != "object")
                mp.Arguments[0].Type = "object";
           
            //should check if the argument is of type EventArgument, 
            //no compiled code, thus no can do.

            return mp.ToString();
        }
    }
}

Before you can use the FxCopRuleFixVSPackageLib you have to install the VSPackage. You can find it here.

The location after installation is %USER%\AppData\Local\Microsoft\VisualStudio\10.0\Extensions\Elbanique\FxCopRuleFixVSPackage\1.0\

Now there is still the issue of register the fixing assembly in the MEF container. This can be done by adding a MefComponent to the extension.vsixmanifest file (also located in the installation directory).

For example, presume you created a RecommendedRulesFixes.dll stored on a local drive c:\FxCopExtensions\ this should become:

<?xml version="1.0" encoding="utf-8"?>
<Vsix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
  <Identifier Id="FxCopRuleFixVSPackage.Microsoft.87948990-c755-459d-b04d-4060ea76f5a4">
    <Name>FxCopRuleFixVSPackage</Name>
    <Author>Elbanique</Author>
    <Version>1.0</Version>
    <Description xml:space="preserve">This package is a tool that allows you to import (MEF) FxCop Rule repair assemblies. In the ErrorList, when selecting an CA-rule and an extension is found to fix it, the 'Fix It' item in the context menu will show up.</Description>
    <Locale>1033</Locale>
    <MoreInfoUrl>http://vsxexperience.net</MoreInfoUrl>
    <Icon>VS2010-FIXIT.png</Icon>
    <PreviewImage>screenshot.png</PreviewImage>
    <SupportedProducts>
      <VisualStudio Version="10.0">
        <Edition>Pro</Edition>
      </VisualStudio>
    </SupportedProducts>
    <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
  </Identifier>
  <References>
    <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
      <Name>Visual Studio MPF</Name>
    </Reference>
  </References>
  <Content>
    <VsPackage>FxCopRuleFixVSPackage.pkgdef</VsPackage>
    <MefComponent>FxCopRuleFixVSPackageLib.dll</MefComponent>
    <MefComponent>C:\FxCopExtensions\RecommendedRulesFixes.dll</MefComponent>
  </Content>
</Vsix>

 

— Any suggestions on improving the way of registering assemblies are welcome! —

NOTES:

  • You can imagine this is not the silver bullet because we have to work with a file as input. No CodeDOM or IL. Therefore, not all rules can be fixable. I’ve deliberately excluded the usage of EnvDTE or MPF when interfacing with fixing assemblies. If the future should prove the nenecessity, a supplementairy interface could be created passing the EnvDTE.
  • The package has an assembly (RecommendedRulesFixes.dll) included which – just for demo reasons – implements some CA rules (CA1009, CA1033, CA1305, CA2112, CA1801, CA2200). If there is enough response on this, I’ll consider to extend this assembly covering all CA rules.

Part 2 : Decide Approach

January 2, 2010 Leave a comment

In previous post I pointed out where I would be focussing on to achieve a better DX with VSX integration package:

  • Refactoring MPF in context of usability
  • Writing a new integration package project

Now, after some considerations made, I’ve decided to change my approach. What I realy want to achieve is to be able to present tools to fasten up the development and lower the threshold. Herefore I only need a integration package for the VSX integration package to automate common tasks.

Refactoring the MPF is a good plan, but actually, the only thing that should be refactored is the top-layer, the way MPF organizes itself. I’ve looked into refactoring it and notice that it tantamounts to change the way it registers components (commands, toolwindows, services). Also, the core of all is enclosed in one file, namely the Package class.

Refactoring would take a massive part of my time, so I looked around to see if someone else would have made the effort. I found a need project, http://vsxtra.codeplex.com/. This project does all the refactoring. I considered to use it as a base to create my own integration package. I will not use it because of an extra dependency into my package.

It is still my intention to write a new integration package project. I can start with an Integration package to later migrate to a seperate project template.

So to conclude: I will focus on wrap the most of the MPF with a thin layer to automate small pieces of functionality. This way of working allows me to gradually increase the scope and leverage very fast small pieces of automated task without impacting existing VSX packages.

I also named the project VSXL. So from now on I’ll will use this to address the Visual Studio Integration Package Integration Package.

First Post on VSX Experience

December 30, 2009 Leave a comment

As an early on developer, the VSX (former VSIP) always intreged me. Creating your own project template and extensions felt like a massive advandage in developer productivity. Reading around on the possibilities on extensibility encourage me several times to start developing packages and work out ideas that were buggling around in my head. Unfortunately,  each time, the learning curve was to big and time inhibited me to finialize the projects in mind.

Again, this year (2009), I was confronted with the same situation. Only this time I bogged down in VSX and started to free-up some time. On the way, I saw myself spending the most of the time creating registrationkeys and configuring vsct (command tables). This wasn’t very encouraging, especially considering building automation packages with a tool that actually doen’t do any automation except on creation of a package.

Out of this frustration, I took the intention to create me a VSX  package to extend the VS integration Package project with automated task. That’s what this blog will be about (in the first instance): how to achieve an integration package to support VSX automation.

Hope you’ll enjoy !

Oh yes, and a happy 2010 !

Follow

Get every new post delivered to your Inbox.