Introduction
In the previous notes, we analyzed the source files, relying entirely on the syntax tree and traversing its nodes. In many cases, traversing a single syntax tree is not enough to perform deep analysis. Here the concept ''semantic model'' comes in handy. A semantic model opens up new possibilities for analysis. Great responsibility comes with great power. Therefore, you should know about some features of using a semantic model. This article will cover these points.
Semantic model and symbols
A semantic model provides information about various types of entities, such as methods, local variables, fields, properties, and so on. To get a correct semantic model, we need to compile the project. We'll need an object of the Compilation type for further work. One of the ways to get a compilation object is to call the GetCompilationAsync method of the Project class instance. Check out the previous note and find out how to get and use Project class instances.
Compilation compilation = project.GetCompilationAsync().Result;
SemanticModel model = compilation.GetSemanticModel(tree);
- symbols for getting information about the entity itself.
- symbols for getting information about the entity type.
string codeStr = @" using System; public class ParentClass { virtual public void Mehtod1() { Console.WriteLine(""Hello from Parent""); } } public class ChildClass: ParentClass { public override void Mehtod1() { Console.WriteLine(""Hello from Child""); } } class Program { static void Main(string[] args) { ChildClass childClass = new ChildClass(); childClass.Mehtod1(); } }"; SyntaxTree tree = SyntaxFactory.ParseSyntaxTree(codeStr); var msCorLib = MetadataReference .CreateFromFile(typeof(object).Assembly.Location); var compilation = CSharpCompilation.Create("MyCompilation", syntaxTrees: new[] { tree }, references: new[] { msCorLib }); var model = compilation.GetSemanticModel(tree); var methodInvocSyntax = tree.GetRoot().DescendantNodes() .OfType<InvocationExpressionSyntax>(); foreach (var methodInvocation in methodInvocSyntax) { var methodSymbol = model.GetSymbolInfo(methodInvocation).Symbol; if (methodSymbol.IsOverride) { //Apply your additional logic for analyzing method. } }
... var methodDeclarsSyntax = tree.GetRoot().DescendantNodes() .OfType<MethodDeclarationSyntax>(); ... foreach (var methodDeclaration in methodDeclarsSyntax) { bool isOverriden = methodDeclaration .Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.OverrideKeyword)); }
Getting information about an object. Specification of the symbol type
string codeStr = @" public class MyClass { public string MyProperty { get; } } class Program { static void Main(string[] args) { MyClass myClass = new MyClass(); myClass.MyProperty; } }"; SyntaxTree tree = SyntaxFactory.ParseSyntaxTree(codeStr); var msCorLib = MetadataReference .CreateFromFile(typeof(object).Assembly.Location); var compilation = CSharpCompilation.Create("MyCompilation", syntaxTrees: new[] { tree }, references: new[] { msCorLib }); var model = compilation.GetSemanticModel(tree); var propertyAccessSyntax = tree.GetRoot().DescendantNodes() .OfType<MemberAccessExpressionSyntax>() .First(); var symbol = model.GetSymbolInfo(propertyAccessSyntax).Symbol; if (symbol.Kind == SymbolKind.Property) { var pSymbol = (IPropertySymbol)symbol; var isReadOnly = pSymbol.IsReadOnly; //true var type = pSymbol.Type; // System.String }
How to get information about an object type
- ConvertedType returns information about an expression type after the implicit casting. If there isn't any casting, the returned value will be similar to the one that is returned by the Type property.
- Type returns the type of the expression given in the node. If it's not possible to get an expression type, we will get null. If the type cannot be defined because of some error, then the IErrorTypeSymbol interface is returned.
string codeStr = @" public class MyClass { public string MyProperty { get; set; } public MyClass(string value) { MyProperty = value; } }"; SyntaxTree tree = SyntaxFactory.ParseSyntaxTree(codeStr); var msCorLib = MetadataReference .CreateFromFile(typeof(object).Assembly.Location); var compilation = CSharpCompilation.Create("MyCompilation", syntaxTrees: new[] { tree }, references: new[] { msCorLib }); var model = compilation.GetSemanticModel(tree); var assignmentExpr = tree.GetRoot().DescendantNodes() .OfType<AssignmentExpressionSyntax>() .First(); ExpressionSyntax left = assignmentExpr.Left; var typeOfMyProperty = model.GetTypeInfo(left).Type;
- AllInterfaces - a list of all the interfaces that are implemented by the type. Interfaces implemented by the base types are also taken into account.
- BaseType - a base type.
- Interfaces - a list of interfaces implemented specifically by this type.
- IsAnonymousType - information on whether the type is anonymous.
Some comments on the use of the semantic model
- Learn Roslyn Now: Part 7 Introducing the Semantic Model – a great tutorial blog with examples of how to use Roslyn.
- Introduction to Roslyn and its use in program development - a decent introduction to general principles of static analysis on Roslyn from a developer of a well-known static analyzer.
Summary
Author Credit
Article Type : | Guest Article |
Author : | Ilya Gainulin |
Tags : | CSharp, Knowledge |
Article Date : | 04-02-2021 |
Article Publish Date : | 13-04-2021 |
Note : All content of this article are copyright of their author. |
Thank you for your valuable time, to read this article, If you like this article, please share this article and post your valuable comments.
Once, you post your comment, we will review your posted comment and publish it. It may take a time around 24 business working hours.
Sometimes I not able to give detailed level explanation for your questions or comments, if you want detailed explanation, your can mansion your contact email id along with your question or you can do select given checkbox "Notify me" the time of write comment. So we can drop mail to you.
If you have any questions regarding this article/blog you can contact us on info.codingvila@gmail.com