Exemple #1
0
        private static void OnCompilationStart(CompilationStartAnalysisContext context)
        {
            var methodHelper = MethodHelper.Init(context.Compilation);

            if (methodHelper is null)
            {
                return;
            }

            context.RegisterOperationBlockStartAction(OnOperationBlockStart);
            return;

            void OnOperationBlockStart(OperationBlockStartAnalysisContext context)
            {
                // Patterns we are looking for:
                // Pattern #1                                      Pattern #2
                // var obj = #HashCreation#;           or          #HashCreation#.CompuateHash(buffer);
                // ...
                // obj.CompuateHash(buffer);

                // The core search logic is split into 3 groups
                // 1. Scan all invocations:
                //  a. if it is a Hash Create static method, store its local symbol + declaration operation and type symbols
                //  b. if it is HashAlgorithm.ComputeHash /  HashAlgorithm.TryComputeHash
                //      1. if its instance is a local reference, store its local reference + invocation
                //      2. if its instance is the creation of a hash instance, we found pattern #2. Report diagnostic.
                //  c. if it is HashAlgorithm.Dispose, store the invocation operation
                // 2. Find all HashAlgorithm object creation (new) and store its local symbol + declaration operation and type symbols
                // 3. Find all HashAlgorithm local references and store them

                // At OperationBlockEnd:
                // 1. Compile a 'disposeMap' map of symbol -> dispose invocations (Dictionary<ILocalSymbol, ImmutableArray<IInvocationOperation>>)
                // 2. Compile a 'computeHashOnlyCountMap' map of symbol -> computehash invocation count (Dictionary<ILocalSymbol, int>)
                //    a. if the count for its local references in the block does not match the count of computehash invocations + dispose invocations, it is excluded from this map
                // 3. Iterate the invocation, only report the invocation
                //    a. hashAlgorithm type has a static HashData method
                //    b. hashAlgorithm instance was created in the block
                //    c. hashAlgorithm instance exist in the 'computeHashOnlyCountMap' map

                // Reporting of Diagnostic
                // The main span reported is at the ComputeHash method
                //
                // Properties:
                //  if there is only 1 local reference of a symbol excluding dispose references, DeleteHashCreationPropertyKey is set
                //
                // Additional locations:
                // ComputeHash/ComputeHash(a,b,c)/TryComputeHash:
                // Pattern #1                                      Pattern #2
                // 1. span where the hash instance was created
                // 2-N. dispose invocations

                var dataCollector = new DataCollector();

                context.RegisterOperationAction(CaptureHashLocalReferenceOperation, OperationKind.LocalReference);
                context.RegisterOperationAction(CaptureCreateOrComputeHashInvocationOperation, OperationKind.Invocation);
                context.RegisterOperationAction(CaptureHashObjectCreationOperation, OperationKind.ObjectCreation);
                context.RegisterOperationBlockEndAction(OnOperationBlockEnd);
                return;

                void CaptureHashLocalReferenceOperation(OperationAnalysisContext context)
                {
                    var localReferenceOperation = (ILocalReferenceOperation)context.Operation;

                    if (methodHelper.IsLocalReferenceInheritingHashAlgorithm(localReferenceOperation))
                    {
                        dataCollector.CollectLocalReferenceInheritingHashAlgorithm(localReferenceOperation);
                    }
                }

                void CaptureCreateOrComputeHashInvocationOperation(OperationAnalysisContext context)
                {
                    var invocationOperation = (IInvocationOperation)context.Operation;

                    if (methodHelper.IsHashCreateMethod(invocationOperation))
                    {
                        CaptureHashCreateInvocationOperation(dataCollector, invocationOperation);
                    }
                    else if (methodHelper.IsComputeHashMethod(invocationOperation, out ComputeType computeType))
                    {
                        CaptureOrReportComputeHashInvocationOperation(context, methodHelper, dataCollector, invocationOperation, computeType);
                    }
                    else if (invocationOperation.Instance is ILocalReferenceOperation && methodHelper.IsDisposeMethod(invocationOperation))
                    {
                        dataCollector.CollectDisposeInvocation(invocationOperation);
                    }
                }

                void CaptureHashObjectCreationOperation(OperationAnalysisContext context)
                {
                    var objectCreationOperation = (IObjectCreationOperation)context.Operation;

                    if (!methodHelper.IsObjectCreationInheritingHashAlgorithm(objectCreationOperation))
                    {
                        return;
                    }
                    if (TryGetVariableInitializerOperation(objectCreationOperation.Parent, out var variableInitializerOperation))
                    {
                        CaptureVariableDeclaratorOperation(dataCollector, objectCreationOperation.Type, variableInitializerOperation);
                    }
                }

                void OnOperationBlockEnd(OperationBlockAnalysisContext context)
                {
                    var cancellationToken = context.CancellationToken;
                    var dataResult        = dataCollector.Compile(cancellationToken);

                    if (dataResult is null)
                    {
                        return;
                    }

                    foreach (var(computeHash, type) in dataResult.ComputeHashMap)
                    {
                        ImmutableArray <Location> codefixerLocations;
                        var localSymbol            = ((ILocalReferenceOperation)computeHash.Instance).Local;
                        var isToDeleteHashCreation = false;

                        if (dataResult.TryGetDeclarationTuple(localSymbol, out var declarationTuple) &&
                            dataResult.TryGetSymbolComputeHashRefCountTuple(localSymbol, out var refCount) &&
                            methodHelper.TryGetHashDataMethod(declarationTuple.OriginalType, type, out var hashDataMethodSymbol))
                        {
                            var disposeArray = dataResult.GetDisposeInvocationArray(localSymbol);
                            isToDeleteHashCreation = refCount == 1;
                            codefixerLocations     = GetFixerLocations(declarationTuple.VariableIntitializerOperation, disposeArray, out var hashCreationLocationIndex);
                            var diagnostics = CreateDiagnostics(computeHash, hashDataMethodSymbol.ContainingType, codefixerLocations, type, isToDeleteHashCreation, hashCreationLocationIndex);
                            context.ReportDiagnostic(diagnostics);
                        }
                    }
                    dataResult.Free(cancellationToken);
                }
            }