private static void ReportError(SyntaxNodeAnalysisContext context, ISymbol local, RefCounterStatus status) { if (!status.IsSkip) { if (status.AssignCounter > 1) { var diagnostic = Diagnostic.Create(RuleRefCounterSkip, local.Locations[0], local.Name, string.Format("assigned more than once: {0}", status.AssignInfo)); context.ReportDiagnostic(diagnostic); } else if (status.RefCounter != 0) { var diagnostic = Diagnostic.Create(RuleRefCounterError, local.Locations[0], local.Name, status.RefCounter, status.RefInfo); context.ReportDiagnostic(diagnostic); } } else { var diagnostic = Diagnostic.Create(RuleRefCounterSkip, local.Locations[0], local.Name, status.SkipReason); context.ReportDiagnostic(diagnostic); } }
public static void ProcessReturnStatement(ISymbol local, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { using (var walker = ReturnValueWalker.Borrow(block, Search.TopLevel, semanticModel, cancellationToken)) { foreach (var value in walker) { var returnedSymbol = semanticModel.GetSymbolSafe(value, cancellationToken); if (SymbolComparer.Equals(local, returnedSymbol)) { var method = block.FirstAncestor <MethodDeclarationSyntax>(); var access = block.FirstAncestor <AccessorDeclarationSyntax>(); bool isGet = false; if (method != null) { isGet = KnownSymbol.IsGetMethodName(method.Identifier.ToString()); } else if (access != null) { isGet = true; } if (isGet) { status.RemainRef("return value from get method", value.GetLocation()); } else { status.ReleaseReference("return value from non get", value.GetLocation()); } } } } }
private static void HandleMethodParameter(SyntaxNodeAnalysisContext context, ParameterSyntax parameter) { var paramSympol = context.SemanticModel.GetDeclaredSymbol(parameter, context.CancellationToken); if (paramSympol != null) { var isRefCounter = RefCounter.IsRefCounterType(paramSympol.Type); if (isRefCounter) { var param = context.SemanticModel.GetDeclaredSymbolSafe(parameter, context.CancellationToken); if (param != null) { var method = parameter.FirstAncestorOrSelf <MethodDeclarationSyntax>(); var block = method?.Body; if (block == null) { return; } RefCounterStatus status = new RefCounterStatus(); if (method.Identifier.ToString().Equals(KnownSymbol.ReleaseReference)) { status.AcquireReference("param of ReleaseReference", method.GetLocation()); } ProcessLocalOrParamenterVar(context, param, block, status); } } } }
public static void ProcessRightOfAssignmentToField( ISymbol variable, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { List <AssignmentExpressionSyntax> rc = new List <AssignmentExpressionSyntax>(); block?.TryGetAssignment(variable, semanticModel, cancellationToken, rc); int count = 0; ISymbol field = null; Location loc = Location.None; foreach (AssignmentExpressionSyntax assignment in rc) { var classDef = block.FirstAncestor <ClassDeclarationSyntax>(); var classSymbol = CSharpExtensions.GetDeclaredSymbol(semanticModel, classDef, cancellationToken); if (IsRightOfAssignmentToField(semanticModel, cancellationToken, assignment.Left, out field)) { count++; loc = assignment.GetLocation(); if (!field.ContainingType.Equals(classSymbol)) { status.Skip("assigned to field/property of other class", loc); } if (LoopUtils.HasLoopBetween(assignment, block)) { status.Skip("loop between assignment to field/property and var block", loc); } } } if (count == 1) { status.ReleaseReference("assign to class field/property", loc); var methodBlock = block?.FirstAncestorOrSelf <BlockSyntax>(); if (methodBlock == null) { return; } if (RightSideOfAssignmentCount(field, methodBlock, semanticModel, cancellationToken) > 0) { status.Skip("var assigned to field/property, which is assigned to others", loc); } ChangeReferenceMethodUtils.ProcessIncDelRefInvocation(field, methodBlock, semanticModel, cancellationToken, status); ReturnUtils.ProcessReturnStatement(field, methodBlock, semanticModel, cancellationToken, status); } else if (count > 1) { status.Skip("multiple assign to field/property", loc); } }
public static void ProcessIncDelRefInvocation(ISymbol local, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { IncRefInvocationCount(local, block, semanticModel, cancellationToken, status); DelRefInvocationCount(local, block, semanticModel, cancellationToken, status); }
public static void ProcessRightSideOfAssignment(ISymbol local, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { ProcessRightOfAssignmentToField(local, block, semanticModel, cancellationToken, status); ProcessRightOfAssignmentToLocal(local, block, semanticModel, cancellationToken, status); }
public static void ProcessVariableInitialization( VariableDeclaratorSyntax variable, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus refCounterStatus) { if (variable.Initializer == null || variable.Initializer.Value == null) { //refCounterStatus.Skip("not initialized while declare"); } else { CalcAssignmentValue(variable.Initializer.Value, block, semanticModel, cancellationToken, refCounterStatus); } }
private static void HandleLocalVariable(SyntaxNodeAnalysisContext context, VariableDeclaratorSyntax variable) { if (context.SemanticModel.GetDeclaredSymbolSafe(variable, context.CancellationToken) is ILocalSymbol local) { var block = variable.FirstAncestorOrSelf <BlockSyntax>(); if (block == null) { return; } RefCounterStatus status = new RefCounterStatus(); AssignmentUtils.ProcessVariableInitialization(variable, block, context.SemanticModel, context.CancellationToken, status); ProcessLocalOrParamenterVar(context, local, block, status); } }
public static void CalcAssignmentValue(ExpressionSyntax variable, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus refCounterStatus) { if (InitializeFromCreation(variable)) { refCounterStatus.IncAssignCounter("initialize from new", variable.GetLocation()); refCounterStatus.AcquireReference("init from new", variable.GetLocation()); } else if (InitializeFromTypeCastParameter(variable, semanticModel, cancellationToken, out var param)) { refCounterStatus.IncAssignCounter("init cast from param", variable.GetLocation()); refCounterStatus.RemainRef("init cast from param", variable.GetLocation()); if (RightOfAssignmentUtils.RightSideOfAssignmentCount(param, block, semanticModel, cancellationToken) > 1) { refCounterStatus.Skip("cast from param also assigned to others", variable.GetLocation()); } } else if (InitializeFromNonGetMethod(variable, semanticModel, cancellationToken)) { refCounterStatus.IncAssignCounter("initialize from non-get method", variable.GetLocation()); refCounterStatus.AcquireReference("init from non-get method", variable.GetLocation()); } else if (InitializeFromGetMethod(variable, semanticModel, cancellationToken)) { refCounterStatus.IncAssignCounter("initialize from get method", variable.GetLocation()); refCounterStatus.RemainRef("init from get method", variable.GetLocation()); } else if (InitializeFromProperty(variable, semanticModel, cancellationToken)) { refCounterStatus.IncAssignCounter("initialize from field/property", variable.GetLocation()); refCounterStatus.RemainRef("init from property", variable.GetLocation()); // nothing } else if (InitializeFromElement(variable, semanticModel, cancellationToken)) { refCounterStatus.IncAssignCounter("initialize from element access", variable.GetLocation()); refCounterStatus.RemainRef("init from element access", variable.GetLocation()); } else // assigned from other local variable, class field, or other expression { refCounterStatus.IncAssignCounter("initialize from unkown", variable.GetLocation()); refCounterStatus.Skip("unsupported initialization", variable.GetLocation()); } }
private static void ProcessLocalOrParamenterVar(SyntaxNodeAnalysisContext context, ISymbol local, BlockSyntax block, RefCounterStatus status) { RightOfAssignmentUtils.ProcessRightSideOfAssignment(local, block, context.SemanticModel, context.CancellationToken, status); SystemCollectionUtils.ProcessAddedToCollection(local, block, context.SemanticModel, context.CancellationToken, status); LeftOfAssignmentUtils.ProcessLeftSideOfAssignment(local, block, context.SemanticModel, context.CancellationToken, status); ChangeReferenceMethodUtils.ProcessIncDelRefInvocation(local, block, context.SemanticModel, context.CancellationToken, status); AssignmentUtils.ProcessOutRefInvocation(local, block, context.SemanticModel, context.CancellationToken, status); ReturnUtils.ProcessReturnStatement(local, block, context.SemanticModel, context.CancellationToken, status); ReportError(context, local, status); }
public static void ProcessOutRefInvocation(ISymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { using (var pooledInvocations = InvocationWalker.Borrow(block)) { foreach (var invocation in pooledInvocations.Invocations) { foreach (ArgumentSyntax arg in invocation.ArgumentList.Arguments) { if (arg.RefOrOutKeyword.IsKind(SyntaxKind.OutKeyword) || arg.RefOrOutKeyword.IsKind(SyntaxKind.RefKeyword)) { var sym = ModelExtensions.GetSymbolInfo(semanticModel, arg.Expression, cancellationToken); ISymbol argSymbol = sym.Symbol; if (symbol.Equals(argSymbol)) { status.RemainRef("init from out/ref call", arg.GetLocation()); status.IncAssignCounter("out/ref call parameter", arg.GetLocation()); if (LoopUtils.HasLoopBetween(invocation, block)) { status.Skip("loop between ref/out call and var block", arg.GetLocation()); } } } } } } }
public static void ProcessAddedToCollection( ISymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { int count = 0; Location loc = Location.None; using (var pooledInvocations = InvocationWalker.Borrow(block)) { foreach (var invocation in pooledInvocations.Invocations) { bool isAddToCollection = false; var method = semanticModel.GetSymbolSafe(invocation, cancellationToken) as IMethodSymbol; if (method?.ReceiverType != null) { if (!isAddToCollection) { isAddToCollection = (method.ReceiverType.Is(KnownSymbol.IDictionary) || method.ReceiverType.Is(KnownSymbol.ICollection)) && method?.Name == "Add"; } if (!isAddToCollection) { isAddToCollection = method.ReceiverType.Is(KnownSymbol.QueueOfT) && method?.Name == "Enqueue"; } if (!isAddToCollection) { isAddToCollection = method.ReceiverType.Is(KnownSymbol.Queue) && method?.Name == "Enqueue"; } if (!isAddToCollection) { isAddToCollection = method.ReceiverType.Is(KnownSymbol.ListOfT) && method?.Name == "Insert"; } if (!isAddToCollection) { isAddToCollection = method.ReceiverType.Is(KnownSymbol.IRefCounterContainerOfT) && method?.Name == "Add"; } if (!isAddToCollection) { isAddToCollection = method.ReceiverType.Is(KnownSymbol.CollectionOfT) && method?.Name == "Add"; } if (!isAddToCollection) { isAddToCollection = method.ReceiverType.Is(KnownSymbol.LinkedListOfT) && (method?.Name == "AddLast" || method?.Name == "AddFirst"); } } if (isAddToCollection) { bool isInArg = false; foreach (ArgumentSyntax arg in invocation.ArgumentList.Arguments) { var sym = ModelExtensions.GetSymbolInfo(semanticModel, arg.Expression, cancellationToken); ISymbol argSymbol = sym.Symbol; if (symbol.Equals(argSymbol)) { isInArg = true; } } if (isInArg) { count++; loc = invocation.GetLocation(); if (LoopUtils.HasLoopBetween(invocation, block)) { status.Skip("loop between add to collection and var block", invocation.GetLocation()); } } } } } if (count == 1) { status.ReleaseReference("added to collection", loc); } }
private static int IncRefInvocationCount(ISymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { int count = 0; Location loc = Location.None;; using (var pooledInvocations = InvocationWalker.Borrow(block)) { foreach (var invocation in pooledInvocations.Invocations) { var isFromSymbol = IsInvokeFrom(symbol, semanticModel, cancellationToken, invocation); var method = semanticModel.GetSymbolSafe(invocation, cancellationToken) as IMethodSymbol; if (isFromSymbol && method.ReceiverType != null) { if (method.ReceiverType.Is(KnownSymbol.RefCounter) && method?.Name == KnownSymbol.IncRef) { count++; loc = invocation.GetLocation(); if (LoopUtils.HasLoopBetween(invocation, block)) { status.Skip("loop between IncRef call and var block", loc); } } } } } if (count > 1) { status.Skip("more than one IncRef", loc); } else if (count == 1) { status.IncRef("call IncRef", loc); } return(count); }
public static int DelRefInvocationCount(ISymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { int count = 0; Location loc = Location.None;; using (var pooledInvocations = InvocationWalker.Borrow(block)) { foreach (var invocation in pooledInvocations.Invocations) { var isFromSymbol = IsInvokeFrom(symbol, semanticModel, cancellationToken, invocation); var method = semanticModel.GetSymbolSafe(invocation, cancellationToken) as IMethodSymbol; if (isFromSymbol && method?.ReceiverType != null) { if (method.ReceiverType.Is(KnownSymbol.RefCounter) && method?.Name == KnownSymbol.ReleaseReference) { count++; loc = invocation.GetLocation(); if (LoopUtils.HasLoopBetween(invocation, block)) { status.Skip("loop between ReleaseReference call and var block", invocation.GetLocation()); } } } else { if (method?.Name == KnownSymbol.ReleaseReference) { foreach (ArgumentSyntax arg in invocation.ArgumentList.Arguments) { var sym = ModelExtensions.GetSymbolInfo(semanticModel, arg.Expression, cancellationToken); ISymbol argSymbol = sym.Symbol; if (symbol.Equals(argSymbol)) { count++; loc = invocation.GetLocation(); if (LoopUtils.HasLoopBetween(invocation, block)) { status.Skip("loop between ReleaseReference call and var block", arg.GetLocation()); } } } } } } } if (count > 1) { status.Skip("more than one ReleaseReference", loc); } else if (count == 1) { status.ReleaseReference("call ReleaseReference", loc); } return(count); }
public static void ProcessRightOfAssignmentToLocal(ISymbol variable, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { List <AssignmentExpressionSyntax> rc = new List <AssignmentExpressionSyntax>(); block?.TryGetAssignment(variable, semanticModel, cancellationToken, rc); foreach (AssignmentExpressionSyntax assignment in rc) { var left = semanticModel.GetSymbolSafe(assignment.Left, cancellationToken) ?? semanticModel.GetSymbolSafe((assignment.Left as ElementAccessExpressionSyntax)?.Expression, cancellationToken); bool fOrP = left is ILocalSymbol || left is IParameterSymbol; if (fOrP) { status.Skip("assigned to other local variables", assignment.GetLocation()); } } }
public static void ProcessLeftSideOfAssignment(ISymbol variable, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken, RefCounterStatus status) { List <AssignmentExpressionSyntax> rc = new List <AssignmentExpressionSyntax>(); using (var walker = AssignmentWalker.Borrow(block, Search.TopLevel, semanticModel, cancellationToken)) { foreach (AssignmentExpressionSyntax assignment in walker.Assignments) { var sym = ModelExtensions.GetSymbolInfo(semanticModel, assignment.Left, cancellationToken); if (variable.Equals(sym.Symbol)) { AssignmentUtils.CalcAssignmentValue(assignment.Right, block, semanticModel, cancellationToken, status); } } } }