/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Test/Core/Diagnostics/OperationTestAnalyzer.cs
2 161 строка
100 KB
Youssef Victor
Remove unused private members (#61064)
12 июн 2022, 21:22
Не верифицирован
12 июн 2022, 21:22
a528fe2
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics { // These analyzers are not intended for any actual use. They exist solely to test IOperation support. /// <summary>Analyzer used to test for bad statements and expressions.</summary> public class BadStuffTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor InvalidExpressionDescriptor = new DiagnosticDescriptor( "InvalidExpression", "Invalid Expression", "Invalid expression found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor InvalidStatementDescriptor = new DiagnosticDescriptor( "InvalidStatement", "Invalid Statement", "Invalid statement found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor IsInvalidDescriptor = new DiagnosticDescriptor( "IsInvalid", "Is Invalid", "Operation found that is invalid.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(InvalidExpressionDescriptor, InvalidStatementDescriptor, IsInvalidDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var invalidOperation = (IInvalidOperation)operationContext.Operation; if (invalidOperation.Type == null) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidStatementDescriptor, operationContext.Operation.Syntax.GetLocation())); } else { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidExpressionDescriptor, operationContext.Operation.Syntax.GetLocation())); } }, OperationKind.Invalid); context.RegisterOperationAction( (operationContext) => { if (operationContext.Operation.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { operationContext.ReportDiagnostic(Diagnostic.Create(IsInvalidDescriptor, operationContext.Operation.Syntax.GetLocation())); } }, OperationKind.Invocation, OperationKind.Invalid); } } /// <summary>Analyzer used to test for operations within symbols of certain names.</summary> public class OwningSymbolTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor ExpressionDescriptor = new DiagnosticDescriptor( "Expression", "Expression", "Expression found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(ExpressionDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationBlockStartAction( (operationBlockContext) => { if (operationBlockContext.Compilation.Language != "Stumble") { operationBlockContext.RegisterOperationAction( (operationContext) => { if (operationContext.ContainingSymbol.Name.StartsWith("Funky") && operationContext.Compilation.Language != "Mumble") { operationContext.ReportDiagnostic(Diagnostic.Create(ExpressionDescriptor, operationContext.Operation.Syntax.GetLocation())); } }, OperationKind.LocalReference, OperationKind.Literal); } }); } } /// <summary>Analyzer used to test for loop IOperations.</summary> public class BigForTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Reliability".</summary> private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor BigForDescriptor = new DiagnosticDescriptor( "BigForRule", "Big For Loop", "For loop iterates more than one million times", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(BigForDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction(AnalyzeOperation, OperationKind.Loop); } private void AnalyzeOperation(OperationAnalysisContext operationContext) { ILoopOperation loop = (ILoopOperation)operationContext.Operation; if (loop.LoopKind == LoopKind.For) { IForLoopOperation forLoop = (IForLoopOperation)loop; IOperation forCondition = forLoop.Condition; if (forCondition.Kind == OperationKind.Binary) { IBinaryOperation condition = (IBinaryOperation)forCondition; IOperation conditionLeft = condition.LeftOperand; IOperation conditionRight = condition.RightOperand; if (conditionRight.ConstantValue.HasValue && conditionRight.Type.SpecialType == SpecialType.System_Int32 && conditionLeft.Kind == OperationKind.LocalReference) { // Test is known to be a comparison of a local against a constant. int testValue = (int)conditionRight.ConstantValue.Value; ILocalSymbol testVariable = ((ILocalReferenceOperation)conditionLeft).Local; if (forLoop.Before.Length == 1) { IOperation setup = forLoop.Before[0]; if (setup.Kind == OperationKind.ExpressionStatement && ((IExpressionStatementOperation)setup).Operation.Kind == OperationKind.SimpleAssignment) { ISimpleAssignmentOperation setupAssignment = (ISimpleAssignmentOperation)((IExpressionStatementOperation)setup).Operation; if (setupAssignment.Target.Kind == OperationKind.LocalReference && ((ILocalReferenceOperation)setupAssignment.Target).Local == testVariable && setupAssignment.Value.ConstantValue.HasValue && setupAssignment.Value.Type.SpecialType == SpecialType.System_Int32) { // Setup is known to be an assignment of a constant to the local used in the test. int initialValue = (int)setupAssignment.Value.ConstantValue.Value; if (forLoop.AtLoopBottom.Length == 1) { IOperation advance = forLoop.AtLoopBottom[0]; if (advance.Kind == OperationKind.ExpressionStatement) { IOperation advanceExpression = ((IExpressionStatementOperation)advance).Operation; Optional<object> advanceIncrementOpt; BinaryOperatorKind? advanceOperationCode; GetOperationKindAndValue(testVariable, advanceExpression, out advanceOperationCode, out advanceIncrementOpt); if (advanceIncrementOpt.HasValue && advanceOperationCode.HasValue) { var incrementValue = (int)advanceIncrementOpt.Value; if (advanceOperationCode.Value == BinaryOperatorKind.Subtract) { advanceOperationCode = BinaryOperatorKind.Add; incrementValue = -incrementValue; } if (advanceOperationCode.Value == BinaryOperatorKind.Add && incrementValue != 0 && (condition.OperatorKind == BinaryOperatorKind.LessThan || condition.OperatorKind == BinaryOperatorKind.LessThanOrEqual || condition.OperatorKind == BinaryOperatorKind.NotEquals || condition.OperatorKind == BinaryOperatorKind.GreaterThan || condition.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual)) { int iterationCount = (testValue - initialValue) / incrementValue; if (iterationCount >= 1000000) { Report(operationContext, forLoop.Syntax, BigForDescriptor); } } } } } } } } } } } } private void GetOperationKindAndValue( ILocalSymbol testVariable, IOperation advanceExpression, out BinaryOperatorKind? advanceOperationCode, out Optional<object> advanceIncrementOpt) { advanceIncrementOpt = null; advanceOperationCode = null; if (advanceExpression.Kind == OperationKind.SimpleAssignment) { ISimpleAssignmentOperation advanceAssignment = (ISimpleAssignmentOperation)advanceExpression; if (advanceAssignment.Target.Kind == OperationKind.LocalReference && ((ILocalReferenceOperation)advanceAssignment.Target).Local == testVariable && advanceAssignment.Value.Kind == OperationKind.Binary && advanceAssignment.Value.Type.SpecialType == SpecialType.System_Int32) { // Advance is known to be an assignment of a binary operation to the local used in the test. IBinaryOperation advanceOperation = (IBinaryOperation)advanceAssignment.Value; if (advanceOperation.OperatorMethod == null && advanceOperation.LeftOperand.Kind == OperationKind.LocalReference && ((ILocalReferenceOperation)advanceOperation.LeftOperand).Local == testVariable && advanceOperation.RightOperand.ConstantValue.HasValue && advanceOperation.RightOperand.Type.SpecialType == SpecialType.System_Int32) { // Advance binary operation is known to involve a reference to the local used in the test and a constant. advanceIncrementOpt = advanceOperation.RightOperand.ConstantValue; advanceOperationCode = advanceOperation.OperatorKind; } } } else if (advanceExpression.Kind == OperationKind.CompoundAssignment) { ICompoundAssignmentOperation advanceAssignment = (ICompoundAssignmentOperation)advanceExpression; if (advanceAssignment.Target.Kind == OperationKind.LocalReference && ((ILocalReferenceOperation)advanceAssignment.Target).Local == testVariable && advanceAssignment.Value.ConstantValue.HasValue && advanceAssignment.Value.Type.SpecialType == SpecialType.System_Int32) { // Advance binary operation is known to involve a reference to the local used in the test and a constant. advanceIncrementOpt = advanceAssignment.Value.ConstantValue; advanceOperationCode = advanceAssignment.OperatorKind; } } else if (advanceExpression.Kind == OperationKind.Increment) { IIncrementOrDecrementOperation advanceAssignment = (IIncrementOrDecrementOperation)advanceExpression; if (advanceAssignment.Target.Kind == OperationKind.LocalReference && ((ILocalReferenceOperation)advanceAssignment.Target).Local == testVariable) { // Advance binary operation is known to involve a reference to the local used in the test and a constant. advanceIncrementOpt = new Optional<object>(1); advanceOperationCode = BinaryOperatorKind.Add; } } } private void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test switch IOperations.</summary> public class SwitchTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Reliability".</summary> private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor SparseSwitchDescriptor = new DiagnosticDescriptor( "SparseSwitchRule", "Sparse switch", "Switch has less than one percent density", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor NoDefaultSwitchDescriptor = new DiagnosticDescriptor( "NoDefaultSwitchRule", "No default switch", "Switch has no default case", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor OnlyDefaultSwitchDescriptor = new DiagnosticDescriptor( "OnlyDefaultSwitchRule", "Only default switch", "Switch only has a default case", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(SparseSwitchDescriptor, NoDefaultSwitchDescriptor, OnlyDefaultSwitchDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { ISwitchOperation switchOperation = (ISwitchOperation)operationContext.Operation; long minCaseValue = long.MaxValue; long maxCaseValue = long.MinValue; long caseValueCount = 0; bool hasDefault = false; bool hasNonDefault = false; foreach (ISwitchCaseOperation switchCase in switchOperation.Cases) { foreach (ICaseClauseOperation clause in switchCase.Clauses) { switch (clause.CaseKind) { case CaseKind.SingleValue: { hasNonDefault = true; ISingleValueCaseClauseOperation singleValueClause = (ISingleValueCaseClauseOperation)clause; IOperation singleValueExpression = singleValueClause.Value; if (singleValueExpression != null && singleValueExpression.ConstantValue.HasValue && singleValueExpression.Type.SpecialType == SpecialType.System_Int32) { int singleValue = (int)singleValueExpression.ConstantValue.Value; caseValueCount += IncludeClause(singleValue, singleValue, ref minCaseValue, ref maxCaseValue); } else { return; } break; } case CaseKind.Range: { hasNonDefault = true; IRangeCaseClauseOperation rangeClause = (IRangeCaseClauseOperation)clause; IOperation rangeMinExpression = rangeClause.MinimumValue; IOperation rangeMaxExpression = rangeClause.MaximumValue; if (rangeMinExpression != null && rangeMinExpression.ConstantValue.HasValue && rangeMinExpression.Type.SpecialType == SpecialType.System_Int32 && rangeMaxExpression != null && rangeMaxExpression.ConstantValue.HasValue && rangeMaxExpression.Type.SpecialType == SpecialType.System_Int32) { int rangeMinValue = (int)rangeMinExpression.ConstantValue.Value; int rangeMaxValue = (int)rangeMaxExpression.ConstantValue.Value; caseValueCount += IncludeClause(rangeMinValue, rangeMaxValue, ref minCaseValue, ref maxCaseValue); } else { return; } break; } case CaseKind.Relational: { hasNonDefault = true; IRelationalCaseClauseOperation relationalClause = (IRelationalCaseClauseOperation)clause; IOperation relationalValueExpression = relationalClause.Value; if (relationalValueExpression != null && relationalValueExpression.ConstantValue.HasValue && relationalValueExpression.Type.SpecialType == SpecialType.System_Int32) { int rangeMinValue = int.MaxValue; int rangeMaxValue = int.MinValue; int relationalValue = (int)relationalValueExpression.ConstantValue.Value; switch (relationalClause.Relation) { case BinaryOperatorKind.Equals: rangeMinValue = relationalValue; rangeMaxValue = relationalValue; break; case BinaryOperatorKind.NotEquals: return; case BinaryOperatorKind.LessThan: rangeMinValue = int.MinValue; rangeMaxValue = relationalValue - 1; break; case BinaryOperatorKind.LessThanOrEqual: rangeMinValue = int.MinValue; rangeMaxValue = relationalValue; break; case BinaryOperatorKind.GreaterThanOrEqual: rangeMinValue = relationalValue; rangeMaxValue = int.MaxValue; break; case BinaryOperatorKind.GreaterThan: rangeMinValue = relationalValue + 1; rangeMaxValue = int.MaxValue; break; } caseValueCount += IncludeClause(rangeMinValue, rangeMaxValue, ref minCaseValue, ref maxCaseValue); } else { return; } break; } case CaseKind.Default: { hasDefault = true; break; } } } } long span = maxCaseValue - minCaseValue + 1; if (caseValueCount == 0 && !hasDefault || caseValueCount != 0 && span / caseValueCount > 100) { Report(operationContext, switchOperation.Value.Syntax, SparseSwitchDescriptor); } if (!hasDefault) { Report(operationContext, switchOperation.Value.Syntax, NoDefaultSwitchDescriptor); } if (hasDefault && !hasNonDefault) { Report(operationContext, switchOperation.Value.Syntax, OnlyDefaultSwitchDescriptor); } }, OperationKind.Switch); } private static int IncludeClause(int clauseMinValue, int clauseMaxValue, ref long minCaseValue, ref long maxCaseValue) { if (clauseMinValue < minCaseValue) { minCaseValue = clauseMinValue; } if (clauseMaxValue > maxCaseValue) { maxCaseValue = clauseMaxValue; } return clauseMaxValue - clauseMinValue + 1; } private void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test invocation IOperations.</summary> public class InvocationTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Reliability".</summary> private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor BigParamArrayArgumentsDescriptor = new DiagnosticDescriptor( "BigParamarrayRule", "Big Paramarray", "Paramarray has more than 10 elements", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor OutOfNumericalOrderArgumentsDescriptor = new DiagnosticDescriptor( "OutOfOrderArgumentsRule", "Out of order arguments", "Argument values are not in increasing order", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor UseDefaultArgumentDescriptor = new DiagnosticDescriptor( "UseDefaultArgument", "Use default argument", "Invocation uses default argument {0}", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor InvalidArgumentDescriptor = new DiagnosticDescriptor( "InvalidArgument", "Invalid argument", "Invocation has invalid argument", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(BigParamArrayArgumentsDescriptor, OutOfNumericalOrderArgumentsDescriptor, UseDefaultArgumentDescriptor, InvalidArgumentDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { IInvocationOperation invocation = (IInvocationOperation)operationContext.Operation; long priorArgumentValue = long.MinValue; foreach (IArgumentOperation argument in invocation.Arguments) { if (argument.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidArgumentDescriptor, argument.Syntax.GetLocation())); return; } if (argument.ArgumentKind == ArgumentKind.DefaultValue) { operationContext.ReportDiagnostic(Diagnostic.Create(UseDefaultArgumentDescriptor, invocation.Syntax.GetLocation(), argument.Parameter.Name)); } TestAscendingArgument(operationContext, argument.Value, ref priorArgumentValue); if (argument.ArgumentKind == ArgumentKind.ParamArray) { if (argument.Value is IArrayCreationOperation arrayArgument) { var initializer = arrayArgument.Initializer; if (initializer != null) { if (initializer.ElementValues.Length > 10) { Report(operationContext, invocation.Syntax, BigParamArrayArgumentsDescriptor); } foreach (IOperation element in initializer.ElementValues) { TestAscendingArgument(operationContext, element, ref priorArgumentValue); } } } } } }, OperationKind.Invocation); } private static void TestAscendingArgument(OperationAnalysisContext operationContext, IOperation argument, ref long priorArgumentValue) { Optional<object> argumentValue = argument.ConstantValue; if (argumentValue.HasValue && argument.Type.SpecialType == SpecialType.System_Int32) { int integerArgument = (int)argumentValue.Value; if (integerArgument < priorArgumentValue) { Report(operationContext, argument.Syntax, OutOfNumericalOrderArgumentsDescriptor); } priorArgumentValue = integerArgument; } } private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test various contexts in which IOperations can occur.</summary> public class SeventeenTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Reliability".</summary> private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor SeventeenDescriptor = new DiagnosticDescriptor( "SeventeenRule", "Seventeen", "Seventeen is a recognized value", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(SeventeenDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { ILiteralOperation literal = (ILiteralOperation)operationContext.Operation; if (literal.Type.SpecialType == SpecialType.System_Int32 && literal.ConstantValue.HasValue && (int)literal.ConstantValue.Value == 17) { operationContext.ReportDiagnostic(Diagnostic.Create(SeventeenDescriptor, literal.Syntax.GetLocation())); } }, OperationKind.Literal); } } /// <summary>Analyzer used to test IArgument IOperations.</summary> public class NullArgumentTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Reliability".</summary> private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor NullArgumentsDescriptor = new DiagnosticDescriptor( "NullArgumentRule", "Null Argument", "Value of the argument is null", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(NullArgumentsDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var argument = (IArgumentOperation)operationContext.Operation; if (argument.Value.ConstantValue.HasValue && argument.Value.ConstantValue.Value == null) { Report(operationContext, argument.Syntax, NullArgumentsDescriptor); } }, OperationKind.Argument); } private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test IMemberInitializer IOperations.</summary> public class MemberInitializerTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Reliability".</summary> private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor DoNotUseFieldInitializerDescriptor = new DiagnosticDescriptor( "DoNotUseFieldInitializer", "Do Not Use Field Initializer", "a field initializer is used for object creation", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor DoNotUsePropertyInitializerDescriptor = new DiagnosticDescriptor( "DoNotUsePropertyInitializer", "Do Not Use Property Initializer", "A property initializer is used for object creation", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(DoNotUseFieldInitializerDescriptor, DoNotUsePropertyInitializerDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var initializer = operationContext.Operation; Report(operationContext, initializer.Syntax, initializer.Kind == OperationKind.FieldReference ? DoNotUseFieldInitializerDescriptor : DoNotUsePropertyInitializerDescriptor); }, OperationKind.FieldReference, OperationKind.PropertyReference); } private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test IAssignmentExpression IOperations.</summary> public class AssignmentTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Reliability".</summary> private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor DoNotUseMemberAssignmentDescriptor = new DiagnosticDescriptor( "DoNotUseMemberAssignment", "Do Not Use Member Assignment", "Do not assign values to object members", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(DoNotUseMemberAssignmentDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var assignment = (ISimpleAssignmentOperation)operationContext.Operation; var kind = assignment.Target.Kind; if (kind == OperationKind.FieldReference || kind == OperationKind.PropertyReference) { Report(operationContext, assignment.Syntax, DoNotUseMemberAssignmentDescriptor); } }, OperationKind.SimpleAssignment); } private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test IArrayInitializer IOperations.</summary> public class ArrayInitializerTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Maintainability".</summary> private const string Maintainability = nameof(Maintainability); public static readonly DiagnosticDescriptor DoNotUseLargeListOfArrayInitializersDescriptor = new DiagnosticDescriptor( "DoNotUseLongListToInitializeArray", "Do not use long list to initialize array", "a list of more than 5 elements is used for an array initialization", Maintainability, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(DoNotUseLargeListOfArrayInitializersDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var initializer = (IArrayInitializerOperation)operationContext.Operation; if (initializer.ElementValues.Length > 5) { Report(operationContext, initializer.Syntax, DoNotUseLargeListOfArrayInitializersDescriptor); } }, OperationKind.ArrayInitializer); } private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test IVariableDeclarationStatement IOperations.</summary> public class VariableDeclarationTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Maintainability".</summary> private const string Maintainability = nameof(Maintainability); public static readonly DiagnosticDescriptor TooManyLocalVarDeclarationsDescriptor = new DiagnosticDescriptor( "TooManyLocalVarDeclarations", "Too many local variable declarations", "A declaration statement shouldn't have more than 3 variable declarations", Maintainability, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor LocalVarInitializedDeclarationDescriptor = new DiagnosticDescriptor( "LocalVarInitializedDeclaration", "Local var initialized at declaration", "A local variable is initialized at declaration.", Maintainability, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(TooManyLocalVarDeclarationsDescriptor, LocalVarInitializedDeclarationDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var declarationStatement = (IVariableDeclarationGroupOperation)operationContext.Operation; if (declarationStatement.GetDeclaredVariables().Count() > 3) { Report(operationContext, declarationStatement.Syntax, TooManyLocalVarDeclarationsDescriptor); } foreach (var decl in declarationStatement.Declarations.SelectMany(multiDecl => multiDecl.Declarators)) { var initializer = decl.GetVariableInitializer(); if (initializer != null && !initializer.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { Report(operationContext, decl.Symbol.DeclaringSyntaxReferences.Single().GetSyntax(), LocalVarInitializedDeclarationDescriptor); } } }, OperationKind.VariableDeclarationGroup); } private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test ICase and ICaseClause.</summary> public class CaseTestAnalyzer : DiagnosticAnalyzer { /// <summary>Diagnostic category "Maintainability".</summary> private const string Maintainability = nameof(Maintainability); public static readonly DiagnosticDescriptor HasDefaultCaseDescriptor = new DiagnosticDescriptor( "HasDefaultCase", "Has Default Case", "A default case clause is encountered", Maintainability, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor MultipleCaseClausesDescriptor = new DiagnosticDescriptor( "MultipleCaseClauses", "Multiple Case Clauses", "A switch section has multiple case clauses", Maintainability, DiagnosticSeverity.Warning, isEnabledByDefault: true); /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary> public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(HasDefaultCaseDescriptor, MultipleCaseClausesDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { switch (operationContext.Operation.Kind) { case OperationKind.CaseClause: var caseClause = (ICaseClauseOperation)operationContext.Operation; if (caseClause.CaseKind == CaseKind.Default) { Report(operationContext, caseClause.Syntax, HasDefaultCaseDescriptor); } break; case OperationKind.SwitchCase: var switchSection = (ISwitchCaseOperation)operationContext.Operation; if (!switchSection.HasErrors(operationContext.Compilation, operationContext.CancellationToken) && switchSection.Clauses.Length > 1) { Report(operationContext, switchSection.Syntax, MultipleCaseClausesDescriptor); } break; } }, OperationKind.SwitchCase, OperationKind.CaseClause); } private static void Report(OperationAnalysisContext context, SyntaxNode syntax, DiagnosticDescriptor descriptor) { context.ReportDiagnostic(Diagnostic.Create(descriptor, syntax.GetLocation())); } } /// <summary>Analyzer used to test for explicit vs. implicit instance references.</summary> public class ExplicitVsImplicitInstanceAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor ImplicitInstanceDescriptor = new DiagnosticDescriptor( "ImplicitInstance", "Implicit Instance", "Implicit instance found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor ExplicitInstanceDescriptor = new DiagnosticDescriptor( "ExplicitInstance", "Explicit Instance", "Explicit instance found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(ImplicitInstanceDescriptor, ExplicitInstanceDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { IInstanceReferenceOperation instanceReference = (IInstanceReferenceOperation)operationContext.Operation; operationContext.ReportDiagnostic(Diagnostic.Create(instanceReference.IsImplicit ? ImplicitInstanceDescriptor : ExplicitInstanceDescriptor, instanceReference.Syntax.GetLocation())); }, OperationKind.InstanceReference); } } /// <summary>Analyzer used to test for member references.</summary> public class MemberReferenceAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor EventReferenceDescriptor = new DiagnosticDescriptor( "EventReference", "Event Reference", "Event reference found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor InvalidEventDescriptor = new DiagnosticDescriptor( "InvalidEvent", "Invalid Event", "A EventAssignmentExpression with invalid event found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor HandlerAddedDescriptor = new DiagnosticDescriptor( "HandlerAdded", "Handler Added", "Event handler added.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor HandlerRemovedDescriptor = new DiagnosticDescriptor( "HandlerRemoved", "Handler Removed", "Event handler removed.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor PropertyReferenceDescriptor = new DiagnosticDescriptor( "PropertyReference", "Property Reference", "Property reference found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor FieldReferenceDescriptor = new DiagnosticDescriptor( "FieldReference", "Field Reference", "Field reference found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor MethodBindingDescriptor = new DiagnosticDescriptor( "MethodBinding", "Method Binding", "Method binding found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(EventReferenceDescriptor, HandlerAddedDescriptor, HandlerRemovedDescriptor, PropertyReferenceDescriptor, FieldReferenceDescriptor, MethodBindingDescriptor, InvalidEventDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic(Diagnostic.Create(EventReferenceDescriptor, operationContext.Operation.Syntax.GetLocation())); }, OperationKind.EventReference); context.RegisterOperationAction( (operationContext) => { IEventAssignmentOperation eventAssignment = (IEventAssignmentOperation)operationContext.Operation; operationContext.ReportDiagnostic(Diagnostic.Create(eventAssignment.Adds ? HandlerAddedDescriptor : HandlerRemovedDescriptor, operationContext.Operation.Syntax.GetLocation())); if (eventAssignment.EventReference.Kind == OperationKind.Invalid || eventAssignment.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidEventDescriptor, eventAssignment.Syntax.GetLocation())); } }, OperationKind.EventAssignment); context.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic(Diagnostic.Create(PropertyReferenceDescriptor, operationContext.Operation.Syntax.GetLocation())); }, OperationKind.PropertyReference); context.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic(Diagnostic.Create(FieldReferenceDescriptor, operationContext.Operation.Syntax.GetLocation())); }, OperationKind.FieldReference); context.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic(Diagnostic.Create(MethodBindingDescriptor, operationContext.Operation.Syntax.GetLocation())); }, OperationKind.MethodReference); } } /// <summary>Analyzer used to test IOperation treatment of params array arguments.</summary> public class ParamsArrayTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor LongParamsDescriptor = new DiagnosticDescriptor( "LongParams", "Long Params", "Params array argument has more than 3 elements.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor InvalidConstructorDescriptor = new DiagnosticDescriptor( "InvalidConstructor", "Invalid Constructor", "Invalid Constructor.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(LongParamsDescriptor, InvalidConstructorDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { IInvocationOperation invocation = (IInvocationOperation)operationContext.Operation; foreach (IArgumentOperation argument in invocation.Arguments) { if (argument.Parameter.IsParams) { if (argument.Value is IArrayCreationOperation arrayValue) { Optional<object> dimensionSize = arrayValue.DimensionSizes[0].ConstantValue; if (dimensionSize.HasValue && IntegralValue(dimensionSize.Value) > 3) { operationContext.ReportDiagnostic(Diagnostic.Create(LongParamsDescriptor, argument.Value.Syntax.GetLocation())); } } } } }, OperationKind.Invocation); context.RegisterOperationAction( (operationContext) => { IObjectCreationOperation creation = (IObjectCreationOperation)operationContext.Operation; if (creation.Constructor == null) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidConstructorDescriptor, creation.Syntax.GetLocation())); } foreach (IArgumentOperation argument in creation.Arguments) { if (argument.Parameter.IsParams) { if (argument.Value is IArrayCreationOperation arrayValue) { Optional<object> dimensionSize = arrayValue.DimensionSizes[0].ConstantValue; if (dimensionSize.HasValue && IntegralValue(dimensionSize.Value) > 3) { operationContext.ReportDiagnostic(Diagnostic.Create(LongParamsDescriptor, argument.Value.Syntax.GetLocation())); } } } } }, OperationKind.ObjectCreation); } private static long IntegralValue(object value) { if (value is long v) { return v; } if (value is int i) { return i; } return 0; } } /// <summary>Analyzer used to test for initializer constructs for members and parameters.</summary> public class EqualsValueTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor EqualsValueDescriptor = new DiagnosticDescriptor( "EqualsValue", "Equals Value", "Equals value found.", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(EqualsValueDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { IFieldInitializerOperation equalsValue = (IFieldInitializerOperation)operationContext.Operation; if (equalsValue.InitializedFields[0].Name.StartsWith("F")) { operationContext.ReportDiagnostic(Diagnostic.Create(EqualsValueDescriptor, equalsValue.Syntax.GetLocation())); } }, OperationKind.FieldInitializer); context.RegisterOperationAction( (operationContext) => { IParameterInitializerOperation equalsValue = (IParameterInitializerOperation)operationContext.Operation; if (equalsValue.Parameter.Name.StartsWith("F")) { operationContext.ReportDiagnostic(Diagnostic.Create(EqualsValueDescriptor, equalsValue.Syntax.GetLocation())); } }, OperationKind.ParameterInitializer); } } /// <summary>Analyzer used to test None IOperations.</summary> public class NoneOperationTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; // We should not see this warning triggered by any code public static readonly DiagnosticDescriptor NoneOperationDescriptor = new DiagnosticDescriptor( "NoneOperation", "None operation found", "An IOperation of None kind is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(NoneOperationDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic(Diagnostic.Create(NoneOperationDescriptor, operationContext.Operation.Syntax.GetLocation())); }, // None kind is only supposed to be used internally and will not actually register actions. OperationKind.None); } } public class AddressOfTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor AddressOfDescriptor = new DiagnosticDescriptor( "AddressOfOperation", "AddressOf operation found", "An AddressOf operation found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor InvalidAddressOfReferenceDescriptor = new DiagnosticDescriptor( "InvalidAddressOfReference", "Invalid AddressOf reference found", "An invalid AddressOf reference found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(AddressOfDescriptor, InvalidAddressOfReferenceDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var addressOfOperation = (IAddressOfOperation)operationContext.Operation; operationContext.ReportDiagnostic(Diagnostic.Create(AddressOfDescriptor, addressOfOperation.Syntax.GetLocation())); if (addressOfOperation.Reference.Kind == OperationKind.Invalid && addressOfOperation.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidAddressOfReferenceDescriptor, addressOfOperation.Reference.Syntax.GetLocation())); } }, OperationKind.AddressOf); } } /// <summary>Analyzer used to test LambdaExpression IOperations.</summary> public class LambdaTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor LambdaExpressionDescriptor = new DiagnosticDescriptor( "LambdaExpression", "Lambda expression found", "A Lambda expression is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor TooManyStatementsInLambdaExpressionDescriptor = new DiagnosticDescriptor( "TooManyStatementsInLambdaExpression", "Too many statements in a Lambda expression", "More than 3 statements in a Lambda expression", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); // This warning should never be triggered. public static readonly DiagnosticDescriptor NoneOperationInLambdaExpressionDescriptor = new DiagnosticDescriptor( "NoneOperationInLambdaExpression", "None Operation found in Lambda expression", "None Operation is found Lambda expression", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(LambdaExpressionDescriptor, TooManyStatementsInLambdaExpressionDescriptor, NoneOperationInLambdaExpressionDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var lambdaExpression = (IAnonymousFunctionOperation)operationContext.Operation; operationContext.ReportDiagnostic(Diagnostic.Create(LambdaExpressionDescriptor, operationContext.Operation.Syntax.GetLocation())); var block = lambdaExpression.Body; // TODO: Can this possibly be null? Remove check if not. if (block == null) { return; } if (block.Operations.Length > 3) { operationContext.ReportDiagnostic(Diagnostic.Create(TooManyStatementsInLambdaExpressionDescriptor, operationContext.Operation.Syntax.GetLocation())); } bool flag = false; foreach (var statement in block.Operations) { if (statement.Kind == OperationKind.None) { flag = true; break; } } if (flag) { operationContext.ReportDiagnostic(Diagnostic.Create(NoneOperationInLambdaExpressionDescriptor, operationContext.Operation.Syntax.GetLocation())); } }, OperationKind.AnonymousFunction); } } public class StaticMemberTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor StaticMemberDescriptor = new DiagnosticDescriptor( "StaticMember", "Static member found", "A static member reference expression is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); // We should not see this warning triggered by any code public static readonly DiagnosticDescriptor StaticMemberWithInstanceDescriptor = new DiagnosticDescriptor( "StaticMemberWithInstance", "Static member with non null Instance found", "A static member reference with non null Instance is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(StaticMemberDescriptor, StaticMemberWithInstanceDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var operation = operationContext.Operation; ISymbol memberSymbol; IOperation receiver; switch (operation.Kind) { case OperationKind.FieldReference: memberSymbol = ((IFieldReferenceOperation)operation).Field; receiver = ((IFieldReferenceOperation)operation).Instance; break; case OperationKind.PropertyReference: memberSymbol = ((IPropertyReferenceOperation)operation).Property; receiver = ((IPropertyReferenceOperation)operation).Instance; break; case OperationKind.EventReference: memberSymbol = ((IEventReferenceOperation)operation).Event; receiver = ((IEventReferenceOperation)operation).Instance; break; case OperationKind.MethodReference: memberSymbol = ((IMethodReferenceOperation)operation).Method; receiver = ((IMethodReferenceOperation)operation).Instance; break; case OperationKind.Invocation: memberSymbol = ((IInvocationOperation)operation).TargetMethod; receiver = ((IInvocationOperation)operation).Instance; break; default: throw new ArgumentException(); } if (memberSymbol.IsStatic) { operationContext.ReportDiagnostic(Diagnostic.Create(StaticMemberDescriptor, operation.Syntax.GetLocation())); if (receiver != null) { operationContext.ReportDiagnostic(Diagnostic.Create(StaticMemberWithInstanceDescriptor, operation.Syntax.GetLocation())); } } }, OperationKind.FieldReference, OperationKind.PropertyReference, OperationKind.EventReference, OperationKind.MethodReference, OperationKind.Invocation); } } public class LabelOperationsTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor LabelDescriptor = new DiagnosticDescriptor( "Label", "Label found", "A label was was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor GotoDescriptor = new DiagnosticDescriptor( "Goto", "Goto found", "A goto was was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(LabelDescriptor, GotoDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { ILabelSymbol label = ((ILabeledOperation)operationContext.Operation).Label; if (label.Name == "Wilma" || label.Name == "Betty") { operationContext.ReportDiagnostic(Diagnostic.Create(LabelDescriptor, operationContext.Operation.Syntax.GetLocation())); } }, OperationKind.Labeled); context.RegisterOperationAction( (operationContext) => { IBranchOperation branch = (IBranchOperation)operationContext.Operation; if (branch.BranchKind == BranchKind.GoTo) { ILabelSymbol label = branch.Target; if (label.Name == "Wilma" || label.Name == "Betty") { operationContext.ReportDiagnostic(Diagnostic.Create(GotoDescriptor, branch.Syntax.GetLocation())); } } }, OperationKind.Branch); } } public class UnaryAndBinaryOperationsTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor OperatorAddMethodDescriptor = new DiagnosticDescriptor( "OperatorAddMethod", "Operator Add method found", "An operator Add method was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor OperatorMinusMethodDescriptor = new DiagnosticDescriptor( "OperatorMinusMethod", "Operator Minus method found", "An operator Minus method was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor DoubleMultiplyDescriptor = new DiagnosticDescriptor( "DoubleMultiply", "Double multiply found", "A double multiply was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor BooleanNotDescriptor = new DiagnosticDescriptor( "BooleanNot", "Boolean not found", "A boolean not was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(OperatorAddMethodDescriptor, OperatorMinusMethodDescriptor, DoubleMultiplyDescriptor, BooleanNotDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { IBinaryOperation binary = (IBinaryOperation)operationContext.Operation; if (binary.OperatorKind == BinaryOperatorKind.Add && binary.OperatorMethod != null && binary.OperatorMethod.Name.Contains("Addition")) { operationContext.ReportDiagnostic(Diagnostic.Create(OperatorAddMethodDescriptor, binary.Syntax.GetLocation())); } if (binary.OperatorKind == BinaryOperatorKind.Multiply && binary.Type.SpecialType == SpecialType.System_Double) { operationContext.ReportDiagnostic(Diagnostic.Create(DoubleMultiplyDescriptor, binary.Syntax.GetLocation())); } }, OperationKind.Binary); context.RegisterOperationAction( (operationContext) => { IUnaryOperation unary = (IUnaryOperation)operationContext.Operation; if (unary.OperatorKind == UnaryOperatorKind.Minus && unary.OperatorMethod != null && unary.OperatorMethod.Name.Contains("UnaryNegation")) { operationContext.ReportDiagnostic(Diagnostic.Create(OperatorMinusMethodDescriptor, unary.Syntax.GetLocation())); } if (unary.OperatorKind == UnaryOperatorKind.Not) { operationContext.ReportDiagnostic(Diagnostic.Create(BooleanNotDescriptor, unary.Syntax.GetLocation())); } if (unary.OperatorKind == UnaryOperatorKind.BitwiseNegation) { operationContext.ReportDiagnostic(Diagnostic.Create(BooleanNotDescriptor, unary.Syntax.GetLocation())); } }, OperationKind.Unary); } } public class BinaryOperatorVBTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor BinaryUserDefinedOperatorDescriptor = new DiagnosticDescriptor( "BinaryUserDefinedOperator", "Binary user defined operator found", "A Binary user defined operator {0} is found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(BinaryUserDefinedOperatorDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var binary = (IBinaryOperation)operationContext.Operation; if (binary.OperatorMethod != null) { operationContext.ReportDiagnostic( Diagnostic.Create(BinaryUserDefinedOperatorDescriptor, binary.Syntax.GetLocation(), binary.OperatorKind.ToString())); } }, OperationKind.Binary); } } public class OperatorPropertyPullerTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor BinaryOperatorDescriptor = new DiagnosticDescriptor( "BinaryOperator", "Binary operator found", "A Binary operator {0} was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor UnaryOperatorDescriptor = new DiagnosticDescriptor( "UnaryOperator", "Unary operator found", "A Unary operator {0} was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(BinaryOperatorDescriptor, UnaryOperatorDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var binary = (IBinaryOperation)operationContext.Operation; var left = binary.LeftOperand; var right = binary.RightOperand; if (!left.HasErrors(operationContext.Compilation, operationContext.CancellationToken) && !right.HasErrors(operationContext.Compilation, operationContext.CancellationToken) && binary.OperatorMethod == null) { if (left.Kind == OperationKind.LocalReference) { var leftLocal = ((ILocalReferenceOperation)left).Local; if (leftLocal.Name == "x") { if (right.Kind == OperationKind.Literal) { var rightValue = right.ConstantValue; if (rightValue.HasValue && rightValue.Value is int && (int)rightValue.Value == 10) { operationContext.ReportDiagnostic( Diagnostic.Create(BinaryOperatorDescriptor, binary.Syntax.GetLocation(), binary.OperatorKind.ToString())); } } } } } }, OperationKind.Binary); context.RegisterOperationAction( (operationContext) => { var unary = (IUnaryOperation)operationContext.Operation; var operand = unary.Operand; if (operand.Kind == OperationKind.LocalReference) { var operandLocal = ((ILocalReferenceOperation)operand).Local; if (operandLocal.Name == "x") { if (!operand.HasErrors(operationContext.Compilation, operationContext.CancellationToken) && unary.OperatorMethod == null) { operationContext.ReportDiagnostic( Diagnostic.Create(UnaryOperatorDescriptor, unary.Syntax.GetLocation(), unary.OperatorKind.ToString())); } } } }, OperationKind.Unary); } } public class NullOperationSyntaxTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; // We should not see this warning triggered by any code public static readonly DiagnosticDescriptor NullOperationSyntaxDescriptor = new DiagnosticDescriptor( "NullOperationSyntax", "null operation Syntax found", "An IOperation with Syntax property of value null is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); // since we don't expect to see the first diagnostic, we created this one to make sure // the test didn't pass because the analyzer crashed. public static readonly DiagnosticDescriptor ParamsArrayOperationDescriptor = new DiagnosticDescriptor( "ParamsArray", "Params array argument found", "A params array argument is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(NullOperationSyntaxDescriptor, ParamsArrayOperationDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var nullList = new List<IOperation>(); var paramsList = new List<IOperation>(); var collector = new Walker(nullList, paramsList); collector.Visit(operationContext.Operation); foreach (var nullSyntaxOperation in nullList) { operationContext.ReportDiagnostic( Diagnostic.Create(NullOperationSyntaxDescriptor, null)); } foreach (var paramsarrayArgumentOperation in paramsList) { operationContext.ReportDiagnostic( Diagnostic.Create(ParamsArrayOperationDescriptor, paramsarrayArgumentOperation.Syntax.GetLocation())); } }, OperationKind.Invocation); } // this OperationWalker collect: // 1. all the operation with null Syntax property // 2. all the params array argument operations private sealed class Walker : OperationWalker { private readonly List<IOperation> _nullList; private readonly List<IOperation> _paramsList; public Walker(List<IOperation> nullList, List<IOperation> paramsList) { _nullList = nullList; _paramsList = paramsList; } public override void Visit(IOperation operation) { if (operation != null) { if (operation.Syntax == null) { _nullList.Add(operation); } if (operation.Kind == OperationKind.Argument) { if (((IArgumentOperation)operation).ArgumentKind == ArgumentKind.ParamArray) { _paramsList.Add(operation); } } } base.Visit(operation); } } } public class InvalidOperatorExpressionTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor InvalidBinaryDescriptor = new DiagnosticDescriptor( "InvalidBinary", "Invalid binary expression operation with BinaryOperationKind.Invalid", "An Invalid binary expression operation with BinaryOperationKind.Invalid is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor InvalidUnaryDescriptor = new DiagnosticDescriptor( "InvalidUnary", "Invalid unary expression operation with UnaryOperationKind.Invalid", "An Invalid unary expression operation with UnaryOperationKind.Invalid is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor InvalidIncrementDescriptor = new DiagnosticDescriptor( "InvalidIncrement", "Invalid increment expression operation with ICompoundAssignmentExpression.BinaryOperationKind == BinaryOperationKind.Invalid", "An Invalid increment expression operation with ICompoundAssignmentExpression.BinaryOperationKind == BinaryOperationKind.Invalid is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(InvalidBinaryDescriptor, InvalidUnaryDescriptor, InvalidIncrementDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var operation = operationContext.Operation; if (operation.Kind == OperationKind.Binary) { var binary = (IBinaryOperation)operation; if (binary.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidBinaryDescriptor, binary.Syntax.GetLocation())); } } else if (operation.Kind == OperationKind.Unary) { var unary = (IUnaryOperation)operation; if (unary.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidUnaryDescriptor, unary.Syntax.GetLocation())); } } else if (operation.Kind == OperationKind.Increment) { var inc = (IIncrementOrDecrementOperation)operation; if (inc.HasErrors(operationContext.Compilation)) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidIncrementDescriptor, inc.Syntax.GetLocation())); } } }, OperationKind.Binary, OperationKind.Unary, OperationKind.Increment); } } public class ConditionalAccessOperationTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor ConditionalAccessOperationDescriptor = new DiagnosticDescriptor( "ConditionalAccessOperation", "Conditional access operation found", "Conditional access operation was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor ConditionalAccessInstanceOperationDescriptor = new DiagnosticDescriptor( "ConditionalAccessInstanceOperation", "Conditional access instance operation found", "Conditional access instance operation was found", "Testing", DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(ConditionalAccessOperationDescriptor, ConditionalAccessInstanceOperationDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { IConditionalAccessOperation conditionalAccess = (IConditionalAccessOperation)operationContext.Operation; if (conditionalAccess.WhenNotNull != null && conditionalAccess.Operation != null) { operationContext.ReportDiagnostic(Diagnostic.Create(ConditionalAccessOperationDescriptor, conditionalAccess.Syntax.GetLocation())); } }, OperationKind.ConditionalAccess); context.RegisterOperationAction( (operationContext) => { IConditionalAccessInstanceOperation conditionalAccessInstance = (IConditionalAccessInstanceOperation)operationContext.Operation; operationContext.ReportDiagnostic(Diagnostic.Create(ConditionalAccessInstanceOperationDescriptor, conditionalAccessInstance.Syntax.GetLocation())); }, OperationKind.ConditionalAccessInstance); // https://github.com/dotnet/roslyn/issues/21294 //context.RegisterOperationAction( // (operationContext) => // { // IPlaceholderExpression placeholder = (IPlaceholderExpression)operationContext.Operation; // operationContext.ReportDiagnostic(Diagnostic.Create(ConditionalAccessInstanceOperationDescriptor, placeholder.Syntax.GetLocation())); // }, // OperationKind.PlaceholderExpression); } } public class ConversionExpressionCSharpTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor InvalidConversionExpressionDescriptor = new DiagnosticDescriptor( "InvalidConversionExpression", "Invalid conversion expression", "Invalid conversion expression.", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(InvalidConversionExpressionDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var conversion = (IConversionOperation)operationContext.Operation; if (conversion.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { operationContext.ReportDiagnostic(Diagnostic.Create(InvalidConversionExpressionDescriptor, conversion.Syntax.GetLocation())); } }, OperationKind.Conversion); } } public class ForLoopConditionCrashVBTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor ForLoopConditionCrashDescriptor = new DiagnosticDescriptor( "ForLoopConditionCrash", "Ensure ForLoopCondition property doesn't crash", "Ensure ForLoopCondition property doesn't crash", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(ForLoopConditionCrashDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { ILoopOperation loop = (ILoopOperation)operationContext.Operation; if (loop.LoopKind == LoopKind.ForTo) { var forLoop = (IForToLoopOperation)loop; var forCondition = forLoop.LimitValue; if (forCondition.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { // Generate a warning to prove we didn't crash operationContext.ReportDiagnostic(Diagnostic.Create(ForLoopConditionCrashDescriptor, forLoop.LimitValue.Syntax.GetLocation())); } } }, OperationKind.Loop); } } public class TrueFalseUnaryOperationTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor UnaryTrueDescriptor = new DiagnosticDescriptor( "UnaryTrue", "An unary True operation is found", "A unary True operation is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor UnaryFalseDescriptor = new DiagnosticDescriptor( "UnaryFalse", "An unary False operation is found", "A unary False operation is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(UnaryTrueDescriptor, UnaryFalseDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var unary = (IUnaryOperation)operationContext.Operation; if (unary.OperatorKind == UnaryOperatorKind.True) { operationContext.ReportDiagnostic(Diagnostic.Create(UnaryTrueDescriptor, unary.Syntax.GetLocation())); } else if (unary.OperatorKind == UnaryOperatorKind.False) { operationContext.ReportDiagnostic(Diagnostic.Create(UnaryFalseDescriptor, unary.Syntax.GetLocation())); } }, OperationKind.Unary); } } public class AssignmentOperationSyntaxTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor AssignmentOperationDescriptor = new DiagnosticDescriptor( "AssignmentOperation", "An assignment operation is found", "An assignment operation is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor AssignmentSyntaxDescriptor = new DiagnosticDescriptor( "AssignmentSyntax", "An assignment syntax is found", "An assignment syntax is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(AssignmentOperationDescriptor, AssignmentSyntaxDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic(Diagnostic.Create(AssignmentOperationDescriptor, operationContext.Operation.Syntax.GetLocation())); }, OperationKind.SimpleAssignment); context.RegisterSyntaxNodeAction( (syntaxContext) => { syntaxContext.ReportDiagnostic(Diagnostic.Create(AssignmentSyntaxDescriptor, syntaxContext.Node.GetLocation())); }, CSharp.SyntaxKind.SimpleAssignmentExpression); } } public class LiteralTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor LiteralDescriptor = new DiagnosticDescriptor( "Literal", "A literal is found", "A literal of value {0} is found", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(LiteralDescriptor); } } public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { var literal = (ILiteralOperation)operationContext.Operation; operationContext.ReportDiagnostic(Diagnostic.Create(LiteralDescriptor, literal.Syntax.GetLocation(), literal.Syntax.ToString())); }, OperationKind.Literal); } } // This analyzer is to test operation action registration method in AnalysisContext public class AnalysisContextAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor OperationActionDescriptor = new DiagnosticDescriptor( "AnalysisContext", "An operation related action is invoked", "An {0} action is invoked in {1} context.", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(OperationActionDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic( Diagnostic.Create(OperationActionDescriptor, operationContext.Operation.Syntax.GetLocation(), "Operation", "Analysis")); }, OperationKind.Literal); } } // This analyzer is to test operation action registration method in CompilationStartAnalysisContext public class CompilationStartAnalysisContextAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor OperationActionDescriptor = new DiagnosticDescriptor( "CompilationStartAnalysisContext", "An operation related action is invoked", "An {0} action is invoked in {1} context.", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(OperationActionDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterCompilationStartAction( (compilationStartContext) => { compilationStartContext.RegisterOperationAction( (operationContext) => { operationContext.ReportDiagnostic( Diagnostic.Create(OperationActionDescriptor, operationContext.Operation.Syntax.GetLocation(), "Operation", "CompilationStart within Analysis")); }, OperationKind.Literal); }); } } // This analyzer is to test GetOperation method in SemanticModel public class SemanticModelAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; public static readonly DiagnosticDescriptor GetOperationDescriptor = new DiagnosticDescriptor( "GetOperation", "An IOperation is returned by SemanticModel", "An IOperation is returned by SemanticModel.", ReliabilityCategory, DiagnosticSeverity.Warning, isEnabledByDefault: true); public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(GetOperationDescriptor); public sealed override void Initialize(AnalysisContext context) { context.RegisterSyntaxNodeAction( (syntaxContext) => { var node = syntaxContext.Node; var model = syntaxContext.SemanticModel; if (model.GetOperation(node) != null) { syntaxContext.ReportDiagnostic(Diagnostic.Create(GetOperationDescriptor, node.GetLocation())); } }, Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression); context.RegisterSyntaxNodeAction( (syntaxContext) => { var node = syntaxContext.Node; var model = syntaxContext.SemanticModel; if (model.GetOperation(node) != null) { syntaxContext.ReportDiagnostic(Diagnostic.Create(GetOperationDescriptor, node.GetLocation())); } }, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.NumericLiteralExpression); } } }