Yükleniyor...

C# Roslyn Compiler API ile Kod Analizi

C#’ta Roslyn Compiler API ile kod analizi yapmayı öğrenin. Syntax tree, analiz ve code generation senaryoları örneklerle.

.NET platformunda Roslyn (Microsoft.CodeAnalysis), C# ve VB derleyicilerini tamamen yönetilen (managed) kod olarak sunar. Roslyn Compiler API, kaynak kodu programatik olarak okuma, analiz etme, değiştirme ve hatta yeniden derleme olanağı sağlar. Bu sayede statik analiz araçları, otomatik kod düzeltme (code fix), linting veya özel IDE uzantıları geliştirilebilir.


Roslyn Compiler API Nedir?

Roslyn, C# derleyicisinin API olarak geliştiricilere sunulmuş halidir. Normalde Visual Studio’nun veya dotnet derleyicisinin yaptığı tüm işlemleri (parse, analyze, compile) kodla yapabilmenizi sağlar.

Temel olarak şu katmanlardan oluşur:


Kurulum

Roslyn API’yi kullanmak için Microsoft.CodeAnalysis.CSharp NuGet paketini ekleyin:


dotnet add package Microsoft.CodeAnalysis.CSharp

Kaynak Koddan Syntax Tree Oluşturma

İlk adımda, bir C# kodunu Syntax Tree (sözdizim ağacı) haline dönüştürebilirsiniz. Bu ağaç, kodun yapısal bileşenlerini düğümler (node) halinde tutar.


using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

class Program
{
    static void Main()
    {
        string kod = @"
            using System;
            class Selam
            {
                static void Main()
                {
                    Console.WriteLine(""Merhaba Roslyn!"");
                }
            }";

        SyntaxTree agac = CSharpSyntaxTree.ParseText(kod);

        var kok = agac.GetRoot();
        Console.WriteLine($"Kök düğüm: {kok.Kind()}");

        foreach (var node in kok.DescendantNodes())
            Console.WriteLine(node.Kind());
    }
}

Çıktıda ClassDeclaration, MethodDeclaration, InvocationExpression gibi düğüm türleri listelenir.


Semantic Model ile Anlam Analizi

SemanticModel, değişken türleri, metot imzaları ve sembollerle ilgili detaylı bilgi verir. Örneğin bir metot çağrısının hangi sınıfa ait olduğunu ya da bir değişkenin türünü öğrenebilirsiniz.


using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

class Program
{
    static void Main()
    {
        string kod = @"
using System;
class Test
{
    static void Main()
    {
        string mesaj = ""Selam!"";
        Console.WriteLine(mesaj);
    }
}";

        SyntaxTree agac = CSharpSyntaxTree.ParseText(kod);
        var derleme = CSharpCompilation.Create("TestDerleme")
            .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
            .AddSyntaxTrees(agac);

        var model = derleme.GetSemanticModel(agac);

        var kok = agac.GetRoot();
        var degisken = kok.DescendantNodes().OfType<VariableDeclaratorSyntax>().First();

        var sembol = model.GetDeclaredSymbol(degisken);
        Console.WriteLine($"Değişken adı: {sembol.Name}, Tür: {sembol.GetSymbolType()?.Name}");
    }
}

Bu örnekle mesaj değişkeninin türüne (string) ulaşılır. Roslyn bu bilgiyi sembol tablosundan çıkarır; sadece metin taraması değildir.


Syntax Rewriter: Kod Üzerinde Değişiklik Yapma

Roslyn ile yalnızca analiz değil, aynı zamanda kod dönüştürme (refactoring) de yapılabilir. CSharpSyntaxRewriter sınıfını kullanarak belirli düğümleri değiştirebilir veya silebilirsiniz.


using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;

class UppercaseRewriter : CSharpSyntaxRewriter
{
    public override SyntaxNode VisitLiteralExpression(LiteralExpressionSyntax node)
    {
        if (node.IsKind(SyntaxKind.StringLiteralExpression))
        {
            string yeni = node.Token.ValueText.ToUpperInvariant();
            return SyntaxFactory.LiteralExpression(
                SyntaxKind.StringLiteralExpression,
                SyntaxFactory.Literal(yeni));
        }
        return base.VisitLiteralExpression(node);
    }
}

class Program
{
    static void Main()
    {
        string kod = @"class A { void M() { System.Console.WriteLine(""selam""); } }";
        SyntaxTree tree = CSharpSyntaxTree.ParseText(kod);
        var rewriter = new UppercaseRewriter();
        var yeniKok = rewriter.Visit(tree.GetRoot());
        Console.WriteLine(yeniKok.ToFullString());
    }
}

Bu örnek, tüm string literal’ları büyük harfe çevirir. Sonuç olarak "selam""SELAM" olur.


Diagnostics: Hata ve Uyarı Analizi

Compilation nesnesi, kodun derlenmesi sırasında oluşan hataları ve uyarıları döndürebilir. Bu sayede özel kurallar tanımlayarak statik analiz aracı oluşturabilirsiniz.


using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        var kod = "class A { void M() { int x = \"yanlış\"; } }";
        var agac = CSharpSyntaxTree.ParseText(kod);

        var derleme = CSharpCompilation.Create("Analiz")
            .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
            .AddSyntaxTrees(agac);

        var hatalar = derleme.GetDiagnostics();

        foreach (var h in hatalar)
            Console.WriteLine($"{h.Id}: {h.GetMessage()}");
    }
}

Çıktı: CS0029: 'string' türü 'int' türüne dönüştürülemez Böylece derleyici hatalarını veya özel kurallarınızı yakalayabilirsiniz.


Örnek: “Kullanılmayan Değişken” Analizi

Aşağıdaki örnek, Roslyn API kullanarak bir dosyada tanımlanıp hiç kullanılmayan değişkenleri tespit eder. Bu, Visual Studio’daki yeşil “kullanılmıyor” uyarısına benzer şekilde çalışır.


using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

class Program
{
    static void Main()
    {
        string kod = @"
class Test
{
    void Metot()
    {
        int x = 10;
        int y = 20;
        Console.WriteLine(x);
    }
}";
        var agac = CSharpSyntaxTree.ParseText(kod);
        var derleme = CSharpCompilation.Create("Analiz")
            .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
            .AddSyntaxTrees(agac);

        var model = derleme.GetSemanticModel(agac);
        var kok = agac.GetRoot();

        var degiskenler = kok.DescendantNodes().OfType<VariableDeclaratorSyntax>();

        foreach (var d in degiskenler)
        {
            var sembol = model.GetDeclaredSymbol(d);
            var kullanımlar = kok.DescendantNodes()
                .OfType<IdentifierNameSyntax>()
                .Count(i => i.Identifier.Text == d.Identifier.Text);

            if (kullanımlar <= 1)
                Console.WriteLine($"Uyarı: '{d.Identifier.Text}' değişkeni kullanılmıyor.");
        }
    }
}

Çıktı: Uyarı: 'y' değişkeni kullanılmıyor. Bu basit örnek, statik kod analiz araçlarının çalışma mantığını gösterir.


Roslyn ile Neler Geliştirilebilir?


Performans ve Dikkat Edilecekler


TL;DR

  • Roslyn Compiler API, C# kodunu analiz etmek, dönüştürmek ve derlemek için programatik bir arayüz sağlar.
  • Syntax API sözdizimi (Syntax Tree), Semantic API anlam (type/symbol) bilgisini sunar.
  • Özel code analyzer veya refactoring araçları bu API ile geliştirilebilir.
  • Roslyn, Reflection’dan farklı olarak derleme zamanı analizine odaklanır.
  • Büyük projelerde incremental analiz ve performans optimizasyonu önemlidir.

İlişkili Makaleler

C# ile SOLID Prensipleri

C# örnekleriyle SOLID prensiplerinin uygulanışı: daha esnek, sürdürülebilir ve test edilebilir kod tasarımları.

C# Reflection ve Late Binding

C#’ta Reflection ve Late Binding kullanımını öğrenin. Runtime tip keşfi, dinamik çağrılar ve esnek yapıların örnekleri anlatılıyor.