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 void UpdateGlobalValue(GlobalFlowStateDictionaryAnalysisValue value) { if (value.Kind == GlobalFlowStateDictionaryAnalysisValueKind.Known) { var newState = GlobalFlowStateDictionaryAnalysisValue.Merge(GlobalState, value, false); SetAbstractValue(GlobalEntity, newState); } }
private GlobalFlowStateDictionaryAnalysisValue CreateAndUpdateAnalysisValue( IOperation parameterOrLocalOperation, IDeferredTypeEntity entity, GlobalFlowStateDictionaryAnalysisValue defaultValue, EnumerationCount enumerationCount) { var analysisValueForNewEntity = CreateAnalysisValue(entity, parameterOrLocalOperation, defaultValue, enumerationCount); UpdateGlobalValue(analysisValueForNewEntity); return(analysisValueForNewEntity); }
private GlobalFlowStateDictionaryAnalysisValue VisitLocalOrParameter(ITypeSymbol?typeSymbol, IOperation parameterOrLocalReferenceOperation, GlobalFlowStateDictionaryAnalysisValue defaultValue) { RoslynDebug.Assert(parameterOrLocalReferenceOperation is IParameterReferenceOperation or ILocalReferenceOperation); if (!IsDeferredType(typeSymbol, _wellKnownSymbolsInfo.AdditionalDeferredTypes)) { return(defaultValue); } var enumerationCount = GetEnumerationCount(parameterOrLocalReferenceOperation, _wellKnownSymbolsInfo); if (enumerationCount is EnumerationCount.Zero or EnumerationCount.None) { return(defaultValue); } if (DataFlowAnalysisContext.PointsToAnalysisResult == null) { return(defaultValue); } var pointToResult = DataFlowAnalysisContext.PointsToAnalysisResult[parameterOrLocalReferenceOperation.Kind, parameterOrLocalReferenceOperation.Syntax]; if (pointToResult.Kind != PointsToAbstractValueKind.KnownLocations) { return(defaultValue); } if (pointToResult.Locations.Any( l => !IsDeferredType(l.LocationType?.OriginalDefinition, _wellKnownSymbolsInfo.AdditionalDeferredTypes))) { return(defaultValue); } return(VisitDeferTypeEntities( DataFlowAnalysisContext.PointsToAnalysisResult, parameterOrLocalReferenceOperation, defaultValue, 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); }
protected override void SetAbstractValueForAssignment( AnalysisEntity targetAnalysisEntity, IOperation?assignedValueOperation, GlobalFlowStateDictionaryAnalysisValue assignedValue) { if (assignedValueOperation is null || !IsDeferredType(assignedValueOperation.Type?.OriginalDefinition, _wellKnownSymbolsInfo.AdditionalDeferredTypes)) { return; } var deferredTypeCreationEntity = new DeferredTypeCreationEntity(assignedValueOperation); if (GlobalState.TrackedEntities.ContainsKey(deferredTypeCreationEntity)) { // An operation create an 'IEnumerable' entity is visited again in an AssignmentOperation, this could happens in cases like // foreach (var x in collection) // { // var a = CreateIEnumerable(); // a.Count(); // } // where the 'CreateIEnumerable()' is visited again in the loop. // In this case, reset the linked 'IEnumerable' entity. SetAbstractValue(GlobalEntity, GlobalState.RemoveTrackedDeferredTypeEntity(deferredTypeCreationEntity)); } base.SetAbstractValueForAssignment(targetAnalysisEntity, assignedValueOperation, assignedValue); }