Wednesday, September 30, 2009

Sample StyleCop custom rules


Methods should not have more then 200 lines of code:


private void MethodShouldNotHaveMoreThanTwoHundredLines(CsElement element, CsElement parentElement)
        {
            if ((!element.Generated && (element.Declaration != null)) && (element.Declaration.Name != null))
            {
                switch (element.ElementType)
                {
                    case ElementType.Method:
                        //Checking 203 line as first line for method declaration and two lines for opening and closing parenthesis '{','}'
                        if (element.Location.LineSpan > 203)
                        {
                            base.AddViolation(element, "MethodShouldNotHaveMoreThanTwoHundredLines");
                        }
                        break;
                }
            }

        }



Prefix Private variables with underscore:


private void PrefixPrivateVariableNameWithUnderScore(CsElement element, CsElement parentElement)
        {
            // Flag a violation if the instance variables are not prefixed with an underscore.
            if (!element.Generated && element.ElementType == ElementType.Field && element.ActualAccess == AccessModifierType.Private &&
                 element.Declaration.Name.ToCharArray()[0] != '_')
            {
                //If field type is "const" than Pascal casing rule is applicable on it.
                if (((Field)element).Const) return;

                base.AddViolation(element, "PrefixPrivateVariableNameWithUnderScore");
            }
            else if (!element.Generated && element.ElementType == ElementType.Field && element.ActualAccess == AccessModifierType.Private
                && CheckCase(element.Declaration.Name.Substring(1), false))
            {
                base.AddViolation(element, "PrefixPrivateVariableNameWithUnderScore");
            }

        }



Avoid specifying type for Enum:


private void AvoidSpecifyingEnumType(CsElement element, CsElement parentElement)
        {
            if (element.ElementType == ElementType.Enum)
            {
                Microsoft.StyleCop.CSharp.Enum enm = (Microsoft.StyleCop.CSharp.Enum)element;
                if (enm.BaseType != null)
                {
                    base.AddViolation(element, "AvoidSpecifyingEnumType", new object[0]);
                }
            }
        }



Camel casing for local variable names and method arguments:


private void CamelCasingForMethodArgumentsAndLocalVariable(CsElement element, CsElement parentElement)
        {
            if (element.ElementType == ElementType.Method)
            {
                foreach (Variable tempVariable in element.Variables)
                {
                    //If argument type is 'EventArgs' allow non-Camel case variable name also.
                    if (!tempVariable.Type.Text.ToLower().EndsWith("EventArgs".ToLower()))
                    {
                        if (CheckFieldUnderscores(tempVariable.Name) || CheckCase(tempVariable.Name, false))
                        {
                            base.AddViolation(element, tempVariable.Location.LineNumber, "CamelCasingForMethodArgumentsAndLocalVariable");

                        }
                    }
                }
            }


        }



Avoid abbrivated character variable names, such as i, num, tmp:


private void AvoidAbbrivatedCharacterVariableNames(CsElement element, CsElement parentElement)
        {
            if (element.ElementType == ElementType.Field)
            {
                if (element.Declaration.Name.Length <= 3)
                    base.AddViolation(element, "AvoidAbbrivatedCharacterVariableNames", new object[0]);
            }
            if (element.ElementType == ElementType.Method)
            {
                foreach (Variable elem in element.Variables)
                {
                    if (!elem.Type.Text.ToLower().EndsWith("EventArgs".ToLower()))
                    {
                        if (elem.Name.Length <= 3)
                            base.AddViolation(element, elem.Location.LineNumber, "AvoidAbbrivatedCharacterVariableNames", new object[0]);
                    }
                }
            }   
        }



Using directive inside namespace:


private void UsingDirectiveOutSideOfNameSpace(CsElement element, CsElement parentElement)
        {
            if (!element.Generated && (element.ElementType == ElementType.UsingDirective))
            {
                // CsElement parentElement = element.ParentElement;
                if ((parentElement != null) && (parentElement.ElementType == ElementType.Namespace))
                {

                    AddViolation(element, "UsingDirectiveShouldOutSideOfNameSpace");

                }
            }
        }

4 comments:

  1. What does CheckFieldUnderscores and CheckCase refer to here?

    if (CheckFieldUnderscores(tempVariable.Name) || CheckCase(tempVariable.Name, false))

    ReplyDelete
  2. Sorry for the late response :(
    I was not able to follow the blog due to hectic work...

    CheckCase --> Checks the case of the first character in the given word

    CheckFieldUnderscore --> Checks the field name to look for underscores

    These are in-built functions in StyleCop

    ReplyDelete
  3. Hi,

    I had created a sample styleCop project which got compiled without errors, but I can't get Microsoft StyleCop to pick the rule. Is there any other setting to integrate our custom rule in StyleCop?

    Thanks,
    Jackson C.

    ReplyDelete
  4. Hello,

    Yours pieces of code are what made me wanting to write the rules that until a few weeks ago, was validating by hand.

    My entire rule package is available there: http://code.google.com/p/cleancodersstylecoprules/

    Thanxs a bunch for your code.

    Jeff

    ReplyDelete