public sealed class DisposeAnalyzer : DiagnosticAnalyzer { private const string Category = "Usage"; private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( id: "DisposeObjectsBeforeLosingScope", title: "Dispose objects before losing scope", messageFormat: "Dispose objects before losing scope", category: Category, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArraySupportedDiagnostics => ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.UsingStatement); context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ObjectInitializerExpression); } private static void AnalyzeNode(SyntaxNodeAnalysisContext context) { if (context.Node is UsingStatementSyntax usingStatement) { AnalyzeUsingStatement(context, usingStatement); } else if (context.Node is ObjectCreationExpressionSyntax objectCreation) { AnalyzeObjectCreation(context, objectCreation); } } private static void AnalyzeUsingStatement( SyntaxNodeAnalysisContext context, UsingStatementSyntax usingStatement) { if (usingStatement.Expression != null) { context.ReportDiagnostic(Diagnostic.Create(Rule, usingStatement.Expression.GetLocation())); } } private static void AnalyzeObjectCreation( SyntaxNodeAnalysisContext context, ObjectCreationExpressionSyntax objectCreation) { if (IsDisposable(objectCreation.Type, context.Compilation)) { var enclosingSymbol = context.SemanticModel.GetEnclosingSymbol(objectCreation.Parent.SpanStart, context.CancellationToken); var disposableSymbol = context.Compilation.GetTypeByMetadataName("System.IDisposable"); if (enclosingSymbol != null && !enclosingSymbol.AllInterfaces.Contains(disposableSymbol)) { context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation())); } } } private static bool IsDisposable(TypeSyntax type, Compilation compilation) { return compilation.GetTypeByMetadataName("System.IDisposable") .Equals(compilation.GetTypeByMetadataName(type.ToString())); } }
public sealed class NonExistentVariableAnalyzer : DiagnosticAnalyzer { private const string Category = "Usage"; private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( id: "NonExistentVariable", title: "Non-existent variable usage", messageFormat: "Variable '{0}' does not exist", category: Category, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArraySupportedDiagnostics => ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.IdentifierName); } private static void AnalyzeNode(SyntaxNodeAnalysisContext context) { if (context.Node is IdentifierNameSyntax identifierName) { var symbolInfo = context.SemanticModel.GetSymbolInfo(identifierName); if (symbolInfo.Symbol == null && symbolInfo.CandidateSymbols.Length == 0) { context.ReportDiagnostic(Diagnostic.Create(Rule, identifierName.GetLocation(), identifierName.Identifier.ValueText)); } } } }