private static void ReportAnonymousTypes([NotNull] ClosureInspector inspector, [NotNull] IHighlightingConsumer consumer) { foreach (var queryClause in inspector.AnonymousTypes) { var highlighting = new ObjectAllocationHighlighting(queryClause, "transparent identifier anonymous type instantiation"); consumer.AddHighlighting(highlighting, queryClause.GetDocumentRange()); } }
protected override void Run(ITreeNode element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer) { IParametersOwner function = null; ILocalScope topScope = null; if (element is ICSharpFunctionDeclaration functionDeclaration) { function = functionDeclaration.DeclaredElement; topScope = functionDeclaration.Body as ILocalScope; } if (element is IExpressionBodyOwnerDeclaration expressionBodyOwner) { #if RESHARPER2017_1 var arrowExpression = expressionBodyOwner.ArrowClause; #else var arrowExpression = expressionBodyOwner.ArrowExpression; #endif if (arrowExpression != null) { #if RESHARPER2016_3 function = expressionBodyOwner.GetParametersOwner(); topScope = arrowExpression as ILocalScope; #else function = expressionBodyOwner.GetFunction(); topScope = arrowExpression; #endif } else { if (element is IAccessorOwnerDeclaration) { return; } } } var inspector = new ClosureInspector(element, function); element.ProcessDescendants(inspector); // report closures allocations if (inspector.Closures.Count > 0) { ReportClosureAllocations(element, function, topScope, inspector, consumer); } // report non-cached generic lambda expressions if (function != null && inspector.ClosurelessLambdas.Count > 0) { ReportClosurelessAllocations(element, function, inspector, consumer); } // report anonymous types in query expressions if (inspector.AnonymousTypes.Count > 0) { ReportAnonymousTypes(inspector, consumer); } }
private static void ReportClosurelessAllocations( [NotNull] ICSharpFunctionDeclaration declaration, [NotNull] ClosureInspector inspector, [NotNull] IHighlightingConsumer consumer) { var parametersOwner = declaration.DeclaredElement as ITypeParametersOwner; if (parametersOwner != null && parametersOwner.TypeParameters.Count != 0) { foreach (var lambda in inspector.ClosurelessLambdas) { if (IsExpressionLambda(lambda)) { continue; } var range = GetClosureRange(lambda); if (!range.IsValid()) { continue; } consumer.AddHighlighting( new DelegateAllocationHighlighting(lambda, "from generic anonymous function (always non cached)"), range); consumer.AddHighlighting( new SlowDelegateCreationHighlighting(lambda, "anonymous function in generic method is generic itself"), range); } } foreach (var lambda in inspector.ClosurelessLambdas) { if (!IsExpressionLambda(lambda)) { continue; } var highlightingRange = GetClosureRange(lambda); if (highlightingRange.IsValid()) { consumer.AddHighlighting( new ObjectAllocationHighlighting(lambda, "expression tree construction"), highlightingRange); } } }
private static void ReportClosurelessAllocations( [NotNull] ITreeNode element, [NotNull] IParametersOwner function, [NotNull] ClosureInspector inspector, [NotNull] IHighlightingConsumer consumer) { var typeParametersOwner = function as ITypeParametersOwner; if (typeParametersOwner != null && typeParametersOwner.TypeParameters.Count != 0) { foreach (var lambda in inspector.ClosurelessLambdas) { if (IsExpressionLambda(lambda)) { continue; } var closureRange = GetClosureRange(lambda); if (!closureRange.IsValid()) { continue; } // note: Roslyn compiler implements caching of such closures if (!element.IsCSharp6Supported()) { consumer.AddHighlighting( new DelegateAllocationHighlighting(lambda, "from generic anonymous function (always non cached)"), closureRange); } } } foreach (var lambda in inspector.ClosurelessLambdas) { if (!IsExpressionLambda(lambda)) { continue; } var closureRange = GetClosureRange(lambda); if (closureRange.IsValid()) { consumer.AddHighlighting( new ObjectAllocationHighlighting(lambda, "expression tree construction"), closureRange); } } }
private static void ReportClosureAllocations( [NotNull] ITreeNode topDeclaration, [CanBeNull] IFunction thisElement, [CanBeNull] ILocalScope topScope, [NotNull] ClosureInspector inspector, [NotNull] IHighlightingConsumer consumer) { var scopesMap = new Dictionary <IDeclaredElement, ILocalScope>(); var captureScopes = new Dictionary <ILocalScope, JetHashSet <IDeclaredElement> >(); // group captures by their scope, report non-cached delegates foreach (var closure in inspector.Closures) { foreach (var capture in closure.Value) { ILocalScope scope = null; if (capture is IFunction) { scope = topScope; // 'this' capture } else { var declarations = capture.GetDeclarations(); if (declarations.Count == 0) // accessors 'value' parameter { var accessor = thisElement as IAccessor; if (accessor != null && Equals(accessor.ValueVariable, capture)) { scope = topScope; } } else { foreach (var declaration in declarations) { if (declaration is IRegularParameterDeclaration) { scope = topScope; } else { scope = declaration.GetContainingNode <ILocalScope>(); } break; } } } if (scope == null) { continue; } JetHashSet <IDeclaredElement> captures; if (!captureScopes.TryGetValue(scope, out captures)) { captureScopes[scope] = captures = new JetHashSet <IDeclaredElement>(); } captures.Add(capture); scopesMap[capture] = scope; } { var highlightingRange = GetClosureRange(closure.Key); if (highlightingRange.IsValid()) { if (IsExpressionLambda(closure.Key)) { consumer.AddHighlighting( new ObjectAllocationHighlighting(closure.Key, "expression tree construction"), highlightingRange); } else { consumer.AddHighlighting( new DelegateAllocationHighlighting(closure.Key, "capture of " + FormatClosureDescription(closure.Value)), highlightingRange); } } } } // highlight first captured entity per every scope foreach (var scopeToCaptures in captureScopes) { var firstOffset = TreeOffset.MaxValue; IDeclaredElement firstCapture = null; foreach (var capture in scopeToCaptures.Value) { if (capture is IFunction) { continue; } var offset = GetCaptureStartOffset(capture); if (offset < firstOffset) { firstOffset = offset; firstCapture = capture; } } var scopeClosure = FormatClosureDescription(scopeToCaptures.Value); // collect outer captures JetHashSet <IDeclaredElement> outerCaptures = null; foreach (var closureToCaptures in inspector.Closures) { if (!scopeToCaptures.Key.Contains(closureToCaptures.Key)) { continue; } foreach (var capture in closureToCaptures.Value) { ILocalScope scope; if (!scopesMap.TryGetValue(capture, out scope)) { continue; } if (scopeToCaptures.Key.Contains(scope)) { continue; } outerCaptures = outerCaptures ?? new JetHashSet <IDeclaredElement>(); outerCaptures.Add(capture); } } if (outerCaptures != null) { scopeClosure += string.Format(" + (outer closure of {0})", FormatClosureDescription(outerCaptures)); } if (firstCapture != null) { DocumentRange highlightingRange; var anchor = GetCaptureHighlightingRange( topDeclaration, thisElement, firstCapture, out highlightingRange); if (anchor != null && highlightingRange.IsValid()) { consumer.AddHighlighting( new ClosureAllocationHighlighting(anchor, scopeClosure), highlightingRange); } } } }