From 878f56bf1195642dadec0e6b8520c98bb3c8e238 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 16 Dec 2019 11:25:36 -0800 Subject: [PATCH] Match implicit '.Slice' pattern --- .../UseRangeOperatorTests.cs | 92 ++++++++++++++++++- ...ngeOperatorDiagnosticAnalyzer.InfoCache.cs | 25 +++-- 2 files changed, 108 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseIndexOrRangeOperator/UseRangeOperatorTests.cs b/src/EditorFeatures/CSharpTest/UseIndexOrRangeOperator/UseRangeOperatorTests.cs index aac653b7e57..9806026c027 100644 --- a/src/EditorFeatures/CSharpTest/UseIndexOrRangeOperator/UseRangeOperatorTests.cs +++ b/src/EditorFeatures/CSharpTest/UseIndexOrRangeOperator/UseRangeOperatorTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.VisualStudio.Text; using Roslyn.Test.Utilities; using Xunit; @@ -20,9 +21,6 @@ internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProvider private static readonly CSharpParseOptions s_parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8); - private static readonly TestParameters s_testParameters = - new TestParameters(parseOptions: s_parseOptions); - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] public async Task TestNotInCSharp7() { @@ -221,6 +219,94 @@ void Goo(string s, string t) { var v = t.Substring(s[1..^1][0], t.Length - s[1..^1][0]); } +}", parseOptions: s_parseOptions); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] + public async Task TestWithTypeWithActualSliceMethod1() + { + await TestAsync( +@" +using System; +namespace System +{ + public struct Range { } + public readonly ref struct Span + { + public int Length { get { } } + public Span Slice(int start, int length) { } + } +} + +class C +{ + void Goo(Span s) + { + var v = s.Slice([||]1, s.Length - 1); + } +}", +@" +using System; +namespace System +{ + public struct Range { } + public readonly ref struct Span + { + public int Length { get { } } + public Span Slice(int start, int length) { } + } +} + +class C +{ + void Goo(Span s) + { + var v = s[1..]; + } +}", parseOptions: s_parseOptions); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] + public async Task TestWithTypeWithActualSliceMethod2() + { + await TestAsync( +@" +using System; +namespace System +{ + public struct Range { } + public readonly ref struct Span + { + public int Length { get { } } + public Span Slice(int start, int length) { } + } +} + +class C +{ + void Goo(Span s) + { + var v = s.Slice([||]1, s.Length - 2); + } +}", +@" +using System; +namespace System +{ + public struct Range { } + public readonly ref struct Span + { + public int Length { get { } } + public Span Slice(int start, int length) { } + } +} + +class C +{ + void Goo(Span s) + { + var v = s[1..^1]; + } }", parseOptions: s_parseOptions); } } diff --git a/src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs b/src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs index f32bf63f3df..0188e4c8349 100644 --- a/src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs +++ b/src/Features/CSharp/Portable/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Linq; @@ -83,21 +84,33 @@ private MemberInfo ComputeMemberInfo(IMethodSymbol sliceLikeMethod, bool require } // A Slice method can either be paired with an Range-taking indexer on the type, or - // an Range-taking overload. + // an Range-taking overload, or an explicit method called .Slice that takes two ints: + // + // https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/ranges.md#implicit-range-support if (sliceLikeMethod.ReturnType.Equals(containingType)) { - // it's a method like: MyType MyType.Slice(int start, int length). Look for an - // indexer like `MyType MyType.this[Range range]`. If we can't find one return - // 'default' so we'll consider this named-type non-viable. + // it's a method like: MyType MyType.Get(int start, int length). Look for an + // indexer like `MyType MyType.this[Range range]`. var indexer = GetIndexer(containingType, RangeType, containingType); if (indexer != null) { return new MemberInfo(lengthLikeProperty, overloadedMethodOpt: null); } + + // Also, look to see if the type has a `.Slice(int start, int length)` method. + // This is also a method the compiler knows to look for when a user writes `x[a..b]` + var actualSliceMethod = + sliceLikeMethod.ContainingType.GetMembers(nameof(Span.Slice)) + .OfType() + .FirstOrDefault(s => IsSliceLikeMethod(s)); + if (actualSliceMethod != null) + { + return new MemberInfo(lengthLikeProperty, overloadedMethodOpt: null); + } } - // it's a method like: `SomeType MyType.Slice(int start, int length)`. Look - // for an overload like: `SomeType MyType.Slice(Range)` + // it's a method like: `SomeType MyType.Get(int start, int length)`. Look + // for an overload like: `SomeType MyType.Get(Range)` var overloadedRangeMethod = GetOverload(sliceLikeMethod, RangeType); if (overloadedRangeMethod != null) { -- GitLab