docs/wiki/Getting-Started-VB-Semantic-Analysis.md
Today, the Visual Basic and C# compilers are black boxes - text goes in and bytes come out - with no transparency into the intermediate phases of the compilation pipeline. With the .NET Compiler Platform (formerly known as "Roslyn"), tools and developers can leverage the exact same data structures and algorithms the compiler uses to analyze and understand code with confidence that that information is accurate and complete.
In this walkthrough we'll explore the Symbol and Binding APIs. The Syntax API exposes the parsers, the syntax trees themselves, and utilities for reasoning about and constructing them.
The Syntax API allows you to look at the structure of a program. However, often you'll want richer information about the semantics or meaning of a program. And while a loose code file or snippet of VB or C# code can be syntactically analyzed in isolation it's not very meaningful to ask questions such as "what's the type of this variable" in a vacuum. The meaning of a type name may be dependent on assembly references, namespace imports, or other code files. That's where the Compilation class comes in.
A Compilation is analogous to a single project as seen by the compiler and represents everything needed to compile a Visual Basic or C# program such as assembly references, compiler options, and the set of source files to be compiled. With this context you can reason about the meaning of code. Compilations allow you to find Symbols - entities such as types, namespaces, members, and variables which names and other expressions refer to. The process of associating names and expressions with Symbols is called Binding.
Like SyntaxTree, Compilation is an abstract class with language-specific derivatives. When creating an instance of Compilation you must invoke a factory method on the VisualBasicCompilation (or CSharpCompilation) class.
This example shows how to create a Compilation by adding assembly references and source files. Like the syntax trees, everything in the Symbols API and the Binding API is immutable.
Option Strict Off
Module Module1
Sub Main()
Dim tree = VisualBasicSyntaxTree.ParseText(
"Imports System
Imports System.Collections.Generic
Imports System.Text
Namespace HelloWorld
Class Class1
Shared Sub Main(args As String())
Console.WriteLine(""Hello, World!"")
End Sub
End Class
End Namespace")
Dim root As CompilationUnitSyntax = tree.GetRoot()
End Sub
End Module
Dim compilation As Compilation =
VisualBasicCompilation.Create("HelloWorld").
AddReferences(MetadataReference.CreateFromAssembly(
GetType(Object).Assembly)).
AddSyntaxTrees(tree)
Once you have a Compilation you can ask it for a SemanticModel for any SyntaxTree contained in that Compilation. SemanticModels can be queried to answer questions like "What names are in scope at this location?" "What members are accessible from this method?" "What variables are used in this block of text?" and "What does this name/expression refer to?"
This example shows how to obtain a SemanticModel object for our HelloWorld SyntaxTree. Once the model is obtained, the name in the first Imports statement is bound to retrieve a Symbol for the System namespace.
Dim model = compilation.GetSemanticModel(tree)
Dim firstImport As SimpleImportsClauseSyntax =
root.Imports(0).ImportsClauses(0)
Dim nameInfo = model.GetSymbolInfo(firstImport.Name)
Dim systemSymbol As INamespaceSymbol = nameInfo.Symbol
Execute this statement and examine the systemSymbol variable using the debugger datatips.
Stop the program.
For Each ns In systemSymbol.GetNamespaceMembers()
Console.WriteLine(ns.Name)
Next
Collections
Configuration
Deployment
Diagnostics
Globalization
IO
Reflection
Resources
Runtime
Security
StubHelpers
Text
Threading
Press any key to continue . . .
The previous example showed how to bind name to find a Symbol. However, there are other expressions in a VB program that can be bound that aren't names. This example shows how binding works with other expression types - in this case a simple string literal.
Dim helloWorldString = root.DescendantNodes().
OfType(Of LiteralExpressionSyntax)().
First()
Start debugging the program.
Add the following code to get the TypeInfo for this expression:
Dim literalInfo = model.GetTypeInfo(helloWorldString)
Stop the program.
Add the following code to enumerate the public methods of the System.String class which return strings and print their names to the Console:
Dim stringTypeSymbol As INamedTypeSymbol = literalInfo.Type
Dim methodNames = From method In stringTypeSymbol.GetMembers().
OfType(Of IMethodSymbol)()
Where method.ReturnType.Equals(stringTypeSymbol) AndAlso
method.DeclaredAccessibility = Accessibility.Public
Select method.Name Distinct
Console.Clear()
For Each name In methodNames
Console.WriteLine(name)
Next
Join
Substring
Trim
TrimStart
TrimEnd
Normalize
PadLeft
PadRight
ToLower
ToLowerInvariant
ToUpper
ToUpperInvariant
ToString
Insert
Replace
Remove
Format
Copy
Concat
Intern
IsInterned
Press any key to continue . . .
Option Strict Off
Module Module1
Sub Main()
Dim tree = VisualBasicSyntaxTree.ParseText(
"Imports System
Imports System.Collections.Generic
Imports System.Text
Namespace HelloWorld
Class Class1
Shared Sub Main(args As String())
Console.WriteLine(""Hello, World!"")
End Sub
End Class
End Namespace")
Dim root As CompilationUnitSyntax = tree.GetRoot()
Dim compilation As Compilation =
VisualBasicCompilation.Create("HelloWorld").
AddReferences(MetadataReference.CreateFromAssembly(
GetType(Object).Assembly)).
AddSyntaxTrees(tree)
Dim model = compilation.GetSemanticModel(tree)
Dim firstImport As SimpleImportsClauseSyntax =
root.Imports(0).ImportsClauses(0)
Dim nameInfo = model.GetSymbolInfo(firstImport.Name)
Dim systemSymbol As INamespaceSymbol = nameInfo.Symbol
For Each ns In systemSymbol.GetNamespaceMembers()
Console.WriteLine(ns.Name)
Next
Dim helloWorldString = root.DescendantNodes().
OfType(Of LiteralExpressionSyntax)().
First()
Dim literalInfo = model.GetTypeInfo(helloWorldString)
Dim stringTypeSymbol As INamedTypeSymbol = literalInfo.Type
Dim methodNames = From method In stringTypeSymbol.GetMembers().
OfType(Of IMethodSymbol)()
Where method.ReturnType.Equals(stringTypeSymbol) AndAlso
method.DeclaredAccessibility = Accessibility.Public
Select method.Name Distinct
Console.Clear()
For Each name In methodNames
Console.WriteLine(name)
Next
End Sub
End Module