public override void VisitInvocationExpression(InvocationExpression invocationExpression) { InvocationExpression outerInvocationExpression = invocationExpression; //Note the invocations are in reverse order, so x.Foo().Bar() will have [0] be the Bar() and [1] be the Foo() List <InvocationExpression> invocations = new List <InvocationExpression>(); LinqMethod outerMethod = null; Expression target = null; for (;;) { var resolveResult = ctx.Resolve(invocationExpression) as MemberResolveResult; if (resolveResult == null || !(resolveResult.Member is IMethod)) { break; } var method = LinqMethods.FirstOrDefault(candidate => candidate.FullName == resolveResult.Member.FullName && candidate.ParameterCount == ((IMethod)resolveResult.Member.MemberDefinition).Parameters.Count); if (method == null || (invocations.Any() && method.IsLast)) { break; } var mre = invocationExpression.Target as MemberReferenceExpression; if (mre == null) { break; } if (outerMethod == null) { outerMethod = method; } invocations.Add(invocationExpression); target = mre.Target; var newInvocation = target as InvocationExpression; if (newInvocation == null) { break; } invocationExpression = newInvocation; } if (target == null) { base.VisitInvocationExpression(invocationExpression); return; } if (!outerMethod.IsPoorStyleAlone && invocations.Count == 1) { base.VisitInvocationExpression(invocationExpression); return; } var currentTypeDeclaration = outerInvocationExpression.GetParent <TypeDeclaration>(); var currentTypeResolveResult = ctx.Resolve(currentTypeDeclaration) as TypeResolveResult; if (currentTypeResolveResult == null) { base.VisitInvocationExpression(invocationExpression); return; } var currentTypeDefinition = currentTypeResolveResult.Type.GetDefinition(); var targetResolveResult = ctx.Resolve(target); if (!CanIndex(currentTypeDefinition, targetResolveResult)) { base.VisitInvocationExpression(invocationExpression); return; } string countPropertyName = GetCountProperty(currentTypeDefinition, targetResolveResult); string lastInvocationName = ((MemberReferenceExpression)invocations[0].Target).MemberName; bool endsReversed = invocations.Count(invocation => ((MemberReferenceExpression)invocation.Target).MemberName == "Reverse") % 2 != 0; bool requiresCount = lastInvocationName == "Count" || lastInvocationName == "Any" || (endsReversed ? lastInvocationName == "First" || lastInvocationName == "ElementAt" : lastInvocationName == "Last"); if (countPropertyName == null && requiresCount) { base.VisitInvocationExpression(invocationExpression); return; } AddIssue(new CodeIssue(invocations.Last().LParToken.StartLocation, invocations.First().RParToken.EndLocation, ctx.TranslateString("Use of Linq method when there's a better alternative"), ctx.TranslateString("Replace method by simpler version"), script => { Expression startOffset = null; Expression endOffset = null; Expression expression = null; bool reversed = false; foreach (var invocation in invocations.AsEnumerable().Reverse()) { string invocationName = ((MemberReferenceExpression)invocation.Target).MemberName; switch (invocationName) { case "Skip": Expression offset = reversed ? endOffset : startOffset; if (offset == null) { offset = invocation.Arguments.Last().Clone(); } else { offset = new BinaryOperatorExpression(offset, BinaryOperatorType.Add, invocation.Arguments.Last().Clone()); } if (reversed) { endOffset = offset; } else { startOffset = offset; } break; case "Reverse": reversed = !reversed; break; case "First": case "ElementAt": case "Last": { bool fromEnd = (invocationName == "Last") ^ reversed; Expression index = invocationName == "ElementAt" ? invocation.Arguments.Last().Clone() : null; Expression baseOffset = fromEnd ? endOffset : startOffset; //Our indexWithOffset is baseOffset + index //A baseOffset/index of null is considered "0". Expression indexWithOffset = baseOffset == null ? index : index == null ? baseOffset : new BinaryOperatorExpression(baseOffset, BinaryOperatorType.Add, index); Expression indexerExpression = indexWithOffset; if (fromEnd) { var endExpression = new BinaryOperatorExpression(new MemberReferenceExpression(target.Clone(), countPropertyName), BinaryOperatorType.Subtract, new PrimitiveExpression(1)); if (indexerExpression == null) { indexerExpression = endExpression; } else { indexerExpression = new BinaryOperatorExpression(endExpression, BinaryOperatorType.Subtract, new ParenthesizedExpression(indexerExpression)); } } indexerExpression = indexerExpression ?? new PrimitiveExpression(0); var newExpression = new IndexerExpression(target.Clone(), indexerExpression); script.Replace(outerInvocationExpression, newExpression); break; } case "Count": case "Any": { Expression takenMembers; if (startOffset == null) { takenMembers = endOffset; } else if (endOffset == null) { takenMembers = startOffset; } else { takenMembers = new BinaryOperatorExpression(startOffset, BinaryOperatorType.Add, endOffset); } var countExpression = new MemberReferenceExpression(target.Clone(), countPropertyName); Expression newExpression; if (invocationName == "Count") { if (takenMembers == null) { newExpression = countExpression; } else { newExpression = new BinaryOperatorExpression(countExpression, BinaryOperatorType.Subtract, new ParenthesizedExpression(takenMembers)); } } else { newExpression = new BinaryOperatorExpression(countExpression, BinaryOperatorType.GreaterThan, new ParenthesizedExpression(takenMembers)); } script.Replace(outerInvocationExpression, newExpression); break; } } } })); base.VisitInvocationExpression(invocationExpression); }