Home > FxCop > Extending FxCop Rules with fixing ability

Extending FxCop Rules with fixing ability

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.
About these ads
  1. Steve
    July 20, 2011 at 11:56 | #1

    Great stuff, just what I was looking for.

  2. Steve
    August 31, 2011 at 09:48 | #2

    I have been working on this and have implemented my own FixesLib containg an updated version of your original interface and an extension class that allows some basic LINQ querying of the source file using NRefactory to replace your file manager. Happy to share with you if you would like it.

    • August 31, 2011 at 10:17 | #3

      I’m very happy with your contribution! My FileManager was indeed somewhat ‘cheap’. Post me your code and I’ll update the extension on the Visual Studio gallery. I’ll give you the credits offcourse ;-)

  3. Steve
    August 31, 2011 at 11:45 | #4

    // ——————————————————————————————————————–
    //
    //
    //
    //
    // Interface describing a rule that can be fixed automatically by the Extenstion
    //
    // ——————————————————————————————————————–
    namespace Rules.FixesLib
    {
    ///
    /// Interface describing a rule that can be fixed automatically by the Extenstion
    ///
    public interface IFixableRule
    {
    ///
    /// Gets or sets the error message.
    ///
    ///
    /// The error message.
    ///
    string ErrorMessage { get; set; }

    ///
    /// Gets or sets the fixed line number.
    ///
    ///
    /// The fixed line number.
    ///
    ///
    /// This is the location in code where the fix has been applied, used to move the editor to the approximate position of the change
    ///
    int FixedLineNumber { get; set; }

    ///
    /// Gets or sets the fixed column number.
    ///
    ///
    /// The fixed column number.
    ///
    ///
    /// This is the location in code where the fix has been applied, used to move the editor to the approximate position of the change
    ///
    int FixedColumnNumber { get; set; }

    ///
    /// Gets a value indicating whether this instance is breaking rule.
    ///
    ///
    /// true if this instance is breaking rule; otherwise, false.
    ///
    bool IsBreakingRule { get; }

    ///
    /// Gets the rule identifier.
    ///
    string RuleIdentifier { get; }

    ///
    /// Fixes the violation.
    ///
    /// The original file.
    /// The line number.
    /// The column.
    /// string, containing the fixed source code
    string FixViolation(string originalFile, int? lineNumber, int? column);
    }
    }

  4. Steve
    August 31, 2011 at 11:46 | #5

    // ——————————————————————————————————————–
    //
    //
    //
    //
    // Extension methods to facilitate code parsing using LINQ & NRefactory
    //
    // ——————————————————————————————————————–
    namespace Rules.FixesLib
    {
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using ICSharpCode.NRefactory.Ast;

    ///
    /// Extension methods for processing an enumeration if INode
    ///
    public static class CodeParsingExtensions
    {
    ///
    /// The search criteria specified for a string literal search – Only used by FindStringLiteral
    ///
    private static string stringliteralSearchCriteria = string.Empty;

    ///
    /// The INodes matching the search criteria – Only used by FindStringLiteral
    ///
    private static List searchMatches = null;

    ///
    /// Finds the local variables with the specified variable name
    ///
    /// The source enumeration
    /// Delegate used when searching.
    /// Name of the variable to locate.
    /// IEnumerable containing INode that have the specified variable name (case sensitive)
    public static IEnumerable FindLocalVariables(this IEnumerable source, Func<INode, IEnumerable> searchDelegate, string variableName)
    {
    return source.Traverse(x => x.Children)
    .Where(x => x is LocalVariableDeclaration).Cast()
    .Where(x => x.GetVariableDeclaration(variableName) != null);
    }

    ///
    /// Finds the methods with the specified name
    ///
    /// The source enumeration to search
    /// Delegate used when searching.
    /// Name of the method (case sensitive)
    /// IEnumerable containing INode that have the specified method name
    public static IEnumerable FindMethods(this IEnumerable source, Func<INode, IEnumerable> searchDelegate, string methodName)
    {
    return source.Traverse(x => x.Children)
    .Where(x => x is MethodDeclaration).Cast()
    .Where(x => x.Name == methodName);
    }

    ///
    /// Finds the string literal
    ///
    /// The source enumeration to search
    /// Delegate used when searching.
    /// Content of the literal (case sensitive)
    /// IEnumerable of parent INodes containing the string literal.
    public static IEnumerable FindStringLiteral(this IEnumerable source, Func<INode, IEnumerable> searchDelegate, string literalContent)
    {
    stringliteralSearchCriteria = literalContent;

    // populated when expressions are evaluated
    searchMatches = new List();

    source.Traverse(x => x.Children).Where(x => x is ExpressionStatement).ToList();

    return searchMatches;
    }

    ///
    /// Evaluates an expression and all nested expressions
    ///
    /// The source enumeration
    /// INode containing an expression
    /// This is fairly ugly. In this instance the EvaluateExpression function is used only to support the FindStringLiteral method. It could be extended for more search methods if required.
    private static INode EvaluateExpression(Expression source)
    {
    ////ICSharpCode.NRefactory.Ast.QueryExpressionVB – unsupported right now

    if (source is AddressOfExpression)
    {
    AddressOfExpression a = source as AddressOfExpression;
    return EvaluateExpression(a.Expression);
    }
    else if (source is AnonymousMethodExpression)
    {
    AnonymousMethodExpression a = source as AnonymousMethodExpression;
    foreach (Expression parameterExpression in a.Parameters)
    {
    if (null != EvaluateExpression(parameterExpression))
    {
    return a.Parent;
    }
    }
    }
    else if (source is ArrayCreateExpression)
    {
    ArrayCreateExpression a = source as ArrayCreateExpression;

    if (null != EvaluateExpression(a.ArrayInitializer))
    {
    return a.Parent;
    }

    foreach (Expression argumentExpression in a.Arguments)
    {
    if (null != EvaluateExpression(argumentExpression))
    {
    return a.Parent;
    }
    }
    }
    else if (source is AssignmentExpression)
    {
    AssignmentExpression a = source as AssignmentExpression;

    if (null != EvaluateExpression(a.Left))
    {
    return a.Parent;
    }

    if (null != EvaluateExpression(a.Right))
    {
    return a.Parent;
    }
    }
    else if (source is BinaryOperatorExpression)
    {
    BinaryOperatorExpression b = source as BinaryOperatorExpression;

    if (null != EvaluateExpression(b.Left))
    {
    return b.Parent;
    }

    if (null != EvaluateExpression(b.Right))
    {
    return b.Parent;
    }
    }
    else if (source is CastExpression)
    {
    CastExpression c = source as CastExpression;

    if (null != EvaluateExpression(c.Expression))
    {
    return c.Parent;
    }
    }
    else if (source is CheckedExpression)
    {
    CheckedExpression c = source as CheckedExpression;
    if (null != EvaluateExpression(c.Expression))
    {
    return c.Parent;
    }
    }
    else if (source is CollectionInitializerExpression)
    {
    CollectionInitializerExpression c = source as CollectionInitializerExpression;
    foreach (Expression createExpression in c.CreateExpressions)
    {
    if (null != EvaluateExpression(createExpression))
    {
    return c.Parent;
    }
    }
    }
    else if (source is ConditionalExpression)
    {
    ConditionalExpression c = source as ConditionalExpression;

    if (null != EvaluateExpression(c.Condition))
    {
    return c.Parent;
    }

    if (null != EvaluateExpression(c.TrueExpression))
    {
    return c.Parent;
    }

    if (null != EvaluateExpression(c.FalseExpression))
    {
    return c.Parent;
    }
    }
    else if (source is DirectionExpression)
    {
    DirectionExpression d = source as DirectionExpression;
    if (null != EvaluateExpression(d.Expression))
    {
    return d.Parent;
    }
    }
    else if (source is InvocationExpression)
    {
    InvocationExpression i = source as InvocationExpression;

    if (null != EvaluateExpression(i.TargetObject))
    {
    return i.Parent;
    }

    foreach (Expression argumentExpression in i.Arguments)
    {
    if (null != EvaluateExpression(argumentExpression))
    {
    return i.Parent;
    }
    }
    }
    else if (source is IdentifierExpression)
    {
    IdentifierExpression id = source as IdentifierExpression;
    }
    else if (source is IndexerExpression)
    {
    IndexerExpression id = source as IndexerExpression;
    foreach (Expression indexExpression in id.Indexes)
    {
    if (null != EvaluateExpression(indexExpression))
    {
    return id.Parent;
    }
    }
    }
    else if (source is LambdaExpression)
    {
    LambdaExpression l = source as LambdaExpression;
    if (null != EvaluateExpression(l.ExpressionBody))
    {
    return l.Parent;
    }
    }
    else if (source is MemberInitializerExpression)
    {
    MemberInitializerExpression m = source as MemberInitializerExpression;
    if (null != EvaluateExpression(m.Expression))
    {
    return m.Parent;
    }
    }
    else if (source is MemberReferenceExpression)
    {
    MemberReferenceExpression m = source as MemberReferenceExpression;
    if (null != EvaluateExpression(m.TargetObject))
    {
    return m.Parent;
    }
    }
    else if (source is NamedArgumentExpression)
    {
    NamedArgumentExpression n = source as NamedArgumentExpression;
    if (null != EvaluateExpression(n.Expression))
    {
    return n.Parent;
    }
    }
    else if (source is ObjectCreateExpression)
    {
    ObjectCreateExpression obj = source as ObjectCreateExpression;
    if (null != EvaluateExpression(obj.ObjectInitializer))
    {
    return obj.Parent;
    }

    foreach (Expression parameterExpression in obj.Parameters)
    {
    if (null != EvaluateExpression(parameterExpression))
    {
    return obj.Parent;
    }
    }
    }
    else if (source is ParameterDeclarationExpression)
    {
    ParameterDeclarationExpression p = source as ParameterDeclarationExpression;

    if (null != EvaluateExpression(p.DefaultValue))
    {
    return p.Parent;
    }
    }
    else if (source is ParenthesizedExpression)
    {
    ParenthesizedExpression p = source as ParenthesizedExpression;
    if (null != EvaluateExpression(p.Expression))
    {
    return p.Parent;
    }
    }
    else if (source is PrimitiveExpression)
    {
    PrimitiveExpression p = source as PrimitiveExpression;

    if (p.StringValue == string.Format(“\”{0}\””, stringliteralSearchCriteria))
    {
    searchMatches.Add(p.Parent);
    return p.Parent;
    }
    }
    else if (source is TypeOfIsExpression)
    {
    TypeOfIsExpression t = source as TypeOfIsExpression;
    if (null != EvaluateExpression(t.Expression))
    {
    return t.Parent;
    }
    }
    else if (source is UnaryOperatorExpression)
    {
    UnaryOperatorExpression u = source as UnaryOperatorExpression;
    if (null != EvaluateExpression(u.Expression))
    {
    return u.Parent;
    }
    }
    else if (source is UncheckedExpression)
    {
    UncheckedExpression u = source as UncheckedExpression;
    if (null != EvaluateExpression(u.Expression))
    {
    return u.Parent;
    }
    }
    else if (source is XmlAttributeExpression)
    {
    XmlAttributeExpression x = source as XmlAttributeExpression;
    if (null != EvaluateExpression(x.ExpressionValue))
    {
    return x.Parent;
    }
    }
    else if (source is XmlContentExpression)
    {
    XmlContentExpression x = source as XmlContentExpression;
    }
    else if (source is XmlDocumentExpression)
    {
    XmlDocumentExpression x = source as XmlDocumentExpression;
    foreach (XmlExpression xmlExpression in x.Expressions)
    {
    if (null != EvaluateExpression(xmlExpression))
    {
    return x.Parent;
    }
    }
    }
    else if (source is XmlElementExpression)
    {
    XmlElementExpression x = source as XmlElementExpression;
    if (null != EvaluateExpression(x.Content))
    {
    return x.Parent;
    }

    if (null != EvaluateExpression(x.NameExpression))
    {
    return x.Parent;
    }
    }
    else if (source is XmlEmbeddedExpression)
    {
    XmlEmbeddedExpression x = source as XmlEmbeddedExpression;
    if (null != EvaluateExpression(x.InlineVBExpression))
    {
    return x.Parent;
    }
    }
    else if (source is XmlMemberAccessExpression)
    {
    XmlMemberAccessExpression x = source as XmlMemberAccessExpression;
    if (null != EvaluateExpression(x.TargetObject))
    {
    return x.Parent;
    }
    }
    else if (source is BaseReferenceExpression || source is ClassReferenceExpression ||
    source is DefaultValueExpression || source is QueryExpression || source is TypeOfExpression ||
    source is TypeReferenceExpression || source is ThisReferenceExpression ||
    source is SizeOfExpression || source is XmlExpression)
    {
    // nothing to do
    }
    else if (“NullCollectionInitializerExpression” == source.GetType().Name || “NullExpression” == source.GetType().Name)
    {
    // cant do anything with this type – it is inaccessible
    }

    return null;
    }

    ///
    /// Traverses the code tree, navigating all INodes
    ///
    /// The source enumeration
    /// Delegate used when searching.
    /// All INodes in a source file ready to be filtered by the appropriate “Find” method.
    /// Not all statement types have been implemented as yet. I am implementing on a as needed basis.
    private static IEnumerable Traverse(this IEnumerable source, Func<INode, IEnumerable> searchDelegate)
    {
    foreach (INode item in source)
    {
    yield return item;

    IEnumerable seqRecurse = null;

    if (item is MethodDeclaration)
    {
    MethodDeclaration method = item as MethodDeclaration;
    seqRecurse = searchDelegate(method.Body);
    }
    else if (item is IfElseStatement)
    {
    IfElseStatement ifStatement = item as IfElseStatement;

    foreach (Statement statement in ifStatement.TrueStatement)
    {
    if (statement is BlockStatement)
    {
    BlockStatement blockStatement = statement as BlockStatement;

    foreach (INode itemRecurse in Traverse(blockStatement.Children, searchDelegate))
    {
    yield return itemRecurse;
    }
    }
    }

    if (ifStatement.HasElseStatements)
    {
    foreach (Statement statement in ifStatement.FalseStatement)
    {
    if (statement is BlockStatement)
    {
    BlockStatement blockStatement = statement as BlockStatement;

    foreach (INode itemRecurse in Traverse(blockStatement.Children, searchDelegate))
    {
    yield return itemRecurse;
    }
    }
    }
    }

    if (ifStatement.HasElseIfSections)
    {
    foreach (ElseIfSection elseIfSection in ifStatement.ElseIfSections)
    {
    foreach (INode itemRecurse in Traverse(elseIfSection.EmbeddedStatement.Children, searchDelegate))
    {
    yield return itemRecurse;
    }
    }
    }
    }
    else if (item is SwitchStatement)
    {
    foreach (SwitchSection switchSection in (item as SwitchStatement).SwitchSections)
    {
    foreach (INode itemRecurse in Traverse(switchSection.Children, searchDelegate))
    {
    yield return itemRecurse;
    }
    }
    }
    else if (item is StatementWithEmbeddedStatement)
    {
    StatementWithEmbeddedStatement embeddedStatement = item as StatementWithEmbeddedStatement;
    foreach (INode itemRecurse in Traverse(embeddedStatement.EmbeddedStatement.Children, searchDelegate))
    {
    yield return itemRecurse;
    }
    }
    else if (item is TryCatchStatement)
    {
    TryCatchStatement tryCatch = item as TryCatchStatement;

    foreach (INode itemRecurse in Traverse(tryCatch.StatementBlock.Children, searchDelegate))
    {
    yield return itemRecurse;
    }

    foreach (CatchClause catchClause in tryCatch.CatchClauses)
    {
    foreach (INode itemRecurse in Traverse(catchClause.StatementBlock.Children, searchDelegate))
    {
    yield return itemRecurse;
    }
    }

    foreach (INode itemRecurse in Traverse(tryCatch.FinallyBlock.Children, searchDelegate))
    {
    yield return itemRecurse;
    }
    }
    else if (item is ExpressionStatement)
    {
    INode node = EvaluateExpression(((ExpressionStatement)item).Expression);

    if (null != node)
    {
    yield return node;
    }
    }
    else
    {
    seqRecurse = searchDelegate(item);
    }

    if (seqRecurse != null)
    {
    foreach (INode itemRecurse in Traverse(seqRecurse, searchDelegate))
    {
    yield return itemRecurse;
    }
    }
    }
    }
    }
    }

  5. Steve
    August 31, 2011 at 11:50 | #6

    Sorry about the huge post! Finally, the usage;

    var variableDeclarations =
    parser.CompilationUnit.Children
    .FindMethods(x => x.Children, methodName)
    .FindLocalVariables(x => x.Children, variableName)
    .OrderByDescending(x => x.StartLocation.Line)
    .ToList();

    The above statement finds a specified variable within the specified method name. I then use the list within a for loop as below;

    foreach (var localVariableDeclaration in variableDeclarations)
    {
    INode parentNode = localVariableDeclaration.Parent;

    if (parentNode is BlockStatement && parentNode.StartLocation.Line = lineNumber)
    {
    IdentifierExpression identifierExpression = new IdentifierExpression(variableName);
    MemberReferenceExpression memberReferenceExpression = new MemberReferenceExpression(identifierExpression, “Dispose”);
    InvocationExpression invocationExpression = new InvocationExpression(memberReferenceExpression);

    ExpressionStatement expressionStatement = new ExpressionStatement(invocationExpression);

    INode insertBefore = this.GetInsertionStatement(parentNode, localVariableDeclaration.StartLocation.Line);

    if (insertBefore == null)
    {
    parentNode.Children.Add(expressionStatement);
    }
    else
    {
    parentNode.Children.Insert(parentNode.Children.IndexOf(insertBefore), expressionStatement);
    }

    this.FixedLineNumber = parentNode.EndLocation.Line;
    this.FixedColumnNumber = parentNode.EndLocation.Column;
    }
    }

    This particular example adds a call to Dispose into the code for the variable specified in variableName.

    Hope that all makes sense!

    • September 23, 2011 at 12:45 | #7

      Hey Steve,
      I finally found some time to look into your contribution. It’s a pitty but I will not incorporate your solution and this for the very simple reason that it has too much dependencies to third party assemblies. NRefactory -> mono.cecil and to be able to build this I must install NUnit. I notice that you are favorit to open source software. I don’t hold anything against it but when you want to build a package you want to prevent including to much assemblies. If every contributor would port their 3th party assembly, sooner or later you will end-up with a version clash. I like clean solutions, thus only the tools VS/MS offers.

  6. Steve
    September 21, 2011 at 16:40 | #8

    Hi Jan,

    Me again! I have been working on the extensibility side of this add-in now. I have found that if you change the ComposeContainer method to;

    AggregateCatalog cat = new AggregateCatalog();
    CompositionContainer _container;

    protected void ComposeContainer()
    {
    FileInfo assembly = new FileInfo(Assembly.GetExecutingAssembly().Location);

    this.cat.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
    this.cat.Catalogs.Add(new DirectoryCatalog(assembly.Directory + “\\extensions”));

    _container = new CompositionContainer(this.cat);

    //Fill the imports of this object
    try
    {
    this._container.ComposeParts(this);
    }
    catch (CompositionException compositionException)
    {
    Console.WriteLine(compositionException.ToString());
    }
    }

    Then you can drop assemblies into the extensions sub-folder without having to register them as MEF components. It works equally well with your suggested “C:\FxCopExtensions” too.

    Hope you find this useful.

    Steve

    • September 23, 2011 at 13:25 | #9

      Hey Steve,

      I do use this feature but you are forgetting there is already a container created by VS. You can get to it by resolving the IComponentModel through the ServiceProvider:
      IComponentModel componentModel = GetService(typeof(SComponentModel)) as IComponentModel;

      Then you can create your DirectoryCatalog and aggregate it with the existing one:
      DirectoryCatalog directoryCatalog = ...;
      AggregateCatalog = new AggregateCatelog(componentModel.DefaultCatalog, directoryCatalog);

      You have to make use of a CompositionBatch (allows you to dynamically add/remove parts):
      CompositionBatch compositionBatch = new CompositionBatch();
      CompositionContainer container = new CompositionContainer(aggregateCatalog);
      container.Compose(compositionBatch);

      And your done.

  7. Steve
    September 30, 2011 at 10:09 | #10

    Jan Kinable :
    Hey Steve,
    I finally found some time to look into your contribution. It’s a pitty but I will not incorporate your solution and this for the very simple reason that it has too much dependencies to third party assemblies. NRefactory -> mono.cecil and to be able to build this I must install NUnit. I notice that you are favorit to open source software. I don’t hold anything against it but when you want to build a package you want to prevent including to much assemblies. If every contributor would port their 3th party assembly, sooner or later you will end-up with a version clash. I like clean solutions, thus only the tools VS/MS offers.

    Not a problem. I share your opinion for the most part. When I get some free time I am planning on refactoring NRefactory out for the reasons you specify.

    Also, thanks for the tip about the SComponentModel service, I completely missed that!

  8. May 18, 2012 at 01:20 | #11

    One correction – path to extension is %USERPROFILE%\AppData\Local\Microsoft\VisualStudio\10.0\Extensions\Elbanique

    Interested to hear if any more work has been done on this extension, and does it have a home (eg. codeplex, github etc)?

    -dave

  9. May 18, 2012 at 01:21 | #12

    One minor correction – path to extension is %USERPROFILE%\AppData\Local\Microsoft\VisualStudio\10.0\Extensions\Elbanique

    Also, has there been any more work done on this, and does it have a public home somewhere (eg. github, codeplex?)

    -dave

    • May 18, 2012 at 08:10 | #13

      Hey David,

      First, thanks for your interest and the remark about the path.

      I think it’s not the effort worth to spent much time in the expansion of this extension since it will be embedded in the VS 2011 version. If you’re not planning to use VS2011 in the near future I can always deliver you the source code. You can email me at jankinable@hotmail.com.

      Jan.

  10. Lucifer
    July 9, 2013 at 13:59 | #15

    Great, to bad i have to use FxCop 1.36 with Visual Studio 2005 in 2013.
    But great to know for working at home, where we can be working professionaly without the intervention of Noobs like my boss.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: