private void SoftCopyAllDependencies(MethodOrAccessorData methodOrAccessor) { if (methodOrAccessor == null) { return; } var processedMethods = new HashSet <MethodOrAccessorData>(); var processingMetods = new Queue <MethodOrAccessorData>(); processingMetods.Enqueue(methodOrAccessor); while (processingMetods.Count > 0) { var currentOrAccessor = processingMetods.Dequeue(); processedMethods.Add(currentOrAccessor); foreach (var referencedMethodOrAccessor in currentOrAccessor.BodyFunctionReferences .Select(o => o.ReferenceFunctionData) .Where(o => o != null) .Where(o => o.Conversion != MethodConversion.Ignore && o.TypeData.IsNewType) .Where(o => !processedMethods.Contains(o)) .OfType <MethodOrAccessorData>()) { referencedMethodOrAccessor.SoftCopy(); processingMetods.Enqueue(referencedMethodOrAccessor); } } }
private List <IMethodSymbol> FillRelatedAsyncMethods(MethodOrAccessorData methodOrAccessorData, IMethodSymbol symbol) { var asyncConterparts = GetAsyncCounterparts(symbol, _preAnalyzeSearchOptions).ToList(); if (asyncConterparts.Any()) { foreach (var asyncConterpart in asyncConterparts) { methodOrAccessorData.RelatedAsyncMethods.TryAdd(asyncConterpart); } } return(asyncConterparts); }
private async Task ScanMethodData(MethodOrAccessorData methodOrAccessorData, int depth = 0, CancellationToken cancellationToken = default(CancellationToken)) { if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } if (!_scannedMethodOrAccessors.TryAdd(methodOrAccessorData)) { return; } SyntaxReference syntax; var bodyScanMethodDatas = new HashSet <MethodOrAccessorData>(); var referenceScanMethods = new HashSet <IMethodSymbol>(); if (_configuration.ScanMethodBody || methodOrAccessorData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync)) { bodyScanMethodDatas.Add(methodOrAccessorData); } var interfaceMethods = methodOrAccessorData.ImplementedInterfaces.ToImmutableHashSet(); if (methodOrAccessorData.InterfaceMethod) { interfaceMethods = interfaceMethods.Add(methodOrAccessorData.Symbol); } // Get and save all interface implementations foreach (var interfaceMethod in interfaceMethods) { referenceScanMethods.Add(interfaceMethod); syntax = interfaceMethod.DeclaringSyntaxReferences.Single(); var document = _solution.GetDocument(syntax.SyntaxTree); if (!CanProcessDocument(document)) { continue; } var documentData = ProjectData.GetDocumentData(document); var methodNode = await syntax.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); var interfaceMethodData = documentData.GetMethodOrAccessorData(methodNode); // NOTE: FindImplementationsAsync will not find all implementations when we have an abstract/virtual implementation of the interface. // In this case we will get only the abstract/virtual method so we have to find all overrides for it manually await FindImplementations(interfaceMethod, async (implMethod, implMethodData) => { interfaceMethodData.RelatedMethods.TryAdd(implMethodData); implMethodData.RelatedMethods.TryAdd(interfaceMethodData); if (_configuration.ScanMethodBody || implMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync)) { bodyScanMethodDatas.Add(implMethodData); } if (!implMethod.IsAbstract && !implMethod.IsVirtual) { return; } // Find all overrides await FindOverrides(implMethod, (overrideSymbol, overrideMethodData) => { overrideMethodData.RelatedMethods.TryAdd(interfaceMethodData); interfaceMethodData.RelatedMethods.TryAdd(overrideMethodData); implMethodData.RelatedMethods.TryAdd(overrideMethodData); overrideMethodData.RelatedMethods.TryAdd(implMethodData); if (_configuration.ScanMethodBody || overrideMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync)) { bodyScanMethodDatas.Add(overrideMethodData); } }, cancellationToken).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false); } MethodOrAccessorData baseMethodData = null; IMethodSymbol baseMethodSymbol = null; if (methodOrAccessorData.BaseOverriddenMethod?.DeclaringSyntaxReferences.Any() == true) { baseMethodSymbol = methodOrAccessorData.BaseOverriddenMethod; } else if (!methodOrAccessorData.InterfaceMethod && (methodOrAccessorData.Symbol.IsVirtual || methodOrAccessorData.Symbol.IsAbstract)) // interface method has IsAbstract true { baseMethodSymbol = methodOrAccessorData.Symbol; baseMethodData = methodOrAccessorData; } // Get and save all derived methods if (baseMethodSymbol != null) { referenceScanMethods.Add(baseMethodSymbol); if (baseMethodData == null) { syntax = baseMethodSymbol.DeclaringSyntaxReferences.Single(); var document = _solution.GetDocument(syntax.SyntaxTree); if (CanProcessDocument(document)) { var methodNode = await syntax.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); baseMethodData = ProjectData.GetDocumentData(document).GetMethodOrAccessorData(methodNode); } } if (baseMethodData != null && (_configuration.ScanMethodBody || baseMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync))) { bodyScanMethodDatas.Add(baseMethodData); } // Check if the overrides will be found in the FindImplementations callback, if so we must skip this call otherwise a race condition may happen if (baseMethodData?.ImplementedInterfaces.All(o => !interfaceMethods.Contains(o)) == true) { await FindOverrides(baseMethodSymbol, (overrideMethod, overrideMethodData) => { if (baseMethodData != null) { overrideMethodData.RelatedMethods.TryAdd(baseMethodData); baseMethodData.RelatedMethods.TryAdd(overrideMethodData); } else { overrideMethodData.ExternalRelatedMethods.TryAdd(baseMethodSymbol); } if (!overrideMethod.IsAbstract && (_configuration.ScanMethodBody || overrideMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync))) { bodyScanMethodDatas.Add(overrideMethodData); } }, cancellationToken).ConfigureAwait(false); } } if (baseMethodSymbol == null && !interfaceMethods.Any()) //TODO: what about hiding methods { referenceScanMethods.Add(methodOrAccessorData.Symbol); } foreach (var mData in bodyScanMethodDatas) { foreach (var method in FindNewlyInvokedMethodsWithAsyncCounterpart(mData, referenceScanMethods)) { await ScanAllMethodReferenceLocations(method, depth, cancellationToken).ConfigureAwait(false); } } foreach (var methodToScan in referenceScanMethods) { await ScanAllMethodReferenceLocations(methodToScan, depth, cancellationToken).ConfigureAwait(false); } }
private void AnalyzeMethodData(DocumentData documentData, MethodOrAccessorData methodAccessorData) { if (methodAccessorData.Conversion == MethodConversion.Copy) { // If the method will be copied then we have to copy all methods that are used inside this one and do this recursively SoftCopyAllDependencies(methodAccessorData); return; // We do not want to analyze method that will be only copied } // If all abstract/virtual related methods are ignored then ignore also this one (IsAbstract includes also interface members) // Skip methods that were ignored because having an async counterpart in order to have overrides generated var baseMethods = methodAccessorData.RelatedMethods .Where(o => !o.HasAsyncCounterpart || o.ExplicitlyIgnored) .Where(o => o.Symbol.IsAbstract || o.Symbol.IsVirtual).ToList(); if (!methodAccessorData.Conversion.HasFlag(MethodConversion.ToAsync) && baseMethods.Any() && baseMethods.All(o => o.Conversion == MethodConversion.Ignore)) { if (methodAccessorData.TypeData.GetSelfAndAncestorsTypeData().Any(o => o.Conversion == TypeConversion.NewType)) { methodAccessorData.Copy(); // Check if there are any internal methods that are candidate to be async and are invoked inside this method // If there are, then we need to copy them SoftCopyAllDependencies(methodAccessorData); } else { methodAccessorData.Ignore(IgnoreReason.AllRelatedMethodsIgnored); } return; } var methodBody = methodAccessorData.GetBodyNode(); methodAccessorData.HasYields = methodBody?.DescendantNodes().OfType <YieldStatementSyntax>().Any() == true; methodAccessorData.MustRunSynchronized = methodAccessorData.Symbol.GetAttributes() .Where(o => o.AttributeClass.Name == "MethodImplAttribute") .Any(o => ((MethodImplOptions)(int)o.ConstructorArguments.First().Value).HasFlag(MethodImplOptions.Synchronized)); if (methodBody == null) { methodAccessorData.OmitAsync = true; } // Order by descending so we are sure that methods passed by argument will be processed before the invoked method with those arguments foreach (var reference in methodAccessorData.BodyFunctionReferences.OrderByDescending(o => o.ReferenceNameNode.SpanStart)) { AnalyzeMethodReference(documentData, reference); } foreach (var reference in methodAccessorData.CrefFunctionReferences) { AnalyzeCrefMethodReference(documentData, methodAccessorData, reference); } foreach (var reference in methodAccessorData.NameofFunctionReferences) { AnalyzeNameofMethodReference(documentData, methodAccessorData, reference); } // Ignore all candidate arguments that are not an argument of an async invocation candidate // Do not ignore accessor as they are executed prior passing foreach (var reference in methodAccessorData.BodyFunctionReferences .Where(o => !o.ReferenceSymbol.IsPropertyAccessor() && o.ReferenceNode.IsKind(SyntaxKind.Argument) && o.ArgumentOfFunctionInvocation == null)) { reference.Ignore(IgnoreReason.InvokedMethodNoAsyncCounterpart); } if (methodAccessorData.Conversion.HasFlag(MethodConversion.ToAsync)) { return; } // If a method is never invoked and there is no invocations inside the method body that can be async and there is no related methods we can ignore it. // Methods with Unknown may not have InvokedBy populated so we cannot ignore them here // Do not ignore methods that are inside a type with conversion NewType as ExternalRelatedMethods may not be populated // Do not ignore a method if any of its related methods has an async counterpart that was not ignored by user so that we can gnerate (e.g. async override) if ( methodAccessorData.Dependencies.All(o => o.Conversion.HasFlag(MethodConversion.Ignore)) && methodAccessorData.RelatedMethods.All(o => !o.HasAsyncCounterpart || o.ExplicitlyIgnored) && methodAccessorData.BodyFunctionReferences.All(o => o.Conversion == ReferenceConversion.Ignore) && methodAccessorData.Conversion.HasFlag(MethodConversion.Smart) && methodAccessorData.TypeData.GetSelfAndAncestorsTypeData().All(o => o.Conversion != TypeConversion.NewType) && !methodAccessorData.ExternalRelatedMethods.Any() ) { methodAccessorData.Ignore(IgnoreReason.NeverUsedAndNoAsyncInvocations); } }
private void AnalyzeNameofMethodReference(DocumentData documentData, MethodOrAccessorData methoData, NameofFunctionDataReference nameofData) { nameofData.RelatedBodyFunctionReferences.AddRange( methoData.BodyFunctionReferences.Where(o => o.ReferenceSymbol.Equals(nameofData.ReferenceSymbol))); }