C# İle Roslyn Analizörü Kullanarak Kod Standartları Denetimi Nasıl Yapılır?

C# İle Roslyn Analizörü Kullanarak Kod Standartları Denetimi Nasıl Yapılır?
C# İle Roslyn Analizörü Kullanarak Kod Standartları Denetimi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Roslyn analizörü geliştirmek için sisteminizde belirli araçların yüklü olması gerekir. 2026 yılı standartlarına göre aşağıdaki kurulumları tamamlamış olmalısınız:

  • Visual Studio 2022 veya üzeri (Community, Professional veya Enterprise).
  • .NET 8.0 veya 9.0 SDK.
  • "Visual Studio extension development" iş yükü (Workload).
  • .NET Compiler Platform SDK (Visual Studio Installer üzerinden eklenebilir).

Kurulumları doğruladıktan sonra, "Analyzer with Code Fix" şablonunu kullanarak ilk projenizi oluşturabilirsiniz. Bu şablon, hem bir analiz kuralı hem de bu kuralın nasıl düzeltileceğine dair bir "Code Fix" (kod düzeltici) yapısını otomatik olarak sunar.

Roslyn Analizör Proje Yapısını Anlamak

Bir Roslyn analizörü projesi temel olarak iki ana bileşenden oluşur: DiagnosticAnalyzer (kuralı tanımlayan kısım) ve CodeFixProvider (hatayı otomatik düzelten kısım). Analizörler, kodun sözdizimi ağacı (Syntax Tree) üzerinde çalışır.

Aşağıdaki kod örneği, bir sınıfın isminin belirli bir standartta olup olmadığını kontrol eden temel bir analizör yapısını göstermektedir:

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MyCustomAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "MY001";
    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
        DiagnosticId, "Sınıf ismi hatası", "Sınıf ismi '{0}' standartlara uygun değil", 
        "Naming", DiagnosticSeverity.Warning, isEnabledByDefault: true);

    public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);

    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();
        context.RegisterSyntaxNodeAction(AnalyzeSymbol, SyntaxKind.ClassDeclaration);
    }
}

Bu kodda, RegisterSyntaxNodeAction metodu ile sadece sınıf bildirimlerini (ClassDeclaration) dinliyoruz. Bu, analizörün performansını artırır çünkü tüm dosyayı değil, sadece ilgili düğümü inceler.

Adım Adım Özel Bir Kod Kuralı Yazma

Şimdi, kod içerisinde Console.WriteLine kullanımını yasaklayan ve yerine bir loglama kütüphanesi kullanılmasını zorunlu kılan bir kural yazalım. Bu, üretim ortamında (production) hata ayıklama kodlarının kalmasını engellemek için yaygın bir yöntemdir.

private static void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
{
    var invocation = (InvocationExpressionSyntax)context.Node;
    var memberAccess = invocation.Expression as MemberAccessExpressionSyntax;

    if (memberAccess?.Name.ToString() == "WriteLine" && 
        memberAccess.Expression.ToString() == "Console")
    {
        var diagnostic = Diagnostic.Create(Rule, invocation.GetLocation());
        context.ReportDiagnostic(diagnostic);
    }
}

Bu kod, Console.WriteLine ifadesini yakaladığında bir uyarı oluşturur. context.ReportDiagnostic metodu, Visual Studio'nun "Error List" penceresinde bu hatayı göstermesini sağlar.

Dikkat: Analizörler derleme süresini doğrudan etkiler. Çok karmaşık ağaç taramaları (Syntax Tree traversal) yapmaktan kaçının ve her zaman ConfigureGeneratedCodeAnalysis ile oluşturulmuş kodları (otomatik üretilen dosyalar) kapsam dışı bırakın.

Kod Düzeltici (Code Fix) Entegrasyonu

Analizörünüz bir hata bulduğunda, geliştiriciye bu hatayı tek tuşla düzeltme imkanı sunmalısınız. CodeFixProvider sınıfı, RegisterCodeFixesAsync metodu ile bu işlemi gerçekleştirir.

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
    var diagnostic = context.Diagnostics.First();
    var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
    var node = root.FindNode(diagnostic.Location.SourceSpan);

    context.RegisterCodeFix(
        CodeAction.Create("Logger kullan", c => ReplaceWithLogger(context.Document, node, c), "FixId"),
        diagnostic);
}

Burada ReplaceWithLogger metodu, hatalı kodu yeni kod yapısıyla (örneğin _logger.LogInformation(...)) değiştiren SyntaxRewriter mantığını tetikler.

Analizör Performansını Karşılaştırma

Kod standartlarını denetlemek için farklı yöntemler mevcuttur. Roslyn analizörleri, diğer yöntemlere göre daha derinlemesine analiz sağlar.

Yöntem Avantaj Dezavantaj
EditorConfig Hızlı ve basit Sadece stil odaklı
Roslyn Analizörü Derinlemesine mantıksal analiz Geliştirme süresi uzun
StyleCop Hazır kurallar Özelleştirme kısıtlı

Test Odaklı Analizör Geliştirme

Analizörünüzün çalıştığından emin olmak için birim testleri (unit test) yazmak zorunludur. Roslyn SDK, Microsoft.CodeAnalysis.CSharp.Analyzer.Testing kütüphanesi ile kod parçacıklarını test etmenize olanak tanır.

[TestMethod]
public async Task TestConsoleWriteLineIsFlagged()
{
    var code = @"using System; class Test { void M() { Console.WriteLine(); } }";
    var expected = VerifyCS.Diagnostic("MY001").WithSpan(1, 38, 1, 55);
    await VerifyCS.VerifyAnalyzerAsync(code, expected);
}

Bu test, kodunuzun analizör tarafından doğru bir şekilde yakalanıp yakalanmadığını doğrular. WithSpan metodu, hatanın kod içerisindeki tam konumunu belirtir.

Dağıtım ve Uygulama

Analizörünüzü tamamladıktan sonra bir NuGet paketi olarak paketleyebilirsiniz. Bu sayede, şirket içi projelerinizde veya açık kaynaklı projelerinizde .csproj dosyasına ekleyerek tüm ekibin aynı kod standartlarına uymasını sağlayabilirsiniz.

PrivateAssets="all" etiketi, bu analizörün projenin bağımlılıklarına dahil edilmemesini, sadece derleme zamanında çalışmasını sağlar.

Güvenlik Uyarısı: Analizör kodlarınızda dış kaynaklı (hardcoded) kimlik bilgileri veya hassas veriler bulundurmayın. Analizörler derleme sırasında çalışır ve projenin bir parçasıdır; bu nedenle güvenli kodlama pratiklerine (input validation, logging) burada da dikkat edilmelidir.

Sıkça Sorulan Sorular

Roslyn analizörleri çalışma zamanında (runtime) çalışır mı?

Hayır, Roslyn analizörleri sadece derleme zamanında (compile-time) çalışır. Çalışma zamanı hataları için birim testleri veya çalışma zamanı izleme araçları kullanılmalıdır.

Analizörüm çok yavaş çalışıyor, ne yapmalıyım?

Analizörünüzde Symbol aramaları yerine mümkün olduğunca Syntax düğümleri üzerinde işlem yapın. Ayrıca RegisterSyntaxNodeAction metodunda sadece ihtiyacınız olan SyntaxKind değerlerini filtreleyin.

Birden fazla kuralı tek bir pakette toplayabilir miyim?

Evet, bir NuGet paketi içerisinde onlarca farklı DiagnosticAnalyzer sınıfı tanımlayabilir ve bunları tek bir analizör kütüphanesi olarak dağıtabilirsiniz.

Analizörler CI/CD süreçlerinde nasıl kullanılır?

Analizörler derleme işleminin bir parçası olduğu için, Azure DevOps veya GitHub Actions üzerinde yapılan dotnet build komutu ile otomatik olarak tetiklenirler. Hatalar derlemeyi başarısız kılacak şekilde ayarlanabilir.

Kod düzeltici (Code Fix) yazmak zorunlu mu?

Zorunlu değildir, ancak geliştirici deneyimi (DX) açısından şiddetle önerilir. Sadece uyarı veren bir analizör, geliştiricinin hatayı düzeltmesini manuel bir süreç haline getirir.

İleri Seviye Analizör Teknikleri: SyntaxNode vs Symbol Analizi

Roslyn analizörleri geliştirirken, kodun sadece metinsel yapısına mı yoksa anlamsal (semantic) derinliğine mi odaklanacağınız, performans ve doğruluk açısından kritik bir karardır. Syntax tabanlı analizler hızlıdır ancak tip güvenliği veya kapsam (scope) hakkında bilgi sahibi değildir.

Semantic Model Kullanımı

Eğer bir değişkenin türünü kontrol etmeniz veya bir metodun hangi sınıfa ait olduğunu doğrulamanız gerekiyorsa, SemanticModel kullanmanız şarttır. Aşağıdaki örnek, bir değişkenin türünün tam adını kontrol eden bir analizör mantığını göstermektedir:

public override void Initialize(AnalysisContext context)
{
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.EnableConcurrentExecution();
    context.RegisterSyntaxNodeAction(AnalyzeVariableDeclaration, SyntaxKind.VariableDeclaration);
}

private void AnalyzeVariableDeclaration(SyntaxNodeAnalysisContext context)
{
    var variableDeclaration = (VariableDeclarationSyntax)context.Node;
    var semanticModel = context.SemanticModel;

    foreach (var variable in variableDeclaration.Variables)
    {
        var symbol = semanticModel.GetDeclaredSymbol(variable) as ILocalSymbol;
        if (symbol != null && symbol.Type.Name == "var") 
        {
            // Burada semantik analiz ile değişken türü doğrulanabilir
        }
    }
}

Analizörlerde Bellek Yönetimi ve Optimizasyon

Büyük kod tabanlarında çalışan analizörler, gereksiz nesne oluşturma nedeniyle IDE'nin yavaşlamasına neden olabilir. Roslyn analizörleri "stateless" (durumsuz) olmalıdır. Analizör sınıflarınızda sınıf seviyesinde değişken tutmaktan kaçınmalı ve her zaman AnalysisContext üzerinden sağlanan nesneleri kullanmalısınız.

Performans İpuçları

  • Syntax Tree Walking: Gereksiz ağaç dolaşmalarından kaçının. Sadece ihtiyacınız olan düğüm türlerini (SyntaxKind) kaydedin.
  • Caching: Eğer karmaşık hesaplamalar yapıyorsanız, context.Options içerisinde sağlanan önbellekleme mekanizmalarını kullanın.
  • Concurrent Execution: context.EnableConcurrentExecution() metodunu kullanarak analizörün çok çekirdekli sistemlerden faydalanmasını sağlayın.

Aşağıdaki tablo, analizör geliştirme sürecinde sıkça karşılaşılan performans darboğazlarını ve çözüm yollarını özetlemektedir:

Sorun Çözüm
IDE Yavaşlaması RegisterSyntaxNodeAction yerine daha spesifik filtreler kullanın.
Yüksek Bellek Kullanımı Sınıf üyelerinde büyük koleksiyonlar tutmayın, yerel değişkenleri tercih edin.
Hatalı Teşhisler Symbol karşılaştırmalarında SymbolEqualityComparer.Default kullanın.

Analizör Hata Ayıklama (Debugging) Stratejileri

Analizörünüzü geliştirirken Visual Studio'nun "Experimental Instance" özelliğini kullanmak en etkili yöntemdir. Ancak bazen analizörün canlı kod üzerinde nasıl davrandığını görmek için Debugger.Launch() komutunu kullanabilirsiniz.

private void AnalyzeMethod(SyntaxNodeAnalysisContext context)
{
    // Geliştirme aşamasında hata ayıklayıcıyı tetiklemek için:
    // System.Diagnostics.Debugger.Launch();
    
    var methodDeclaration = (MethodDeclarationSyntax)context.Node;
    if (methodDeclaration.Identifier.ValueText.StartsWith("Test"))
    {
        // Analiz mantığı
    }
}

Bu yöntemle, analizörünüzün hangi aşamada başarısız olduğunu veya hangi düğümde beklediğiniz veriyi alamadığınızı anlık olarak görebilirsiniz. Unutmayın, üretim (production) sürümüne geçmeden önce bu hata ayıklama satırlarını temizlemeyi ihmal etmeyin.

Sonuç

C# ile Roslyn analizörü geliştirmek, kod kalitesini standartlaştırmanın en profesyonel yoludur. Bu rehberde, bir analizörün nasıl tasarlanacağını, test edileceğini ve projelere entegre edileceğini öğrendiniz. Bir sonraki adım olarak, projenizdeki en sık yapılan hataları listeleyerek bunları engelleyen özel bir kural seti oluşturmayı deneyebilirsiniz. Temiz ve sürdürülebilir bir kod tabanı için Roslyn, .NET geliştiricisinin en güçlü silahıdır.

Bu yazıya tepkinizi paylaşın:
Zeynep Kaya

Ev yönetimi ve kendin yap (DIY) projeleri konusunda uzmanlaşmış bir içerik üreticisiyim. Detaylı rehberler hazırlayarak okuyucuların evdeki küçük sorunları profesyonel yardıma gerek duymadan çözmelerini sağlıyorum.

Yorumlar (0)

Yorum Yaz