
Kevin Erath
Geschäftsführer
Veröffentlicht am
22. Mai 2021

Vor kurzem habe ich mithilfe von Roslyn gezeigt, wie ein Quellcode nach Ausnahmen durchsucht werden kann. Hierzu habe ich den Syntaxbaum von Roslyn verwendet. Dieser liefert Informationen zum lexikalischen und syntaktischen Aufbau eines Quellcodes. Möchte man weitere Details über einen Syntax-Knoten erhalten, so ist dieser aber nicht ausreichend. Im genannten Beispiel war es z.B. nicht möglich den Namensraum der geworfenen Ausnahmen zu ermitteln. Um an diese Informationen zu kommen wird das semantische Modell benötigt. Das semantische Modell wird von Roslyn während des Kompiliervorgangs erstellt.
Dies klingt allerdings komplizierter als es ist. So kann das semantische Modell durch Aufruf der Methode GetSemanticModelAsync()
für ein Dokument abgerufen werden.
var filename = ...;
var ws = MSBuildWorkspace.Create();
var project = ws.OpenProjectAsync(filename).Result;
var document = project.Documents.First();
var model = document.GetSemanticModelAsync().Result;
Das semantische Modell besteht aus Symbolen. Ein Symbol entspricht dabei jeweils einem vom Compiler gefundenem Element, wie z.B. einer Klasse, Variablen oder Methode. Die enthaltenen Informationen unterscheiden sich zwar je nach Element, aber es gibt auch Informationen die bei allen Symbolen zur Verfügung stehen. Dies sind beispielsweise der Ort (Stelle im Quellcode bzw. in einem externen Verweis, sowie der umschließende Namensraum) oder Modifikatoren wie public
, protected
, static
oder abstract
. Weitere Informationen kommen dann je nach Symboltyp hinzu. So hat ein Symbol für eine Methode Informationen über die Übergabeparameter und dessen Rückgabewert.
Um das Symbol für einen Syntax-Knoten zu erhalten, reicht es eine Methode auf dem semantischen Modell aufzurufen. Je nach Art des Syntax-Knotens muss zwischen den beiden Methoden GetDeclaredSymbol()
und GetSymbolInfo()
unterschieden werden. Erstere dient zum Abrufen des Symbols für Deklarationen wie z.B. einer Klassen- oder Methodendeklaration. Die zweite Methode kommt dann zum Einsatz, wenn das Symbol für einen Ausdruck, wie einen Methodenaufruf, abgefragt werden soll.
Das kürzlich gezeigte Programm zur Auflistung von Ausnahmen lässt sich leicht, um die Fähigkeit den Namensraum anzuzeigen, erweitern. Hierzu muss zuerst die Klasse DocumentInfo
um die Eigenschaft Model
für das Speichern des SemanticModel
erweitert werden:
public class DocumentInfo
{
public SyntaxNode Node { get; set; }
public SemanticModel Model { get; set; }
}
Diese Eigenschaft kann dann in der Methode OpenAllDocuments()
mit dem entsprechenden Modell befüllt werden:
private static IEnumerable<DocumentInfo> OpenAllDocuments(
IEnumerable<Project> projects)
{
var documents = projects.SelectMany(x => x.Documents);
return documents.Select(x => new DocumentInfo {
Node = x.GetSyntaxRootAsync().Result,
Model = x.GetSemanticModelAsync().Result});
}
Als letztes wird die Methode FindExceptions()
angepasst. Diese soll nun über das Modell den Namensraum der Ausnahme ermitteln. Bisher wurde dort nur der Name über den Syntax-Knoten ermittelt:
...
foreach (var stmt in throwStatements)
{
var objCreation = stmt.DescendantNodes().
OfType<ObjectCreationExpressionSyntax>().FirstOrDefault();
if (objCreation != null)
{
var model = element.Parent.Document.Model;
var symbolInfo = model.GetSymbolInfo(objCreation);
var type = symbolInfo.Symbol.ContainingType;
nodeDesc.Add(type.ContainingNamespace + "." + type.Name);
}
}
...
Natürlich ist der Quellcode des kompletten Programms wieder auf Github verfügbar. Ich hoffe, ich konnte einen kleinen Einblick in das semantische Modell geben.

Hier schreibt
Kevin Erath
Als Mitbegründer und Geschäftsführer von pep.digital verbringe ich zwar nicht mehr jeden Tag ausschließlich damit, coole Lösungen für unsere Kunden zu realisieren. Trotzdem finde ich immer wieder die Zeit, mich auch mal tiefer in die Technik einzutauchen und meine Erkenntnisse hier im Blog zu teilen. Und ehrlich gesagt, das Unternehmen und unsere tollen Mitarbeiter:innen weiterzuentwickeln, macht mir mindestens genauso viel Spaß.
Quellen
Weitere interessante Artikel
Wir möchten hier nicht nur über Neuigkeiten aus dem Unternehmen berichten, sondern auch das Wissen und die Erfahrung unserer Experten teilen.

ULID – die bessere UUID
Immer wieder stehen Entwickler und Datenbankadministratoren vor der Entscheidung, welcher Datentyp am besten für die Primärschlüssel in relationalen Datenbanksystemen geeignet ist. Integer und UUID sind die gängigsten Typen. Während Integer mit ihrer Kompaktheit und Leistungsfähigkeit überzeugen, bieten UUIDs mit ihrer globalen Eindeutigkeit Vorteile, die insbesondere in verteilten Systemen unverzichtbar sind.

Dirk Randhahn
Teamleiter, Softwarearchitekt

Was ist Microsoft Roslyn?
Die .NET Compiler Platform ermöglicht es, C#-Code programmatisch zu lesen und auch zu generieren. Dadurch ist es nicht nur möglich Code besser zu analysieren, sondern auch dynamisch zur Laufzeit neuen Code zu erzeugen.

Kevin Erath
Geschäftsführer