private static GlobalFlowStateDictionaryAnalysisValue CreateAnalysisValue( IDeferredTypeEntity entity, IOperation parameterOrLocalReferenceOperation, GlobalFlowStateDictionaryAnalysisValue defaultValue, EnumerationCount enumerationCount) { var operationsSetBuilder = PooledHashSet <IOperation> .GetInstance(); operationsSetBuilder.Add(parameterOrLocalReferenceOperation); var newInvocationSet = new TrackingEnumerationSet( operationsSetBuilder.ToImmutableAndFree(), enumerationCount); var trackedEntitiesBuilder = PooledDictionary <IDeferredTypeEntity, TrackingEnumerationSet> .GetInstance(); trackedEntitiesBuilder.Add(entity, newInvocationSet); var analysisValue = new GlobalFlowStateDictionaryAnalysisValue( trackedEntitiesBuilder.ToImmutableDictionaryAndFree(), GlobalFlowStateDictionaryAnalysisValueKind.Known); return(defaultValue.Kind == GlobalFlowStateDictionaryAnalysisValueKind.Known ? GlobalFlowStateDictionaryAnalysisValue.Merge(analysisValue, defaultValue, false) : analysisValue); }
private static EnumerationCount Min(EnumerationCount count1, EnumerationCount count2) { // Unknown = -1, Zero = 0, One = 1, TwoOrMoreTime = 2 var min = Math.Min((int)count1, (int)count2); return((EnumerationCount)min); }
private GlobalFlowStateDictionaryAnalysisValue CreateAndUpdateAnalysisValue( IOperation parameterOrLocalOperation, IDeferredTypeEntity entity, GlobalFlowStateDictionaryAnalysisValue defaultValue, EnumerationCount enumerationCount) { var analysisValueForNewEntity = CreateAnalysisValue(entity, parameterOrLocalOperation, defaultValue, enumerationCount); UpdateGlobalValue(analysisValueForNewEntity); return(analysisValueForNewEntity); }
public static EnumerationCount AddInvocationCount(EnumerationCount count1, EnumerationCount count2) => (count1, count2) switch { (EnumerationCount.None, _) => EnumerationCount.None, (_, EnumerationCount.None) => EnumerationCount.None, (EnumerationCount.Zero, _) => count2, (_, EnumerationCount.Zero) => count1, (EnumerationCount.One, EnumerationCount.One) => EnumerationCount.TwoOrMoreTime, (EnumerationCount.TwoOrMoreTime, _) => EnumerationCount.TwoOrMoreTime, (_, EnumerationCount.TwoOrMoreTime) => EnumerationCount.TwoOrMoreTime, (_, _) => EnumerationCount.None, };
static (IOperation linqChainTailOperation, EnumerationCount enumerationCount) VisitLinqChainAndCoversionMethod( IOperation operation, EnumerationCount enumerationCount, WellKnownSymbolsInfo wellKnownSymbolsInfo) { if (IsValidImplicitConversion(operation.Parent, wellKnownSymbolsInfo)) { // Go to the implicit conversion if needed // e.g. // void Bar (IOrderedEnumerable<T> c) // { // c.First(); // } // here 'c' would be converted to IEnumerable<T> return(VisitLinqChainAndCoversionMethod(operation.Parent, enumerationCount, wellKnownSymbolsInfo)); } if (IsOperationIsArgumentOfLinqChainInvocation(operation.Parent, wellKnownSymbolsInfo, out var enumerateArgument)) { // This operation is used as an argument of a deferred execution method. // Check if the invocation of the deferred execution method is used in another deferred execution method. return(VisitLinqChainAndCoversionMethod( operation.Parent.Parent, enumerateArgument ? InvocationSetHelpers.AddInvocationCount(enumerationCount, EnumerationCount.One) : enumerationCount, wellKnownSymbolsInfo)); } if (IsInstanceOfLinqChainInvocation(operation, wellKnownSymbolsInfo, out var enumerateInstance)) { // If the extension method could be used as reduced method, also check its invocation instance. // Like in VB, // 'i.Select(Function(a) a)', 'i' is the invocation instance of 'Select' return(VisitLinqChainAndCoversionMethod( operation.Parent, enumerateInstance ? InvocationSetHelpers.AddInvocationCount(enumerationCount, EnumerationCount.One) : enumerationCount, wellKnownSymbolsInfo)); } return(operation, enumerationCount); }
/// <summary> /// Visit all the possible deferred type entities referenced by <param name="parameterOrLocalOperation"/>. /// </summary> private GlobalFlowStateDictionaryAnalysisValue VisitDeferTypeEntities( PointsToAnalysisResult pointsToAnalysisResult, IOperation parameterOrLocalOperation, GlobalFlowStateDictionaryAnalysisValue defaultValue, EnumerationCount enumerationCount) { RoslynDebug.Assert(parameterOrLocalOperation is IParameterReferenceOperation or ILocalReferenceOperation); // With the initial operation as the root, expand it if // the operation is parameter or local reference. It has only one pointToAnalysisResult, and it is a deferred execution invocation. // Visit its argument. // e.g. // var a = b.Concat(c); // a.ElementAt(10); // When we visit 'a.Element(10)' and look back to 'b.Concat(c)', also try to visit 'b' and 'c'. // // Update the analysis value when reach one of the following nodes. // 1. A parameter or local that has symbol, but no creationOperation. // e.g. // void Bar(IEnumerable<int> b, IEnumerable<int> c) // { // var a = b.Concat(c); // a.ElementAt(10); // } // When 'a.ElementAt(10)' is called, 'b' and 'c' are enumerated once. // // 2. Invocation operation that returns a deferred type. // e.g. // void Bar() // { // var a = Enumerable.Range(1, 1); // var b = Enumerable.Range(2, 2); // var c = a.Concat(b); // c.ElementAt(10); // } // When 'c.ElementAt(10)' is called, then 'Enumerable.Range(1, 1)', 'Enumerable.Range(2, 2)' and 'a.Concat(b)' are enumerated. // 3. A parameter or local reference operation with multiple AbstractLocations. Stop expanding the tree at this node // because we don't know how to proceed. // e.g. // void Bar(bool flag) // { // var a = flag ? Enumerable.Range(1, 1) : Enumerable.Range(2, 2); // a.ElementAt(10); // } var queue = new Queue <IOperation>(); queue.Enqueue(parameterOrLocalOperation); var resultAnalysisValue = defaultValue; while (queue.Count > 0) { var currentOperation = queue.Dequeue(); if (currentOperation is IParameterReferenceOperation or ILocalReferenceOperation) { var result = pointsToAnalysisResult[currentOperation]; if (result.Kind != PointsToAbstractValueKind.KnownLocations || result.Locations.IsEmpty) { continue; } // Expand if there is only one AbstractLocation for this operation. if (result.Locations.Count == 1) { var location = result.Locations.Single(); var creationOperation = location.Creation; // Node 1: A parameter or local that has symbol, but no creation operation. if (creationOperation == null && location.Symbol != null && IsDeferredType(location.LocationType?.OriginalDefinition, _wellKnownSymbolsInfo.AdditionalDeferredTypes)) { var analysisValue = CreateAndUpdateAnalysisValue(currentOperation, new DeferredTypeSymbolEntity(location.Symbol), defaultValue, enumerationCount); resultAnalysisValue = GlobalFlowStateDictionaryAnalysisValue.Merge(resultAnalysisValue, analysisValue, false); continue; } if (creationOperation is IInvocationOperation invocationCreationOperation) { // Try to expand the argument of this invocation operation. ExpandInvocationOperation(invocationCreationOperation, _wellKnownSymbolsInfo, queue); var creationMethod = invocationCreationOperation.TargetMethod.ReducedFrom ?? invocationCreationOperation.TargetMethod; // Make sure this creation operation is not 'AsEnumerable', which only do a cast, and do not create new IEnumerable type. if (!_wellKnownSymbolsInfo.NoEffectLinqChainMethods.Contains(creationMethod.OriginalDefinition) && IsDeferredType(invocationCreationOperation.Type?.OriginalDefinition, _wellKnownSymbolsInfo.AdditionalDeferredTypes)) { // Node 2: Invocation operation that returns a deferred type. var analysisValue = CreateAndUpdateAnalysisValue(currentOperation, new DeferredTypeCreationEntity(invocationCreationOperation), defaultValue, enumerationCount); resultAnalysisValue = GlobalFlowStateDictionaryAnalysisValue.Merge(resultAnalysisValue, analysisValue, false); } continue; } } else { // Make sure all the locations are pointing to a deferred type. if (result.Locations.Any( l => !IsDeferredType(l.LocationType?.OriginalDefinition, _wellKnownSymbolsInfo.AdditionalDeferredTypes))) { continue; } // Node 3: A parameter or local reference operation with multiple AbstractLocations. var analysisValue = CreateAndUpdateAnalysisValue( currentOperation, new DeferredTypeEntitySet(result.Locations), defaultValue, enumerationCount); resultAnalysisValue = GlobalFlowStateDictionaryAnalysisValue.Merge(resultAnalysisValue, analysisValue, false); } } // Make sure we iterate into the nested operations. // e.g. // var a = b.Concat(c).Concat(d.Concat(e)); // Make sure 'd.Concat(e)' is expanded so that 'd' and 'e' could be found. if (currentOperation is IInvocationOperation invocationOperation) { ExpandInvocationOperation(invocationOperation, _wellKnownSymbolsInfo, queue); } // Expand the implicit conversion operation if it is converting a deferred type to another deferred type. // This might happen in such case: // var c = a.OrderBy(i => i).Concat(b) // The tree would be: // a.OrderBy(i => i).Concat(b) (root) // / \ // ArgumentOperation ArgumentOperation // / \ // Conversion *(expand this node) b // / // a.OrderBy(i => i) // / // ArgumentOperation // / // a if (currentOperation is IConversionOperation conversionOperation) { ExpandConversionOperation(conversionOperation, _wellKnownSymbolsInfo, queue); } } return(resultAnalysisValue); }