private static void BinaryExpressionCheck(EnabledRules rules, SyntaxNode node, SemanticModel semanticModel, bool isAssignmentToReadonly, Action <Diagnostic> reportDiagnostic, string filePath, CancellationToken cancellationToken) { var binaryExpression = node as BinaryExpressionSyntax; bool conversionRuleEnabled = rules.TryGet(AllocationRules.ValueTypeToReferenceTypeConversionRule.Id, out DiagnosticDescriptor conversionRule); // as expression if (conversionRuleEnabled && binaryExpression.IsKind(SyntaxKind.AsExpression) && binaryExpression.Left != null && binaryExpression.Right != null) { var leftT = semanticModel.GetTypeInfo(binaryExpression.Left, cancellationToken); var rightT = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken); if (leftT.Type?.IsValueType == true && rightT.Type?.IsReferenceType == true) { reportDiagnostic(Diagnostic.Create(conversionRule, binaryExpression.Left.GetLocation(), EmptyMessageArgs)); } return; } if (binaryExpression.Right != null) { if (conversionRuleEnabled) { var assignmentExprConversionInfo = semanticModel.GetConversion(binaryExpression.Right, cancellationToken); CheckTypeConversion(conversionRule, assignmentExprConversionInfo, reportDiagnostic, binaryExpression.Right.GetLocation(), filePath); } var assignmentExprTypeInfo = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken); CheckDelegateCreation(rules, binaryExpression.Right, assignmentExprTypeInfo, semanticModel, isAssignmentToReadonly, reportDiagnostic, binaryExpression.Right.GetLocation(), filePath, cancellationToken); return; } }
protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules) { var node = context.Node; var semanticModel = context.SemanticModel; Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic; var cancellationToken = context.CancellationToken; string filePath = node.SyntaxTree.FilePath; var binaryExpressions = node.DescendantNodesAndSelf().OfType <BinaryExpressionSyntax>().Reverse(); // need inner most expressions int stringConcatenationCount = 0; foreach (var binaryExpression in binaryExpressions) { if (binaryExpression.Left == null || binaryExpression.Right == null) { continue; } bool isConstant = semanticModel.GetConstantValue(binaryExpression, cancellationToken).HasValue; if (isConstant) { continue; } // TODO: TryGetEnabled() var left = semanticModel.GetTypeInfo(binaryExpression.Left, cancellationToken); var right = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken); if (rules.TryGet(AllocationRules.ValueTypeToReferenceTypeInAStringConcatenationRule.Id, out var rule)) { var leftConversion = semanticModel.GetConversion(binaryExpression.Left, cancellationToken); var rightConversion = semanticModel.GetConversion(binaryExpression.Right, cancellationToken); CheckTypeConversion(rule, left, leftConversion, reportDiagnostic, binaryExpression.Left.GetLocation(), filePath); CheckTypeConversion(rule, right, rightConversion, reportDiagnostic, binaryExpression.Right.GetLocation(), filePath); } // regular string allocation if (rules.IsEnabled(AllocationRules.StringConcatenationAllocationRule.Id)) { if (left.Type?.SpecialType == SpecialType.System_String || right.Type?.SpecialType == SpecialType.System_String) { stringConcatenationCount++; } } } if (stringConcatenationCount > 3) { var rule = rules.Get(AllocationRules.StringConcatenationAllocationRule.Id); reportDiagnostic(Diagnostic.Create(rule, node.GetLocation(), EmptyMessageArgs)); } }
protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules) { var node = context.Node; var semanticModel = context.SemanticModel; Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic; var cancellationToken = context.CancellationToken; string filePath = node.SyntaxTree.FilePath; // An InitializerExpressionSyntax has an ObjectCreationExpressionSyntax as it's parent, i.e // var testing = new TestClass { Name = "Bob" }; // | |--------------| <- InitializerExpressionSyntax or SyntaxKind.ObjectInitializerExpression // |----------------------------| <- ObjectCreationExpressionSyntax or SyntaxKind.ObjectCreationExpression if (rules.TryGet(AllocationRules.InitializerCreationRule.Id, out var creationRule)) { var initializerExpression = node as InitializerExpressionSyntax; if (initializerExpression?.Parent is ObjectCreationExpressionSyntax) { var objectCreation = node.Parent as ObjectCreationExpressionSyntax; var typeInfo = semanticModel.GetTypeInfo(objectCreation, cancellationToken); if (typeInfo.ConvertedType?.TypeKind != TypeKind.Error && typeInfo.ConvertedType?.IsReferenceType == true && objectCreation.Parent?.IsKind(SyntaxKind.EqualsValueClause) == true && objectCreation.Parent?.Parent?.IsKind(SyntaxKind.VariableDeclarator) == true) { reportDiagnostic(Diagnostic.Create(creationRule, ((VariableDeclaratorSyntax)objectCreation.Parent.Parent).Identifier.GetLocation(), EmptyMessageArgs)); return; } } } if (rules.TryGet(AllocationRules.ImplicitArrayCreationRule.Id, out var arrayCreationRule)) { if (node is ImplicitArrayCreationExpressionSyntax implicitArrayExpression) { reportDiagnostic(Diagnostic.Create(arrayCreationRule, implicitArrayExpression.NewKeyword.GetLocation(), EmptyMessageArgs)); return; } } if (rules.TryGet(AllocationRules.AnonymousNewObjectRule.Id, out var anonCreationRule)) { if (node is AnonymousObjectCreationExpressionSyntax newAnon) { reportDiagnostic(Diagnostic.Create(anonCreationRule, newAnon.NewKeyword.GetLocation(), EmptyMessageArgs)); return; } } if (rules.TryGet(AllocationRules.NewArrayRule.Id, out var newArrayRule)) { if (node is ArrayCreationExpressionSyntax newArr) { reportDiagnostic(Diagnostic.Create(newArrayRule, newArr.NewKeyword.GetLocation(), EmptyMessageArgs)); return; } } if (rules.TryGet(AllocationRules.NewObjectRule.Id, out var newObjectRule)) { if (node is ObjectCreationExpressionSyntax newObj) { var typeInfo = semanticModel.GetTypeInfo(newObj, cancellationToken); if (typeInfo.ConvertedType != null && typeInfo.ConvertedType.TypeKind != TypeKind.Error && typeInfo.ConvertedType.IsReferenceType) { reportDiagnostic(Diagnostic.Create(newObjectRule, newObj.NewKeyword.GetLocation(), EmptyMessageArgs)); } return; } } if (rules.TryGet(AllocationRules.LetCauseRule.Id, out var letClauseRule)) { if (node is LetClauseSyntax letKind) { reportDiagnostic(Diagnostic.Create(letClauseRule, letKind.LetKeyword.GetLocation(), EmptyMessageArgs)); return; } } }
protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules) { var node = context.Node; var semanticModel = context.SemanticModel; var cancellationToken = context.CancellationToken; Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic; string filePath = node.SyntaxTree.FilePath; bool assignedToReadonlyFieldOrProperty = (context.ContainingSymbol as IFieldSymbol)?.IsReadOnly == true || (context.ContainingSymbol as IPropertySymbol)?.IsReadOnly == true; bool isValueTypeToReferenceRuleEnabled = rules.TryGet(AllocationRules.ValueTypeToReferenceTypeConversionRule.Id, out DiagnosticDescriptor valueTypeToReferenceRule); // this.fooObjCall(10); // new myobject(10); if (node is ArgumentSyntax) { ArgumentSyntaxCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken); } // object foo { get { return 0; } } if (isValueTypeToReferenceRuleEnabled && node is ReturnStatementSyntax) { ReturnStatementExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken); return; } // yield return 0 if (isValueTypeToReferenceRuleEnabled && node is YieldStatementSyntax) { YieldReturnStatementExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken); return; } // object a = x ?? 0; // var a = 10 as object; if (node is BinaryExpressionSyntax) { BinaryExpressionCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken); return; } // for (object i = 0;;) if (node is EqualsValueClauseSyntax) { EqualsValueClauseCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken); return; } // object = true ? 0 : obj if (isValueTypeToReferenceRuleEnabled && node is ConditionalExpressionSyntax) { ConditionalExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken); return; } // string a = $"{1}"; if (isValueTypeToReferenceRuleEnabled && node is InterpolationSyntax) { InterpolationCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken); return; } // var f = (object) if (isValueTypeToReferenceRuleEnabled && node is CastExpressionSyntax) { CastExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken); return; } // object Foo => 1 if (node is ArrowExpressionClauseSyntax) { ArrowExpressionCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken); return; } }
private static void CheckDelegateCreation(EnabledRules rules, SyntaxNode node, TypeInfo typeInfo, SemanticModel semanticModel, bool isAssignmentToReadonly, Action <Diagnostic> reportDiagnostic, Location location, string filePath, CancellationToken cancellationToken) { if (typeInfo.ConvertedType?.TypeKind != TypeKind.Delegate) { return; } if (rules.TryGet(AllocationRules.DelegateOnStructInstanceRule.Id, out var delegateOnStructRule)) { var symbolInfo = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol; if (symbolInfo?.ContainingType?.IsValueType == true && symbolInfo.GetType().Name == "SourceMemberMethodSymbol") { reportDiagnostic(Diagnostic.Create(delegateOnStructRule, location, EmptyMessageArgs)); } } // new Action<Foo>(MethodGroup); should skip this one var insideObjectCreation = node?.Parent?.Parent?.Parent?.Kind() == SyntaxKind.ObjectCreationExpression; if (node is ParenthesizedLambdaExpressionSyntax || node is SimpleLambdaExpressionSyntax || node is AnonymousMethodExpressionSyntax || node is ObjectCreationExpressionSyntax || insideObjectCreation) { // skip this, because it's intended. return; } if (rules.IsEnabled(AllocationRules.MethodGroupAllocationRule.Id) && node.IsKind(SyntaxKind.IdentifierName)) { if (semanticModel.GetSymbolInfo(node, cancellationToken).Symbol is IMethodSymbol) { reportDiagnostic(Diagnostic.Create(rules.Get(AllocationRules.MethodGroupAllocationRule.Id), location, EmptyMessageArgs)); } } else if (node.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { var memberAccess = node as MemberAccessExpressionSyntax; if (semanticModel.GetSymbolInfo(memberAccess.Name, cancellationToken).Symbol is IMethodSymbol) { if (isAssignmentToReadonly && rules.TryGet(AllocationRules.ReadonlyMethodGroupAllocationRule.Id, out var readonlyMethodGroupAllocationRule)) { reportDiagnostic(Diagnostic.Create(readonlyMethodGroupAllocationRule, location, EmptyMessageArgs)); } else if (rules.TryGet(AllocationRules.MethodGroupAllocationRule.Id, out var methodGroupAllocationRule)) { reportDiagnostic(Diagnostic.Create(methodGroupAllocationRule, location, EmptyMessageArgs)); } } } else if (node is ArrowExpressionClauseSyntax) { if (rules.TryGet(AllocationRules.MethodGroupAllocationRule.Id, out var methodGroupAllocationRule)) { var arrowClause = node as ArrowExpressionClauseSyntax; if (semanticModel.GetSymbolInfo(arrowClause.Expression, cancellationToken).Symbol is IMethodSymbol) { reportDiagnostic(Diagnostic.Create(methodGroupAllocationRule, location, EmptyMessageArgs)); } } } }
protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules) { if (!rules.TryGet(AllocationRules.ReferenceTypeEnumeratorRule.Id, out DiagnosticDescriptor rule)) { return; } var node = context.Node; var semanticModel = context.SemanticModel; Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic; var cancellationToken = context.CancellationToken; string filePath = node.SyntaxTree.FilePath; if (node is ForEachStatementSyntax foreachExpression) { var typeInfo = semanticModel.GetTypeInfo(foreachExpression.Expression, cancellationToken); if (typeInfo.Type == null) { return; } if (typeInfo.Type.Name == "String" && typeInfo.Type.ContainingNamespace.Name == "System") { // Special case for System.String which is optmizined by // the compiler and does not result in an allocation. return; } // Regular way of getting the enumerator ImmutableArray <ISymbol> enumerator = typeInfo.Type.GetMembers("GetEnumerator"); if ((enumerator == null || enumerator.Length == 0) && typeInfo.ConvertedType != null) { // 1st we try and fallback to using the ConvertedType enumerator = typeInfo.ConvertedType.GetMembers("GetEnumerator"); } if ((enumerator == null || enumerator.Length == 0) && typeInfo.Type.Interfaces != null) { // 2nd fallback, now we try and find the IEnumerable Interface explicitly var iEnumerable = typeInfo.Type.Interfaces.Where(i => i.Name == "IEnumerable").ToImmutableArray(); if (iEnumerable != null && iEnumerable.Length > 0) { enumerator = iEnumerable[0].GetMembers("GetEnumerator"); } } if (enumerator != null && enumerator.Length > 0) { if (enumerator[0] is IMethodSymbol methodSymbol) // probably should do something better here, hack. { if (methodSymbol.ReturnType.IsReferenceType && methodSymbol.ReturnType.SpecialType != SpecialType.System_Collections_IEnumerator) { reportDiagnostic(Diagnostic.Create(rule, foreachExpression.InKeyword.GetLocation(), EmptyMessageArgs)); } } } return; } if (node is InvocationExpressionSyntax invocationExpression) { var methodInfo = semanticModel.GetSymbolInfo(invocationExpression, cancellationToken).Symbol as IMethodSymbol; if (methodInfo?.ReturnType != null && methodInfo.ReturnType.IsReferenceType) { if (methodInfo.ReturnType.AllInterfaces != null) { foreach (var @interface in methodInfo.ReturnType.AllInterfaces) { if (@interface.SpecialType == SpecialType.System_Collections_Generic_IEnumerator_T || @interface.SpecialType == SpecialType.System_Collections_IEnumerator) { reportDiagnostic(Diagnostic.Create(rule, invocationExpression.GetLocation(), EmptyMessageArgs)); } } } } } }
protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules) { var node = context.Node; var semanticModel = context.SemanticModel; var cancellationToken = context.CancellationToken; Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic; bool genericRuleEnabled = rules.TryGet(AllocationRules.LambaOrAnonymousMethodInGenericMethodRule.Id, out DiagnosticDescriptor genericRule); bool driverRuleEnabled = rules.TryGet(AllocationRules.ClosureDriverRule.Id, out DiagnosticDescriptor driverRule); bool captureRuleEnabled = rules.TryGet(AllocationRules.ClosureCaptureRule.Id, out DiagnosticDescriptor captureRule); SyntaxNode genericCheckNode = null; Location location = null; DataFlowAnalysis flowAnalysis = null; var anonExpr = node as AnonymousMethodExpressionSyntax; if (anonExpr?.Block?.ChildNodes() != null && anonExpr.Block.ChildNodes().Any()) { if (genericRuleEnabled) { genericCheckNode = node; location = anonExpr.DelegateKeyword.GetLocation(); } if (captureRuleEnabled || driverRuleEnabled) { flowAnalysis = semanticModel.AnalyzeDataFlow(anonExpr.Block.ChildNodes().First(), anonExpr.Block.ChildNodes().Last()); location = anonExpr.DelegateKeyword.GetLocation(); } } if (node is SimpleLambdaExpressionSyntax lambdaExpr) { if (genericRuleEnabled) { genericCheckNode = node; location = lambdaExpr.ArrowToken.GetLocation(); } if (captureRuleEnabled || driverRuleEnabled) { flowAnalysis = semanticModel.AnalyzeDataFlow(lambdaExpr); location = lambdaExpr.ArrowToken.GetLocation(); } } if (node is ParenthesizedLambdaExpressionSyntax parenLambdaExpr) { if (genericRuleEnabled) { genericCheckNode = node; location = parenLambdaExpr.ArrowToken.GetLocation(); } if (captureRuleEnabled || driverRuleEnabled) { flowAnalysis = semanticModel.AnalyzeDataFlow(parenLambdaExpr); location = parenLambdaExpr.ArrowToken.GetLocation(); } } if (genericCheckNode != null) { GenericMethodCheck(genericRule, semanticModel, genericCheckNode, location, reportDiagnostic, cancellationToken); } if (captureRuleEnabled) { ClosureCaptureDataFlowAnalysis(captureRule, flowAnalysis, reportDiagnostic, location); } if (driverRuleEnabled) { if (flowAnalysis?.Captured.Length > 0) { reportDiagnostic(Diagnostic.Create(driverRule, location, new[] { string.Join(",", flowAnalysis.Captured.Select(x => x.Name)) })); } } }