From 6b513abfd89e5a7ba84ac56fff141740a1e27455 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 21 Oct 2018 12:51:52 -0700 Subject: [PATCH] Add tests. --- .../UseIndexOperator/UseIndexOperatorTests.cs | 22 +-- .../UseIndexOperator/UseRangeOperatorTests.cs | 12 +- .../CSharpUseRangeOperatorCodeFixProvider.cs | 138 ++++++++++++++++-- ...ngeOperatorDiagnosticAnalyzer.InfoCache.cs | 2 +- ...geOperatorDiagnosticAnalyzer.MemberInfo.cs | 2 +- ...SharpUseRangeOperatorDiagnosticAnalyzer.cs | 122 ++++++---------- .../Portable/UseIndexOperator/Helpers.cs | 3 + 7 files changed, 194 insertions(+), 107 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseIndexOperator/UseIndexOperatorTests.cs b/src/EditorFeatures/CSharpTest/UseIndexOperator/UseIndexOperatorTests.cs index 9a0bce5f96a..c686041e30c 100644 --- a/src/EditorFeatures/CSharpTest/UseIndexOperator/UseIndexOperatorTests.cs +++ b/src/EditorFeatures/CSharpTest/UseIndexOperator/UseIndexOperatorTests.cs @@ -37,7 +37,7 @@ void Goo(string s) parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7))); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestSimple() { await TestAsync( @@ -59,7 +59,7 @@ void Goo(string s) }", parseOptions: s_parseOptions); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestComplexSubtaction() { await TestAsync( @@ -81,7 +81,7 @@ void Goo(string s) }", parseOptions: s_parseOptions); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestComplexInstance() { await TestAsync( @@ -107,7 +107,7 @@ void Goo(string[] ss) }", parseOptions: s_parseOptions); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestNotWithoutSubtraction1() { await TestMissingAsync( @@ -121,7 +121,7 @@ void Goo(string s) }", parameters: s_testParameters); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestNotWithoutSubtraction2() { await TestMissingAsync( @@ -135,7 +135,7 @@ void Goo(string s) }", parameters: s_testParameters); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestNotWithoutMultipleArgs() { await TestMissingAsync( @@ -149,7 +149,7 @@ void Goo(string s) }", parameters: s_testParameters); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestUserDefinedTypeWithLength() { await TestAsync( @@ -175,7 +175,7 @@ void Goo(S s) }", parseOptions: s_parseOptions); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestUserDefinedTypeWithCount() { await TestAsync( @@ -201,7 +201,7 @@ void Goo(S s) }", parseOptions: s_parseOptions); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestUserDefinedTypeWithNoLengthOrCount() { await TestMissingAsync( @@ -217,7 +217,7 @@ void Goo(S s) }", parameters: s_testParameters); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestUserDefinedTypeWithNoInt32Indexer() { await TestMissingAsync( @@ -233,7 +233,7 @@ void Goo(S s) }", parameters: s_testParameters); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] public async Task TestUserDefinedTypeWithNoIndexIndexer() { await TestMissingAsync( diff --git a/src/EditorFeatures/CSharpTest/UseIndexOperator/UseRangeOperatorTests.cs b/src/EditorFeatures/CSharpTest/UseIndexOperator/UseRangeOperatorTests.cs index 8a5cead073f..6ff9db13828 100644 --- a/src/EditorFeatures/CSharpTest/UseIndexOperator/UseRangeOperatorTests.cs +++ b/src/EditorFeatures/CSharpTest/UseIndexOperator/UseRangeOperatorTests.cs @@ -22,7 +22,7 @@ internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProvider private static readonly TestParameters s_testParameters = new TestParameters(parseOptions: s_parseOptions); - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] public async Task TestNotInCSharp7() { await TestMissingAsync( @@ -37,7 +37,7 @@ void Goo(string s) parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7))); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] public async Task TestSimple() { await TestAsync( @@ -59,14 +59,14 @@ void Goo(string s) }", parseOptions: s_parseOptions); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] public async Task TestComplexSubstraction() { await TestAsync( @" class C { - void Goo(string s) + void Goo(string s, int bar, int baz) { var v = s.Substring([||]bar, s.Length - baz - bar); } @@ -74,14 +74,14 @@ void Goo(string s) @" class C { - void Goo(string s) + void Goo(string s, int bar, int baz) { var v = s[bar..^baz]; } }", parseOptions: s_parseOptions); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDefaultLiteral)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] public async Task TestConstantSubtraction1() { await TestAsync( diff --git a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorCodeFixProvider.cs b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorCodeFixProvider.cs index 2b820e4a57b..5f0337e57bc 100644 --- a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorCodeFixProvider.cs @@ -7,11 +7,14 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseIndexOperator { @@ -34,31 +37,29 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) return Task.CompletedTask; } - protected override Task FixAllAsync( + protected override async Task FixAllAsync( Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + foreach (var diagnostic in diagnostics) { - FixOne(diagnostic, editor, cancellationToken); + FixOne(semanticModel, editor, diagnostic, cancellationToken); } - - return Task.CompletedTask; } private void FixOne( - Diagnostic diagnostic, SyntaxEditor editor, CancellationToken cancellationToken) + SemanticModel semanticModel, SyntaxEditor editor, + Diagnostic diagnostic, CancellationToken cancellationToken) { var invocation = (InvocationExpressionSyntax)diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); var expression = invocation.Expression is MemberAccessExpressionSyntax memberAccess ? memberAccess.Expression : invocation.Expression; - var start = (ExpressionSyntax)diagnostic.AdditionalLocations[1].FindNode(getInnermostNodeForTie: true, cancellationToken); - var end = (ExpressionSyntax)diagnostic.AdditionalLocations[2].FindNode(getInnermostNodeForTie: true, cancellationToken); - var rangeExpression = RangeExpression( - GetExpression(diagnostic.Properties, start, OmitStart, StartFromEnd), - GetExpression(diagnostic.Properties, end, OmitEnd, EndFromEnd)); + var rangeExpression = CreateRangeExpression( + semanticModel, diagnostic, invocation, cancellationToken); var argList = invocation.ArgumentList; var elementAccess = ElementAccessExpression( @@ -71,11 +72,128 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) editor.ReplaceNode(invocation, elementAccess); } + private RangeExpressionSyntax CreateRangeExpression( + SemanticModel semanticModel, Diagnostic diagnostic, + InvocationExpressionSyntax invocation, CancellationToken cancellationToken) + { + var properties = diagnostic.Properties; + if (properties.ContainsKey(ComputedRange)) + { + return CreateComputedRange(semanticModel, diagnostic, invocation, cancellationToken); + } + else if (properties.ContainsKey(ConstantRange)) + { + return CreateConstantRange(semanticModel, diagnostic, cancellationToken); + } + else + { + throw ExceptionUtilities.Unreachable; + } + } + + private RangeExpressionSyntax CreateComputedRange( + SemanticModel semanticModel, Diagnostic diagnostic, + InvocationExpressionSyntax invocation, CancellationToken cancellationToken) + { + var startExpr = (ExpressionSyntax)diagnostic.AdditionalLocations[1].FindNode(getInnermostNodeForTie: true, cancellationToken); + var endExpr = (ExpressionSyntax)diagnostic.AdditionalLocations[2].FindNode(getInnermostNodeForTie: true, cancellationToken); + + // We have enough information now to generate `start..end`. However, this will often + // not be what the user wants. For example, generating `start..expr.Length` is not as + // desirable as `start..`. Similarly, `start..(expr.Length - 1)` is not as desirable as + // `start..^1`. + + var startFromEnd = false; + var endFromEnd = false; + + if (semanticModel.GetOperation(invocation, cancellationToken) is IInvocationOperation invocationOp && + invocationOp.Instance != null) + { + var startOperation = semanticModel.GetOperation(startExpr, cancellationToken); + var endOperation = semanticModel.GetOperation(endExpr, cancellationToken); + var checker = new InfoCache(semanticModel.Compilation); + + if (startOperation != null && + endOperation != null && + checker.TryGetMemberInfo(invocationOp.TargetMethod.ContainingType, out var memberInfo)) + { + var lengthLikeProperty = memberInfo.LengthLikeProperty; + + // If our start-op is actually equivalent to `expr.Length - val`, then just change our + // start-op to be `val` and record that we should emit it as `^val`. + startFromEnd = IsFromEnd(lengthLikeProperty, invocationOp.Instance, ref startOperation); + startExpr = (ExpressionSyntax)startOperation.Syntax; + + // Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just + // change our end-op to be `val` and record that we should emit it as `^val`. + endFromEnd = IsFromEnd(lengthLikeProperty, invocationOp.Instance, ref endOperation); + endExpr = (ExpressionSyntax)endOperation.Syntax; + + // If the range operation goes to 'expr.Length' then we can just leave off the end part + // of the range. i.e. `start..` + if (IsInstanceLengthCheck(lengthLikeProperty, invocationOp.Instance, endOperation)) + { + endExpr = null; + } + + // If we're starting the range operation from 0, then we can just leave off the start of + // the range. i.e. `..end` + if (startOperation.ConstantValue.HasValue && + startOperation.ConstantValue.Value is 0) + { + startExpr = null; + } + } + } + + return RangeExpression( + startExpr != null && startFromEnd ? IndexExpression(startExpr) : startExpr, + endExpr != null && endFromEnd ? IndexExpression(endExpr) : endExpr); + } + private static ExpressionSyntax GetExpression(ImmutableDictionary props, ExpressionSyntax expr, string omitKey, string fromEndKey) => props.ContainsKey(omitKey) ? null : props.ContainsKey(fromEndKey) ? IndexExpression(expr) : expr; + private static RangeExpressionSyntax CreateConstantRange( + SemanticModel semanticModel, Diagnostic diagnostic, CancellationToken cancellationToken) + { + var constant1Syntax = (ExpressionSyntax)diagnostic.AdditionalLocations[1].FindNode(getInnermostNodeForTie: true, cancellationToken); + var constant2Syntax = (ExpressionSyntax)diagnostic.AdditionalLocations[2].FindNode(getInnermostNodeForTie: true, cancellationToken); + + // the form is s.Slice(constant1, s.Length - constant2). Want to generate + // s[constant1..(constant2-constant1)] + var constant1 = GetInt32Value(semanticModel.GetOperation(constant1Syntax)); + var constant2 = GetInt32Value(semanticModel.GetOperation(constant2Syntax)); + + var endExpr = (ExpressionSyntax)CSharpSyntaxGenerator.Instance.LiteralExpression(constant2 - constant1); + return RangeExpression( + constant1Syntax, + IndexExpression(endExpr)); + } + + private static int GetInt32Value(IOperation operation) + => (int)operation.ConstantValue.Value; + + /// + /// check if its the form: `expr.Length - value`. If so, update rangeOperation to then + /// point to 'value' so that we can generate '^value'. + /// + private static bool IsFromEnd( + IPropertySymbol lengthLikeProperty, IOperation instance, ref IOperation rangeOperation) + { + if (rangeOperation is IBinaryOperation binaryOperation && + binaryOperation.OperatorKind == BinaryOperatorKind.Subtract && + IsInstanceLengthCheck(lengthLikeProperty, instance, binaryOperation.LeftOperand)) + { + rangeOperation = binaryOperation.RightOperand; + return true; + } + + return false; + } + private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) diff --git a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs index e87960584c9..d452ed34c7f 100644 --- a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs +++ b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs @@ -12,7 +12,7 @@ internal partial class CSharpUseRangeOperatorDiagnosticAnalyzer /// /// Helper type to cache information about types while analyzing the compilation. /// - private class InfoCache + public class InfoCache { /// /// The System.Range type. Needed so that we only fixup code if we see the type diff --git a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.MemberInfo.cs b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.MemberInfo.cs index f7fea1d457a..91d4fcaf1d7 100644 --- a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.MemberInfo.cs +++ b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.MemberInfo.cs @@ -4,7 +4,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseIndexOperator { internal partial class CSharpUseRangeOperatorDiagnosticAnalyzer { - private struct MemberInfo + public struct MemberInfo { public readonly IPropertySymbol LengthLikeProperty; public readonly IMethodSymbol SliceLikeMethod; diff --git a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs index db0499eb7bf..ff730418fd4 100644 --- a/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseIndexOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs @@ -26,13 +26,16 @@ namespace Microsoft.CodeAnalysis.CSharp.UseIndexOperator [DiagnosticAnalyzer(LanguageNames.CSharp), Shared] internal partial class CSharpUseRangeOperatorDiagnosticAnalyzer : AbstractCodeStyleDiagnosticAnalyzer { - // Flags to indicate if we should generate 'val' or '^val' for the start or end range values - public const string StartFromEnd = nameof(StartFromEnd); - public const string EndFromEnd = nameof(EndFromEnd); + public const string ComputedRange = nameof(ComputedRange); + public const string ConstantRange = nameof(ConstantRange); - // Flags to indicate if we should just omit the start/end value of the range entirely. - public const string OmitStart = nameof(OmitStart); - public const string OmitEnd = nameof(OmitEnd); + //// Flags to indicate if we should generate 'val' or '^val' for the start or end range values + //public const string StartFromEnd = nameof(StartFromEnd); + //public const string EndFromEnd = nameof(EndFromEnd); + + //// Flags to indicate if we should just omit the start/end value of the range entirely. + //public const string OmitStart = nameof(OmitStart); + //public const string OmitEnd = nameof(OmitEnd); public CSharpUseRangeOperatorDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseRangeOperatorDiagnosticId, @@ -138,97 +141,60 @@ protected override void InitializeWorker(AnalysisContext context) return; } - // Make sure we have: (start, end - start). The start operation has to be - // the same as the right side of the subtraction. + // See if we have: (start, end - start). The start operation has to be the same as the + // right side of the subtraction. var startOperation = invocation.Arguments[0].Value; - var endOperation = subtraction.LeftOperand; - if (!CSharpSyntaxFactsService.Instance.AreEquivalent(startOperation.Syntax, subtraction.RightOperand.Syntax)) + if (CSharpSyntaxFactsService.Instance.AreEquivalent(startOperation.Syntax, subtraction.RightOperand.Syntax)) { + context.ReportDiagnostic(CreateDiagnostic( + ComputedRange, option, invocationSyntax, + memberInfo, startOperation, subtraction.LeftOperand)); return; } - // We have enough information now to generate `start..end`. However, this will often - // not be what the user wants. For example, generating `start..expr.Length` is not as - // desirable as `start..`. Similarly, `start..(expr.Length - 1)` is not as desirable as - // `start..^1`. Look for these patterns and record what we have so we can produce more - // idiomatic results in the fixer. - // - // Note: we could also compute this in the fixer. But it's nice and easy to do here - // given that we already have the options, and it's cheap to do now. - - var properties = ImmutableDictionary.CreateBuilder(); - - var lengthLikeProperty = memberInfo.LengthLikeProperty; - - // If our start-op is actually equivalent to `expr.Length - val`, then just change our - // start-op to be `val` and record that we should emit it as `^val`. - if (IsFromEnd(lengthLikeProperty, invocation.Instance, ref startOperation)) + // See if we have: (constant1, s.Length - constant2). The constants don't have to be + // the same value. This will convert over to s[constant1..(constant - constant1)] + if (IsConstantInt32(startOperation) && + IsConstantInt32(subtraction.RightOperand) && + IsInstanceLengthCheck(memberInfo.LengthLikeProperty, invocation.Instance, subtraction.LeftOperand)) { - properties.Add(StartFromEnd, StartFromEnd); - } - - // Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just - // change our end-op to be `val` and record that we should emit it as `^val`. - if (IsFromEnd(lengthLikeProperty, invocation.Instance, ref endOperation)) - { - properties.Add(EndFromEnd, EndFromEnd); - } - - // If the range operation goes to 'expr.Length' then we can just leave off the end part - // of the range. i.e. `start..` - if (IsInstanceLengthCheck(lengthLikeProperty, invocation.Instance, endOperation)) - { - properties.Add(OmitEnd, OmitEnd); + context.ReportDiagnostic(CreateDiagnostic( + ConstantRange, option, invocationSyntax, + memberInfo, startOperation, subtraction.RightOperand)); + return; } + } - // If we're starting the range operation from 0, then we can just leave off the start of - // the range. i.e. `..end` - if (startOperation.ConstantValue.HasValue && - startOperation.ConstantValue.Value is 0) - { - properties.Add(OmitStart, OmitStart); - } + private Diagnostic CreateDiagnostic( + string rangeKind, CodeStyleOption option, InvocationExpressionSyntax invocation, + MemberInfo memberInfo, IOperation op1, IOperation op2) + { + var properties = ImmutableDictionary.Empty.Add(rangeKind, rangeKind); // Keep track of the syntax nodes from the start/end ops so that we can easily // generate the range-expression in the fixer. var additionalLocations = ImmutableArray.Create( - invocationSyntax.GetLocation(), - startOperation.Syntax.GetLocation(), - endOperation.Syntax.GetLocation()); + invocation.GetLocation(), + op1.Syntax.GetLocation(), + op2.Syntax.GetLocation()); // Mark the span under the two arguments to .Slice(..., ...) as what we will be // updating. - var arguments = invocationSyntax.ArgumentList.Arguments; - var location = Location.Create(syntaxTree, + var arguments = invocation.ArgumentList.Arguments; + var location = Location.Create(invocation.SyntaxTree, TextSpan.FromBounds(arguments.First().SpanStart, arguments.Last().Span.End)); - context.ReportDiagnostic( - DiagnosticHelper.Create( - Descriptor, - location, - option.Notification.Severity, - additionalLocations, - properties.ToImmutable(), - memberInfo.SliceLikeMethod.Name)); + return DiagnosticHelper.Create( + Descriptor, + location, + option.Notification.Severity, + additionalLocations, + properties, + memberInfo.SliceLikeMethod.Name); } - /// - /// check if its the form: `expr.Length - value`. If so, update rangeOperation to then - /// point to 'value' so that we can generate '^value'. - /// - private bool IsFromEnd( - IPropertySymbol lengthLikeProperty, IOperation instance, ref IOperation rangeOperation) - { - if (rangeOperation is IBinaryOperation binaryOperation && - binaryOperation.OperatorKind == BinaryOperatorKind.Subtract && - IsInstanceLengthCheck(lengthLikeProperty, instance, binaryOperation.LeftOperand)) - { - rangeOperation = binaryOperation.RightOperand; - return true; - } - - return false; - } + private static bool IsConstantInt32(IOperation operation) + => operation.ConstantValue.HasValue && operation.ConstantValue.Value is int; } } diff --git a/src/Features/CSharp/Portable/UseIndexOperator/Helpers.cs b/src/Features/CSharp/Portable/UseIndexOperator/Helpers.cs index e2c36bf1bb8..8aad615ea9e 100644 --- a/src/Features/CSharp/Portable/UseIndexOperator/Helpers.cs +++ b/src/Features/CSharp/Portable/UseIndexOperator/Helpers.cs @@ -60,5 +60,8 @@ public static bool IsSubtraction(IArgumentOperation arg, out IBinaryOperation su subtraction = null; return false; } + + private static bool IsConstantInt32(IOperation operation) + => operation.ConstantValue.HasValue && operation.ConstantValue.Value is int; } } -- GitLab