Roslyn CTP–Syntax Tree–Part II–Singleton Checker

Posted: November 20, 2011 in .NET, C#
Tags: ,

In the last post related to Roslyn we developed a very basic syntax walker. In this post we will see how we can use the same for code analysis and checking. As a good practice we generally try to avoid having instance members in a singleton class. This is true if you are making your business logic layer classes Singleton and would prefer to avoid mess up by multiple threads. In order to check that we need to check the following:

a) Whether the class has any instance fields or properties

b) Whether class is a singleton i.e having a private constructor

Let’s consider the following singleton class:

public class Test 
    { 
        private static Test instance = null; 
        private int i = 0; 
        private int J { get; set; } 
        private Test() 
        {

        } 
        public static Test GetInstance() 
        { 
            lock(typeof(Test)) 
            { 
                if(instance==null) 
                { 
                    instance = new Test(); 
                } 
            } 
            return instance; 
        } 
        public void M1() { } 
        public void M2() { } 
    }

Here there are three static variables but we will not consider one field or property with type Test. As this is essential for the basic Singleton implementation. We will use the following rules:

  • If the class is static then Singleton check is not required.
  • If class has any constructor which is not private then it’s not a valid singleton class.
  • If all the methods are static then the code will issue an warning to mark it as static
  • If there instance variables of type other than the type of Singleton class then it’ll flag an error
  • If there are more than one instance variable to type of Singleton class then it’ll flag an error

 

public class SingletonClassChecker 
{ 
     private bool isStatic = false; 
     private List<string> propertyFieldTypes = new List<string>();

     private List<string> methodStaticNonStatic = new List<string>(); 
     private int nonPrivateCnstrCounter = 0; 
     
     public void CheckSingletonClass(ClassDeclarationSyntax cds) 
     { 
         
         CollectData(cds); 
         AnalyzeData(cds);

     }

     private void CollectData(ClassDeclarationSyntax cds) 
     { 
         PropertyDeclarationSyntax prop = null; 
         FieldDeclarationSyntax field = null; 
         MethodDeclarationSyntax meth = null; 
         var hasPrivateConstructor = false; 
         var isStatic = false; 
         foreach (var md in cds.Modifiers) 
         { 
             if (md.GetText().Equals("static")) 
             { 
                 isStatic = true; 
                 break; 
             } 
         } 
         if (!isStatic) 
         { 
             foreach (var m in cds.Members) 
             { 
                 prop = m as PropertyDeclarationSyntax; 
                 field = m as FieldDeclarationSyntax; 
                 meth = m as MethodDeclarationSyntax; 
                 if (prop != null) 
                 { 
                     propertyFieldTypes.Add(prop.Type.PlainName); 
                 } 
                 if (field != null) 
                 { 
                     propertyFieldTypes.Add(field.Declaration.Type.PlainName); 
                 } 
                 if (m is ConstructorDeclarationSyntax) 
                 { 
                     hasPrivateConstructor = false; 
                     foreach (var cmd in (m as ConstructorDeclarationSyntax).Modifiers) 
                     { 
                         if (cmd.GetText().Equals("private")) 
                         { 
                             hasPrivateConstructor = true; 
                         } 
                     } 
                     if (!hasPrivateConstructor) nonPrivateCnstrCounter = nonPrivateCnstrCounter + 1; 
                 } 
                 if (meth!=null) 
                 { 
                     isStatic = false; 
                     foreach (var mm in meth.Modifiers) 
                     { 
                         if (mm.GetText().Equals("static")) isStatic = true; 
                     } 
                     if (isStatic == true) methodStaticNonStatic.Add("Static"); else methodStaticNonStatic.Add("Non Static"); 
                     

                 } 
             } 
         } 
     } 
     private void AnalyzeData(ClassDeclarationSyntax cds) 
     { 
         Console.WriteLine("Analyzing Class::" + cds.Identifier); 
         //If class is static then Singleton check is not required 
         if (isStatic) 
         { 
             Console.WriteLine("Static Class.Singleton check is not required"); 
         } 
         else 
         { 
             // If Class has non private constructor then it's not a singleton 
             if (nonPrivateCnstrCounter > 0) 
             { 
                 Console.WriteLine("Has non private constructor.Not a valid singleton.Singleton check is not required"); 
             } 
             else 
             { 
                 var count = methodStaticNonStatic.Count(p => p.Equals("static")); 
                 //If all methods are static then the class need not be a singleton 
                 if (count == methodStaticNonStatic.Count) 
                 { 
                     Console.WriteLine("Warning::All methods are static.Mark the class as static.Singleton not required."); 
                 } 
                 else 
                 { 
                     // If class contains instance variables other than singleton instance then there is an error 
                     if(propertyFieldTypes.Count(p=>!p.Equals(cds.Identifier.GetText()))>0) 
                     { 
                         Console.WriteLine("Error:: Singleton class with instance variables"); 
                     }// If class contains instance variables of same type but more than one then there is an error. 
                     else if (propertyFieldTypes.Count(p => p.Equals(cds.Identifier.GetText())) > 1) 
                     { 
                         Console.WriteLine("Error:: Singleton class with instance variables"); 
                     } 
                 } 
             }

         } 
     }
} 

This method is invoked as shown below:

static void Main(string[] args)
    {
        SyntaxTree tree = SyntaxTree.ParseCompilationUnit(@"namespace RoslynCTPDemo
                        {
                        public class Test
                        {
                            private static Test instance = null;
                            private int i = 0;
                            private int J { get; set; }
                            private Test()
                            {

                            }
                            public static Test GetInstance()
                            {
                                lock(typeof(Test))
                                {
                                    if(instance==null)
                                    {
                                        instance = new Test();
                                    }
                                }
                                return instance;
                            }
                            public void M1() { }
                            public void M2() { }
                        }
                    }");
        SingletonClassChecker sc = new SingletonClassChecker();
        var root = (CompilationUnitSyntax)tree.Root;
        foreach (var n in root.Members)
        {
            if (n is NamespaceDeclarationSyntax)
            {
                //Find Classes if inside namespace
                foreach (var c in (n as NamespaceDeclarationSyntax).Members)
                {
                    if (c is ClassDeclarationSyntax) sc.CheckSingletonClass(c as ClassDeclarationSyntax);
                }
            } // For classes with namespace declaration
            else if (n is ClassDeclarationSyntax)
            {
                sc.CheckSingletonClass(n as ClassDeclarationSyntax);
            }
        }
        Console.Read();
    }

The output is:

image

This example demonstrates the beauty, simplicity yet the power of Roslyn Syntax API. This small code snippet is converted to a suitable Visual Studio plug-in can serve the purpose of source code checker the way Resharper does that now.

Comments
  1. Roslyn CTP–Syntax Tree–Part II–Singleton Checker « Sankarsan’s Journal…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.